Fingerprinting web applications

Anyone who has ever studied computer security, probably learned the theory of the least privilege: “Always give your users the minimum access (and information) necessary to do their jobs”. End users hate it, but we try to apply it whenever we can, including on how we broadcast our systems to the world.

No one needs to know what version of Linux, Apache or Wordpress we are running, so we try to hide their versions the best we can.

It means we go around disabling banners, removing generators (common on web apps) and clearing the headers from their servers.

It is useful to avoid automated attacks and scanners looking to exploit specific versions of a software. Some people call that security through obscurity, but if that’s not the only security mechanism deployed, it is security WITH obscurity. Good practice.

However, we can still fingerprint (detect) which web application and version it is running by fingerprinting a few common files included in the web application It means we look at the CSS files, javascripts and sometimes even README files that are left on the web root to pinpoint which version is being used.

  1. Fingerprinting web applications (common files)
  2. Wordpress Version Detection
  3. Joomla Version Detection
  4. Mediawiki Version Detection
  5. Live test
  6. Try yourself

1. Fingerprinting web applications (common files)

Similar to operating system fingerprinting, this technique uses unique fingerprints that are available on each version of a web application to determine which one is being used.

What these fingerprints are, depend on the web application, but generally we can use .js (javascript) , .css and a few other files that are available and we can access the source remotely. We can’t do the same with .php, because it will not return the source (only the executed output).

To create the fingerprints, we need download the packages for different versions and perform a diff between each of them. After that, we compare the diffs looking for unique patterns present on each version.

To exemplify this technique we are going to use Wordpress, Joomla and MediaWiki, since they are widely used and have an archive with all their versions. For closed-source applications, it can still work, but require getting access to a few installations with known versions to create the baselines.

2. Wordpress Version Detection

For wordpress, we went to their web site and downloaded the packages for the following versions:


With all these packages, we uncompressed them and generate a diff -q (to do not show the content of the changes) for each version:

$dd@ss:~/wp$ tar -zxvf wordpress-2.0.tar.gz; mv wordpress wordpress-2.0
$dd@ss:~/wp$ tar -zxvf wordpress-2.1.tar.gz; mv wordpress wordpress-2.1
$dd@ss:~/wp$ tar -zxvf wordpress-2.2.tar.gz; mv wordpress wordpress-2.2
$dd@ss:~/wp$ tar -zxvf wordpress-2.3.tar.gz; mv wordpress wordpress-2.3
$dd@ss:~/wp$ tar -zxvf wordpress-2.5.tar.gz; mv wordpress wordpress-2.5
$dd@ss:~/wp$ tar -zxvf wordpress-2.6.3.tar.gz; mv wordpress wordpress-2.6.3
$dd@ss:~/wp$ tar -zxvf wordpress-2.7.tar.gz; mv wordpress wordpress-2.7
$dd@ss:~/wp$ tar -zxvf wordpress-2.7.1.tar.gz; mv wordpress wordpress-2.7.1

Note that in the diff we restricted our search to only .js and .css files and also ignored the wp-admin directory since it is blocked sometimes:

$dd@ss:~/wp$ diff -r -q wordpress-2.0 wordpress-2.1 |grep \.js |grep -v wp-admin > wp-diffs
$dd@ss:~/wp$ diff -r -q wordpress-2.1 wordpress-2.2 |grep \.js |grep -v wp-admin >> wp-diffs
$dd@ss:~/wp$ diff -r -q wordpress-2.2 wordpress-2.3 |grep \.js |grep -v wp-admin >> wp-diffs
$dd@ss:~/wp$ diff -r -q wordpress-2.3 wordpress-2.5 |grep \.js |grep -v wp-admin >> wp-diffs
$dd@ss:~/wp$ diff -r -q wordpress-2.5 wordpress-2.6.3 |grep \.js |grep -v wp-admin >> wp-diffs
$dd@ss:~/wp$ diff -r -q wordpress-2.6.3 wordpress-2.7 |grep \.js |grep -v wp-admin >> wp-diffs
$dd@ss:~/wp$ diff -r -q wordpress-2.7 wordpress-2.7.1 |grep \.js |grep -v wp-admin >> wp-diffs

A sample of the file looks like:

