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. Security Specialist. WordPress Buff.
Archives
86 responses
  1. Thanks for the very important reminder and help, Jeff. I’ve experienced database crashes before and of course, have seen cases where install.php is loaded instead of the typical “Error in establishing database connection” page being displayed. Although for each crashes I’ve managed to get the database and WP up and running again, to my horror after reading your article I realized that I’ve completely forgot the risks behind install.php being loaded. Instead, I was too engrossed in fixing the craptaculous situation (eg contacting my host, restoring database and in some cases, forming a complete, clean WP install again) that the risks behind install.php being loaded was totally beyond me.

    I do check my logs once in a while and although I’ve never had any attacks carried out on my domain, this post is a good wake-up call to me, as well as to everyone who has a self-hosted WP blog out there.

    Luckily you were around when the error occured, and you fixed the loophole immediately before any hacker could take advantage of the situation. Phew, what a close call!

    p/s: Not to mention, everyone should really upgrade to WP2.8 instead of being happily, voluntarily stuck in older versions. Security, folks!

  2. Marty Thornley May 9, 2009 @ 5:31 pm

    Wow. That is scary.

    Thank you for sharing and providing what looks like a great fix. I will definitely be adding this to my list of things to do for every new installation.

    Good to know you survived it all!

    Thanks again,
    Marty

  3. Jonathan Ellse May 10, 2009 @ 8:54 am

    Yes! I often get this, but it always says “Already Installed” which really annoys me. Thanks for the solution.

  4. Thanks, I deleted mine as soon as I read this! Thanks again for making us all aware of this issue.

  5. Nice catch.

    There is a typo in your code. You say teh when I think you mean the. In the mail() call on line 5.

  6. Jeff Starr

    @Callum: Yeah, that’s just my sad attempt at a little “leet” humor.. as a comment, it’s of no real consequence. Feel free to change it if it bothers you. Sharp eyes, though! ;)

  7. @Jeff: The more I think about it, the more I think that it’s not really an issue.

    If WP has been installed, the install will not load. If WP thinks that it has not been installed, that means for some reason it cannot find the WP tables via MySQL. If somebody came upon that page and followed the install process, they would only be able to do anything if MySQL is running and allows the creation of new tables. That doesn’t sound like such a major problem.

    I reckon the fix might be to add an optional constant to wp-config.php which allows you to define WP as installed which then disables the install function. Something like:
    define('WP_INSTALLED', true);

    Personally, I don’t think this issue is serious enough to warrant a manual hack (or fix).

  8. Jeff Starr

    @Callum: That’s about three “if’s” too many for me. Personally, the event scared the crap out of me and could have resulted in the loss of everything on my server (not just this site). To me, that is as about as serious as it gets. If you feel comfortable ignoring the vulnerability, then be my guest, but my personal policy is to take seriously any potentially harmful security risks.

  9. @Jeff: I question:

    could have resulted in the loss of everything on my server

    For the scenario you experienced to occur, MySQL must be online and responsive but not be able to find your blog’s tables. Presuming that is the case, and that MySQL allows the WordPress user defined in wp-config.php to create new tables (a very big if), an attacker could install a blog on your server. They’d then have access to an admin level account on a WordPress site on your server.

    I host a load of WP blogs and give admin accounts to all sorts of people. It doesn’t represent a significant security risk. There is some risk, but it’s fairly minimal. I keep the file permissions in wp-content fairly tight, so there’s a limit on what they can do.

    In the very, very unlikely situation that this situation occurs, an attacker gains an admin level account on your WP files on your server.

    I’m not sure I can imagine a MySQL failure scenario where this happens. I’d be interested to know more if you’re able to extract any further info from your host.

  10. Jeff Starr

    @Callum: It happened once, and it could happen again. As you say:

    There is some risk, but it’s fairly minimal

    In my experience working with security issues, any risk — even minimal ones — need to be resolved immediately.

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

    Not to sound disrespectful, but in a fraction of the time you just spent trying to convince yourself that it’s not worth doing anything about, you could have deleted the install.php file and eliminated a potential security risk.

  11. @Jeff: In deleting the install.php file, I’ve only prevented this highly unlikely issue once. In order to properly fix the problem, I need to delete install.php *every time* I upgrade. In exchange for that effort, I reduce the one in a million chance that somebody compromises my server. Personally, I don’t think that’s a worthwhile tradeoff.

    How would you take down a server with an admin level account? You could upload various PHP nasties to the plugins folder, assuming folder permissions allow (mine generally don’t). Beyond that, I’m not aware of much that an attacker could do.

    Bearing in mind that to get to this point, several one in a million failures would need to occur. Do you know what happened to MySQL on your server? I’m not sure that the “happened once, could happen again” argument is strong enough to warrant prevention of this highly unlikely issue.

  12. Hope nobody minds if I chime in a comment.

    Callum, I understand where you’re going – you’re speaking of the efficiency and simplicity of upgrades, and worry that replacing, or deleting install.php can be a huge hassle everytime WP releases an upgrade and you wish to upgrade your WP installation to the latest version. Just like what Jeff has mentioned, there are other ways to solve the vulnerability of install.php, including the .htaccess way that is simpler and doesn’t require manual work everytime you do an upgrade.

    Personally, while I love everything being automated, it doesn’t hurt when you do a little manual work, right? I am still doing WP upgrades manually because I would want to circumvent any possible problems that may arise from automatic upgrade. The latest upgrade system is almost flawless, but it still throws up errors for some once in a while. Ah, that’s not the main point.

    I also understand that Jeff is very serious and careful about any security holes and vulnerabilities. Browsing through his archives show that Jeff takes every issue seriously and handles it with great care, and more importantly, he tackles it using different solutions. That’s one thing, the attention to minute details, that makes his sites secure.

    It’s always fine that you don’t implement the methods. What Jeff is talking about is a suggested fix, it’s not a direction, neither is it mandatory. See it as a tutorial, and a sound advice from an expert in WP security – you can take it, or leave it.

    Peace out :)

[ Comments are closed for this post ]