Latest TweetsDifference between mod_alias and mod_rewrite perishablepress.com/difference…
Perishable Press

Important Security Fix for WordPress

The other day, my server crashed and Perishable Press was unable to connect to the MySQL database. Normally, when WordPress encounters a database error, it delivers a specific error message similar to the following:

[ Screenshot: WP Default Database Error Page ]
Default database-error message

This customizable database error message explains the situation to visitors and circumvents any malicious activity involving exposed scripts, PHP errors, and other issues related to unexpected database issues.

That sounds nice, but there is a problem

The problem that I painfully discovered when my server crashed is that WordPress does not always display the default page for all database-related issues. Apparently, if the database is missing entirely, WordPress assumes that it has not yet been installed and loads the Installation Page:

[ Screenshot: WP Installation Page ]
WordPress Installation Page

Yikes! This is exactly what happened when my server crashed, MySQL was unavailable, and the WordPress Installation Page was displayed to over 100 visitors while I scrambled to resolve the issue.

During the event, there were several attempts to assume control of my site through the Installation Page. Fortunately, I was working on the site (via FTP, cPanel, phpMyAdmin, and so on) during the attacks, and was able to terminate an inevitable hostile takeover.

“john@greatCampingTrips.com”, I’m staring at you here.

It happened to me, and it could happen to you

To me, this scenario represents an enormous security risk for all currently available versions of WordPress (up to 2.8 at the time of this writing). If WordPress serves up the Installation Page the next time your database goes down, anyone could easily gain full control of your entire server. By simply entering an email address and specifying a blog title, an attacker would administratively re-install WordPress and receive the following message:

[ Screenshot: WP Installation Success ]
WordPress installation-success message

Ouch! This is no good. I was fortunate enough to have been there during the incident, however it could have happened while I was away from the computer. What if I had been asleep, at work, or on vacation? Trust me, it doesn’t take long for a savvy attacker to annihilate an entire server, and the damage may be irreversible.

A temporary solution, until WordPress does it better

After restoring full functionality to my site, deleting multiple “Hello world!” posts and “About” pages, and removing the newly added Administrator, it was time to prevent this situation from happening again. The easiest way to do this involves deleting, blocking, or modifying the wp-admin/install.php file, which contains the script that generates the Installation Page.

Until WordPress incorporates a better solution, I recommend one of the following three fixes:

Fix #1: Just nuke it

Simply delete the wp-admin/install.php file entirely. It is not needed after installation.

Fix #2: HTAccess to the rescue

Place the following slice of HTAccess into your site’s web-accessible root directory to prevent access to your install.php file:

# PROTECT install.php
<Files install.php>
	Order Allow,Deny
	Deny from all
	Satisfy all
</Files>

Fix #3: Replace it with something safe and useful

Replace the insecure version of the file with something secure and informative by following these quick steps:

  1. Rename the original install.php to something like, “install_DISABLED.php” or whatever.
  2. Create a new file named “install.php” and add the following code:
<?php // install.php replacement page: https://perishablepress.com/press/2009/05/05/important-security-fix-for-wordpress/ ?>
<?php header("HTTP/1.1 503 Service Temporarily Unavailable"); ?>
<?php header("Status 503 Service Temporarily Unavailable"); ?>
<?php header("Retry-After 3600"); // 60 minutes ?>
<?php mail("your@email.com", "Database Error", "There is a problem with teh database!"); ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xml:lang="en" xmlns="http://www.w3.org/1999/xhtml" lang="en">
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
		<title>Error Establishing Database Connection</title>
	</head>
	<body>
		<img src="images/wordpress-logo.png" />
		<h1>Error Establishing Database Connection</h1>
		<p>We are currently experiencing database issues. Please check back shortly. Thank you.</p>
	</body>
</html>

Once uploaded to your server, this new install.php file will prevent any malicious behavior by serving up a static web page. Now, instead of showing the Installation Page when your database is unavailable, WordPress will display the following message:

[ Screenshot: WP Database Error ]
WordPress installation replacement page

In addition to displaying this information to your visitors, the Installation Replacement Page also performs the following actions:

  • Communicates a 503 (Service Temporarily Unavailable) status code to clients and search engines
  • Instructs clients and search engines to return after 60 minutes (configurable)
  • Sends an email informing you of the situation so that you may take action

To use the Replacement Page, don’t forget to specify an email address in the fourth line. You may also change other variables, such as the time duration, email subject, or email message. If you need any help with these variables, please leave a comment. I have also made the Installation Replacement Page available for easy download:

WP - install.php replacement file – Version 1.0 (655 B zip)

Live long and prosper

Until the developers at WordPress implement a cleaner, more permanent solution for this issue, I highly recommend applying one of the solutions provided in this article. There is no reason to keep the original install.php file after you have installed WordPress, so feel free to delete, modify, or block it. Trust me, dealing with the trauma of watching your site get hijacked by some unscrupulous cracker whore just ain’t worth the pain.