Only in wordpress-2.2/wp-includes/js: wp-ajax.js
Only in wordpress-2.2/wp-includes/js: list-manipulation.js
Files wordpress-2.1/wp-includes/js/prototype.js and wordpress-2.2/wp-includes/js/prototype.js differ
Files wordpress-2.1/wp-includes/js/quicktags.js and wordpress-2.2/wp-includes/js/quicktags.js differ
Files wordpress-2.7/wp-includes/js/autosave.js and wordpress-2.7.1/wp-includes/js/autosave.js differ
Files wordpress-2.7/wp-includes/.../thickbox.css and wordpress-2.7.1/wp-includes/.../thickbox.css differ
Files wordpress-2.7/wp-includes/.../thickbox.js and wordpress-2.7.1/wp-includes/.../thickbox.js differ
Files wordpress-2.7/.../wp-ajax-response.js and wordpress-2.7.1/.../wp-ajax-response.js differ

With those diffs, we can easily reduce the set of files that changed more often to avoid having to perform too many requests on the server side.

$dd@ss:~/wp$ cat wp-diffs|grep "Files "| cut -d " " -f 2 | cut -d "/" -f 2,3,4,5,6,7,8,9,10 |sort| uniq -c |sort
4 wp-includes/js/autosave.js
4 wp-includes/js/quicktags.js
5 wp-includes/js/tinymce/plugins/wordpress/editor_plugin.js
5 wp-includes/js/tinymce/tiny_mce_popup.js
6 wp-includes/js/tinymce/plugins/inlinepopups/editor_plugin.js
6 wp-includes/js/tinymce/themes/advanced/editor_template.js
6 wp-includes/js/tinymce/tiny_mce.js

These are just some of the files that changed 4 or more times across all these releases. We can also look for files that were deleted from one branch to the other, to detect the first time it was installed too. With the checksums, we can easily automate a script to detect the version:

$dd@ss:~/wp$  find ./ -name tiny_mce.js | xargs md5sum | sort
a306a72ce0f250e5f67132dc6bcb2ccb  ./wordpress-2.0/wp-includes/js/tinymce/tiny_mce.js
4f04728cb4631a553c4266c14b9846aa  ./wordpress-2.1/wp-includes/js/tinymce/tiny_mce.js
25e1e78d5b0c221e98e14c6e8c62084f  ./wordpress-2.2/wp-includes/js/tinymce/tiny_mce.js
83c83d0f0a71bd57c320d93e59991c53  ./wordpress-2.3/wp-includes/js/tinymce/tiny_mce.js
7293453cf0ff5a9a4cfe8cebd5b5a71a  ./wordpress-2.5/wp-includes/js/tinymce/tiny_mce.js
61740709537bd19fb6e03b7e11eb8812  ./wordpress-2.6/wp-includes/js/tinymce/tiny_mce.js
e6bbc53a727f3af003af272fd229b0b2  ./wordpress-2.7/wp-includes/js/tinymce/tiny_mce.js
e6bbc53a727f3af003af272fd229b0b2  ./wordpress-2.7.1/wp-includes/js/tinymce/tiny_mce.js
128e75ed19d49a94a771586bf83265ec  ./wordpress-2.9.1/wp-includes/js/tinymce/tiny_mce.js

As you can see, with only one file ‘tiny_mce.js’ we are able to get very close to the real version that wordpress is running. If you do this for a few more files, you will get a much more reliable answer. To test on a remote site, just lynx –dump –source this file to see the version.

For example, on the Tech Crunch site, we can guess they are running 2.9 because the tiny_mce.js matches the checksum of that version:

$dd@ss:~/wp$ lynx --dump --source > test.js
$dd@ss:~/wp$ md5sum test.js
128e75ed19d49a94a771586bf83265ec  j.js

The more files we profile, the more accurate our guess will be.

3. Joomla Version Detection

For Joomla, we did a similar technique as we did with Wordpress. However, they didn’t stored archived versions of their software online, but we found a Mirror that had all versions since 1.0. These are the ones we downloaded


With all these packages, we uncompressed them and generate a diff -q for each version:

$dd@ss:~/joomla$ tar -zxvf *.tar.gz;

Note that in the diff we restricted our search to only .js and .css files:

