Securing WordPress: Plugging the WordPress version leaks

July 12th, 2009 by Cory Leave a reply »

WordPress is probably the most commonly used blogging platform around.  Mostly because it’s free.  Being #1 brings a lot of headaches as it makes the software a target for hackers and spammers.

As with securing your home, the best you can do is make your WordPress install a little more secure in the hope that that hacker or spammer will move down the line to the next install on their list.  Nothing you can ever do will make you invincible.

WordPress loves advertising the version number of the install for statistical reasons.  The problem with this is that when a bad guy knows the version of your WordPress install, they will know which vulnerabilities to use to break into it.  By not letting them know this version number, it makes it just a little more difficult to break into your blog.

These instructions are for version 2.8 of WordPress but would be similar for all recent versions of WordPress.

Meta Generator Version

The first step to making it much harder to determine your WordPress version is to simply not include it in your template file.

On a lot of WordPress powered blogs, if you view source, you will see a line at the top like this:

<meta name="generator" content="WordPress 2.8">

The recommended way to keep this from being displayed is to put the following line of code in the functions.php file of your theme.

<?php
remove_action('wp_head', 'wp_generator');
?>

If your theme doesn’t have a functions.php file, you can create one in your theme directory.

Broadcasting

WordPress likes to broadcast its version to every website it reaches out to–and some plugins.

Your WordPress version is stored in wp-includes/version.php on line 11 in the variable $wp_version.

The best way from keeping this variable private is to search all your WordPress files for $wp_version and review each occurance manually.

What I do, is create a $wp_version_alt variable and set it to 2.0 below:

$wp_version = ’2.8′;
$wp_version_alt = ’2.0′;

Then, for each file that wp_version is found, I manually review the usage and determine whether or not to change it to the alternative fake version.

Here is the list of files I changed:

./xmlrpc.php

Function: initialise_blog_option_info()

Some people might need to leave this intact for third-party integration.  I do not, and do not want anything to leak out of xmlrpc.

From (Line 376)

    function initialise_blog_option_info( ) {
        global $wp_version;

To

    function initialise_blog_option_info( ) {
        global $wp_version_alt;

wp-includes/comment.php

Function: pingback()

The pingback function reaches out to websites to let them know that you have linked to their site.  It also informs them what version of WordPress you are using by sending it as the User Agent.

From (Line 1402)

function pingback($content, $post_ID) {
	global $wp_version;

And from (Line 1457)

$client->useragent .= ' -- WordPress/' . $wp_version;

To

function pingback($content, $post_ID) {
	global $wp_version_alt;

And

$client->useragent .= ' -- WordPress/' . $wp_version_alt;

Function: weblog_ping

Another form of the ping to other blogs.  Again, using the version as part of the User Agent which could be captured by other compromised blogs.

From (Line 1532)

function weblog_ping($server = '', $path = '') {
	global $wp_version;

And from (Line 1539)

$client->useragent .= ' -- WordPress/' . $wp_version;

To

function weblog_ping($server = '', $path = '') {
	global $wp_version_alt;

And

$client->useragent .= ' -- WordPress/' . $wp_version_alt;

wp-includes/http.php

Whenever the request function is called, it uses the WordPress version as part of the User Agent.  Same as ping above.

From (Line 218)

    function request( $url, $args = array() ) {
        global $wp_version;

And from (Line 226)

'user-agent' => apply_filters( 'http_headers_useragent', 'WordPress/' . $wp_version . '; ' . get_bloginfo( 'url' )  ),

To

    function request( $url, $args = array() ) {
        global $wp_version_alt;

And

'user-agent' => apply_filters( 'http_headers_useragent', 'WordPress/' . $wp_version_alt . '; ' . get_bloginfo( 'url' )  ),

The get_bloginfo(‘version’) function

The get_bloginfo function returns the $wp_version when asked: get_bloginfo(‘version’)

The simple way to plug this leak would be to edit that function and return the $wp_version_alt variable–but of course, it’s not that simple.

get_bloginfo is called by plugins and WP alike.  Sometimes for good, sometimes for evil.

In my install, the only place where get_bloginfo is a problem, is in the feed.  The feeds generator is listed as WordPress and includes the version number.

The main place to fix this is in the wp-includes/general-template.php file.  Search for get_bloginfo(‘version’) in that file and replace all those with whatever you want.  Maybe not even include WordPress.

Another way to handle this problem is by finding the get_the_generator() function and making sure it never returns any relevant data.  This will prevent a version from slipping out.

Untrusted/Compromised User Accounts

If you allow users to register for your blog, you will have to perform one final step.  When a user logs in as a subscriber/contributor/whatever, they will have access to a rudimentary version of the admin section.  In the footer of every admin page is your WordPress version and easily read by all.

To stop this, you can open up wp-admin/admin-footer.php and comment out this bit of PHP code.

From (Line 22)

<p id="footer-upgrade" class="alignright"><?php echo $upgrade; ?></p>

To

<p id="footer-upgrade" class="alignright"><?php //echo $upgrade; ?></p>

The only change is adding to forward slashes (“//”) just before the echo $upgrade.

Last, you will need to comment out the “update nag” in wp-admin/includes/update.php.  The update nag will inform those low level subscribers that your WordPress install is out of date!  Usually, when a new version of WordPress is released, it is to plug some security hole.  Why do we want to inform our subscribers that our install might be easily compromised?

From (Line 128)

if ( current_user_can('manage_options') )
	$msg = sprintf( __('WordPress %1$s is available! <a href="%2$s">Please update now</a>.'), $cur->current, 'update-core.php' );
else
	$msg = sprintf( __('WordPress %1$s is available! Please notify the site administrator.'), $cur->current );

To

if ( current_user_can('manage_options') )
	$msg = sprintf( __('WordPress %1$s is available! <a href="%2$s">Please update now</a>.'), $cur->current, 'update-core.php' );
//else
//	$msg = sprintf( __('WordPress %1$s is available! Please notify the site administrator.'), $cur->current );

This modification will hide the “Your wordpress is out of date” nag to everyone except those that can make changes to the WordPress install (the admin).

The WP Hide Dashboard Plugin

Allowing subscribers to register on your blog also gives them access to the Admin Dashboard.  This area also broadcasts your WP version along with several other bits of information you may wish to keep private.

The WordPress Hide Dashboard Plugin takes care hiding this menu option and preventing access to it.

Conclusion

WordPress is a target for hackers and spammers and will remain to be so as long as it is popular.  There is no such thing as a completely secure install of WordPress, but you can definitely take steps, such as preventing your version from being leaked, to make you a little less of a target.

3 comments

  1. Doug says:

    What a ridiculous waste of time. Just upgrade to the latest version instead of hacking all of this code, you consummate fool.

    Reply

    Cory reply on August 22nd, 2010 16.29.33:

    I’ll let your comment speak for itself.

    Reply

Leave a Reply