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.
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.
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.
For wordpress, we went to their web site and downloaded the packages for the following versions:
wordpress-2.0.tar.gz wordpress-2.0.11.tar.gz wordpress-2.1.tar.gz wordpress-2.1.3.tar.gz wordpress-2.2.tar.gz wordpress-2.3.tar.gz wordpress-2.5.tar.gz wordpress-2.6.3.tar.gz wordpress-2.7.tar.gz wordpress-2.7.1.tar.gz wordpress-2.8.tar.gz wordpress-2.9.tar.gz
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 http://techcrunch.com/wp-includes/js/tinymce/tiny_mce.js > test.js $dd@ss:~/wp$ md5sum test.js 128e75ed19d49a94a771586bf83265ec j.js
The more files we profile, the more accurate our guess will be.
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
Joomla-1.0.0 Joomla-1.0.11 Joomla-1.0.14 Joomla-1.0.15 Joomla-1.0.4 Joomla-1.0.6 Joomla-1.0.8 Joomla-1.5.1 Joomla-1.5.10 Joomla-1.5.11 Joomla-1.5.14 Joomla-1.5.15 Joomla-1.5.4 Joomla-1.5.5 Joomla-1.5.8 Joomla-1.5.9
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:
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.
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.
We took a similar approach with Mediawiki. We went to their archive site http://download.wikimedia.org/mediawiki and downloaded the following versions:
mediawiki-1.3.11.tar.gz mediawiki-1.3.15.tar.gz mediawiki-1.5.2.tar.gz mediawiki-1.5.8.tar.gz mediawiki-1.8.2.tar.gz .. mediawiki-1.14.0.tar.gz mediawiki-1.14.1.tar.gz mediawiki-1.15.1.tar.gz
$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 paulgu.com), we get:
$dd@ss:~/mediawiki$ lynx --source --dump http://paulgu.com//w/skins/simple/main.css > a $dd@ss:~/mediawiki$ md5sum a b6301262680144f1709d995a6c097db8 a
We can see that he is on the latest version 1.15.1.
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.