$dd@ss:~/joomla$ diff -r -q Joomla-1.0.0 Joomla-1.0.4 |grep -E "\.js|\.css" |grep -v admin > diffs
$dd@ss:~/joomla$ diff -r -q Joomla-1.0.4 Joomla-1.0.6 |grep -E "\.js|\.css" |grep -v admin >> diffs
$dd@ss:~/joomla$ diff -r -q Joomla-1.0.6 Joomla-1.0.8 |grep -E "\.js|\.css" |grep -v admin >> diffs
$dd@ss:~/joomla$ diff -r -q Joomla-1.0.8 Joomla-1.0.11 |grep -E "\.js|\.css" |grep -v admin >> diffs
$dd@ss:~/joomla$ diff -r -q Joomla-1.0.11 Joomla-1.0.14 |grep -E "\.js|\.css" |grep -v admin >> diffs
$dd@ss:~/joomla$ diff -r -q Joomla-1.5.14 Joomla-1.5.15 |grep -E "\.js|\.css" |grep -v admin >> diffs

With those diffs, we can easily reduce the set of files that changed more often:

$dd@ss:~/joomla$  cat diffs|grep "Files "| cut -d " " -f 2 | cut -d "/" -f 2,3,4,5,6,7,8,9,10 |sort| uniq -c |sort
4 templates/system/css/system.css
4 includes/js/joomla.javascript.js
4 mambots/editors/tinymce/jscripts/tiny_mce/plugins/advlink/jscripts/functions.js
4 mambots/editors/tinymce/jscripts/tiny_mce/plugins/autosave/editor_plugin.js
4 mambots/editors/tinymce/jscripts/tiny_mce/plugins/autosave/editor_plugin_src.js
4 mambots/editors/tinymce/jscripts/tiny_mce/plugins/preview/editor_plugin_src.js

These are just some of the files that changed 4 or more times across all these releases. With a little shell script we an loop through them to get their checksum.

$dd@ss:~/joomla$ for i in *; do  md5sum $i/includes/js/joomla.javascript.js; done
1d28094f16c310591b855982759bc992  1.0.11/includes/js/joomla.javascript.js
9570ccaab7cdac45e6727740515ce69a  1.0.14/includes/js/joomla.javascript.js
9570ccaab7cdac45e6727740515ce69a  1.0.15/includes/js/joomla.javascript.js
1080567bb801a301e3be618805a55125  1.0.4/includes/js/joomla.javascript.js
1080567bb801a301e3be618805a55125  1.0.6/includes/js/joomla.javascript.js
222ab5eb9cb8136619053a4f8358b9a5  1.0.8/includes/js/joomla.javascript.js
b891f61dc9b85a9193592c9d13e9c97a  1.5.1/includes/js/joomla.javascript.js
326412fc179cb787500adffada69c4e7  1.5.10/includes/js/joomla.javascript.js
326412fc179cb787500adffada69c4e7  1.5.11/includes/js/joomla.javascript.js
326412fc179cb787500adffada69c4e7  1.5.14/includes/js/joomla.javascript.js
326412fc179cb787500adffada69c4e7  1.5.15/includes/js/joomla.javascript.js
326412fc179cb787500adffada69c4e7  1.5.4/includes/js/joomla.javascript.js
326412fc179cb787500adffada69c4e7  1.5.5/includes/js/joomla.javascript.js
326412fc179cb787500adffada69c4e7  1.5.8/includes/js/joomla.javascript.js
326412fc179cb787500adffada69c4e7  1.5.9/includes/js/joomla.javascript.js

Now if you choose a site and and md5 the output of joomla.javascript.js you can get close to guess which version they have:

$dd@ss:~/joomla$ lynx --source --dump > j.js
$dd@ss:~/joomla$ md5sum j.js
326412fc179cb787500adffada69c4e7  j.js

On the Joomla site we can assume that they are on version 1.5.4 and up. The more files we profile, the more accurate our guess will be.

4.Mediawiki Version Detection

We took a similar approach with Mediawiki. We went to their archive site and downloaded the following versions:


With all these packages, we uncompressed them and generate a diff for each version: (note that we limited to javascript and css files, just like before)

$dd@ss:~/mediawiki$ for in *.tar.gz; do tar -zxvf $i; done;
$dd@ss:~/mediawiki$ diff -r -q mediawiki-1.3.11 mediawiki-1.3.15 |grep -E "\.js|\.css"  > diffs
$dd@ss:~/mediawiki$ diff -r -q mediawiki-1.3.15 mediawiki-1.5.2 |grep -E "\.js|\.css"  >> diffs
$dd@ss:~/mediawiki$ diff -r -q mediawiki-1.5.2 mediawiki-1.5.8 |grep -E "\.js|\.css"  >> diffs
$dd@ss:~/mediawiki$ diff -r -q mediawiki-1.5.8 mediawiki-1.8.2 |grep -E "\.js|\.css"  >> diffs
$dd@ss:~/mediawiki$ diff -r -q mediawiki-1.14.1 mediawiki-1.15.1 |grep -E "\.js|\.css"  >> diffs