Update

MustLive provides more insight into the install.php vulnerability via this comment and this article: Attack on Abuse of Functionality in WordPress. Many thanks to MustLive for his work on this security issue!

Jeff Starr
About the Author Jeff Starr = Web Developer. Book Author. Secretly Important.
Archives
86 responses
  1. Jeff Starr

    Yeah, the cookie idea sounded perfect at first, but then you think about the kind of traffic that most sites (including this one) get: one or two hits per visitor. As the server would be checking each user request for the cookie, I would still receive around 90% of the emails, which is still way too many.

    I like Callum’s idea, and may try to set something like that up on the server. If successful, I will release it to the public.

    Thanks everyone for the ideas :)

  2. Vladimir June 8, 2009 @ 8:45 pm

    Give me an admin level account and I could take down everything on your server.

    Wanna try? :-) You will have all exec() family disabled, the blog resides on the partition mounted with nodev,nosuid,noexec and disk quota enabled, will be unable to create any tables, access someone else’s files/databases etc. The only thing you can do is take down the blog for which you have the administrative privileges. But this is what backups are for :-)

  3. Jeff Starr

    @Vladimir: You sound pretty confident about that – I hope you’re right, because there are plenty of brilliant hackers out there that could probably find a way to do some damage. Unfortunately not all server configurations are quite as secure as yours. The vast majority of folks need all the extra security help they can get. But your point is well-taken — thanks for the comment. :)

  4. @Jeff – the easiest way to add some security is to run every site as a different user.

    Say, example1.com resides in /home/example1 and owned by example1 user, example2.com resides in /home/example2 and owned by example2 user. Then /home/ directory is given 0711 permissions, /home/* – 0700 permissions. Make sure that every application that connects to MySQL database uses its own username/password/database and has minimum permissions (say, if an application does not drop tables, it does not need DROP privilege).

    Then, if, say, example1.com is hacked, the attacker won’t be able to do anything with other sites – because he does not have necessary permissions.

    It is also a good idea to mount /home (or wgere your sites are) partition as nosuid,nodev,noexec to prevent execution of uploaded shell scripts AND use Suhosin to blacklist exec() and company. Then, if a backdoor is uploaded, an attacker will be unable to launch any external script/program.

  5. PhilB WordPress June 15, 2009 @ 5:08 pm

    A additional security issue may be all of the “readme” and “info” and other files left behind – not just for WordPress and all the plugins and templates, but other apps as well. I recently found in my server logs two strange bots from Chinese IPs that were active looking for readme txt files. If a particular plugin or app version has a vulnerability, it certainly may be more effective to scan the txt files that could indicate versions.

    Just to be on the safe side I remove all of the txt files, mods descriptions, etc from any web-accessible directory for my wordpress installs.

  6. Jeff Starr

    @Vladimir: Thanks for the inside security tips. You are a wealth of information and I am grateful for you taking the time share your ideas and strategies with everyone here at Perishable Press. To be honest, some of it is over my head, but it serves as an excellent reference nonetheless. Cheers.

    @PhilB: Good call on deleting the loose files. I usually scrap anything non-essential as part of the installation process. It would also be possible to simply block access to these files via HTAccess, but I never use them anyway, so what’s the point. In any case, good call and sound advice. Thanks.

  7. Jack Palancio July 1, 2009 @ 7:46 am

    Thanks so much for the advice — I deleted the wp-admin/install.php — but how do I get the site back? All the files seem to be there. You mentioned something about MySQL not being available. How do I check/fix for that?

    This just happened to a self-hosted WordPress site I’ve been working on. My host did some work on their servers over the weekend and now the site won”t load. My other non-wordpress sites are fine… what makes wordpress so sensitive that a host performing maintenance on their servers causes it to blow up? Now I’m questioning if I should even use it.

  8. Strictly Online Biz July 3, 2009 @ 1:22 pm

    Thanks for this post. Just implemented it on my own blog. At least now I feel a lil bit safer.

  9. Jeff Starr

    @Jack: I feel your pain! Hopefully you keep backups of your databases. If not now, definitely pick up the practice and do so in the future (even if you choose a non-WP blogging platform). There are a couple of good plugins available for it — just search for “wordpress database backup plugin”.

    As for locating and getting your database back, talk to your host — they will be best equipped to help you deal with this specific situation.

    Good luck!

  10. Greg Sampson September 5, 2009 @ 10:52 am

    In my wp-admin, I also have install-helper.php. Is this also subkect to being erased or should I do something else with this file?

  11. Jeff Starr

    @Greg Sampson: Nah, that file is all PHP – nothing output to the browser, so it should be just fine if left alone.

  12. Thanks for the info. I thought I was hacked last night on one of my sites after seeing the install.php page. I was banging my head against the wall for many hours until I read this post. I believe my wp-options table was bad and hence the install page popped up. I chose option #3 to fix. Thanks Jeff!

[ Comments are closed for this post ]