With those diffs, we can reduce the set to a few files that changed more often:

$dd@ss:~/joomla$  cat diffs|grep "Files "| cut -d " " -f 2 | cut -d "/" -f 2,3,4,5,6,7,8,9,10 |sort| uniq -c |sort
5 skins/chick/main.css
5 skins/common/cologneblue.css
5 skins/common/common_rtl.css
6 skins/common/wikibits.js
6 skins/monobook/main.css
6 skins/monobook/rtl.css
6 skins/simple/main.css

These are just some of the files. Choosing one to do our test:

$dd@ss:~/mediawiki$ for i in media*; do md5sum $i/skins/simple/main.css; done
31ef23cbcdf689bd68d957ae0d8b8a19  mediawiki-1.10.0/skins/simple/main.css
31ef23cbcdf689bd68d957ae0d8b8a19  mediawiki-1.10.2/skins/simple/main.css
31ef23cbcdf689bd68d957ae0d8b8a19  mediawiki-1.10.3/skins/simple/main.css
6781b4412fbc451b792c4cdc88b0a1fa  mediawiki-1.13.0/skins/simple/main.css
6781b4412fbc451b792c4cdc88b0a1fa  mediawiki-1.13.5/skins/simple/main.css
846eec3b6696476a79548b82bf48e492  mediawiki-1.14.0/skins/simple/main.css
846eec3b6696476a79548b82bf48e492  mediawiki-1.14.1/skins/simple/main.css
b6301262680144f1709d995a6c097db8  mediawiki-1.15.1/skins/simple/main.css
2fb3891102f9fe2d37a4bdb47b8f42de  mediawiki-1.5.2/skins/simple/main.css
2fb3891102f9fe2d37a4bdb47b8f42de  mediawiki-1.5.8/skins/simple/main.css
5d52c4473189e70e4878a5a7b38e3a82  mediawiki-1.8.2/skins/simple/main.css
24b79f325b32661fd24c93d7d2e8ccef  mediawiki-1.9.2/skins/simple/main.css
24b79f325b32661fd24c93d7d2e8ccef  mediawiki-1.9.4/skins/simple/main.css

Now if we try on the first Google result for “Powered by Mediawiki” (which is, we get:

$dd@ss:~/mediawiki$ lynx --source --dump > a
$dd@ss:~/mediawiki$ md5sum a
b6301262680144f1709d995a6c097db8  a

We can see that he is on the latest version 1.15.1.

5.Live test

After trying this technique in multiple sites, we believe we were able to pinpoint the Wordpress, Mediawiki and Joomla versions with good precision. Try with your own site and let us know how it goes. Wordpress - v2.8
Version == 2.8 for wp-includes/js/wp-ajax-response.js
Version >= 2.8 for wp-includes/js/hoverIntent.js
Version == 2.8 for wp-includes/js/wp-lists.js
Version == 2.8 for wp-includes/js/autosave.js
Version >= 2.5 for wp-includes/js/scriptaculous/wp-scriptaculous.js Wordpress - v2.6
Version > 2.5 and <= 2.7 for wp-includes/js/wp-ajax-response.js
Version < 2.7 for wp-includes/js/hoverIntent.js
Version >= 2.6.1 and <= 2.6.5 for wp-includes/js/wp-lists.js
Version >= 2.6 and <= 2.6.5 for wp-includes/js/autosave.js
Version >= 2.5 for wp-includes/js/scriptaculous/wp-scriptaculous.js
Version == 2.6 for wp-includes/js/tinymce/tiny_mce.js Joomla - v1.0.11-15
Version 1.0.x for includes/js/dtree/dtree.js
Version 1.0.9 to 1.0.11 for modules/custom.xml
Version 1.0.12 to 1.0.15 for mambots/editors/tinymce/jscripts/tiny_mce/plugins/flash/editor_plugin.js Joomla - v1.5.14
Version 1.5.8 to 1.5.14 for media/system/js/caption.js
Version 1.5.14 for language/en-GB/en-GB.ini
Version 1.5.6 to 1.5.15 for includes/js/dtree/dtree.js
Version 1.5.2 to 1.5.15 for includes/js/joomla.javascript.js

6.Try yourself

Link dead.

Quick Links


External Projects