Important Security Fix for WordPress

by Jeff Starr on Tuesday, May 5, 2009 101 Responses

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: http://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:

Download the Installation Replacement Page [ 655 Bytes • .zip format • 1506 downloads ]

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

May 19th, 2009: 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!


101 Responses

Add a comment

[ Gravatar Icon ]

Mark Cahill#1

Htaccess protect all of wp-admin and you’re all set. This is exactly the reason that software like vBulletin requires that you delete the install file before you use it.

This is a long known and ignored issue…

[ Gravatar Icon ]

Jeff Starr#2

Well hopefully this will help alert a few more users to the severity of the issue.

Be careful protecting the entire WordPress /wp-admin/ directory, however, there are files that are designed to be accessed via HTTP in order to function as intended. There are also scripts and plugins that require access to various files in the /wp-admin/ directory. Without proper permissions/access, many such scripts simply stop working.

So yes, HTAccess is a great way to protect your files if used mindfully, according to the needs of your particular configuration.

[ Gravatar Icon ]

Kim Woodbridge#3

Yikes! That’s a really bad hole.

So you don’t recommend password protection of the entire Admin directory? Do you know of a commonly used plugin that accesses it?

Lazy me thinks it’s easiest to just delete the install file…

[ Gravatar Icon ]

Jeff Starr#4

Yes it is, and I could have lost my entire site because of it.

If you know for certain that no plugins are accessing the files in your admin directory, then you may want to simply block the entire thing, but I really don’t recommend it. I have seen way too many “silent” errors involving all sorts of files and scripts while working with blocking rules to feel comfortable with it.

If you decide to block the admin folder, make sure that you aren’t inadvertently preventing server access to the admin files.

[ Gravatar Icon ]

Matthew Muro#5

I just decided to nuke the file altogether.

There are a lot of articles out there on how to protect your site and I don’t think I’ve ever seen this one, so thanks for pointing this out.

[ Gravatar Icon ]

Jeff Starr#6

My pleasure, Matthew. I likewise wasn’t aware that something like this could happen. Hopefully it will help others as well.

[ Gravatar Icon ]

Alexander#7

Something like this happened to me while creating a new site, however the place was new and luckily had no visitors.
Until now I hadn’t found a reasonable solution but I really like your custom install.php

Thanks for the various solutions. ;)

[ Gravatar Icon ]

Aldo#8

Thank you, thank you, thank you, Jeff, for sharing this experience. A killer tip.

[ Gravatar Icon ]

Mark Cahill#9

Uh, whole wp-admin htaccess - I have 15 sites running in that config with no problem. Turn off XML-RPC, and lock down wp-admin. It’s the only way to be sure…

[ Gravatar Icon ]

Jeff Starr#10

@Mark Cahill: If you allow people to register at your site and block the entire admin directory, they will not be able to login to the admin area. If it’s just you and nobody else on those fifteen sites, then cool, block the whole thing, but I respectfully disagree that your strategy is anywhere near a “one size fits all” solution.

[ Gravatar Icon ]

Mark Jaquith#11

If WordPress serves up the Installation Page the next time your database goes down, anyone could easily gain full control of your entire server.

It only serves that up if your database is UP but there is no WordPress install on the database and table specified in wp-config.php

This could happen if you deleted your blog tables, deleted your options table, or if MySQL thought that you did. If your MySQL server is unavailable, you will NOT get this screen.

[ Gravatar Icon ]

Steve Taylor#12

As you had to delete the new admin account, I guess I’m missing something… but the first thing that came to mind was, if MySQL’s unavailable, how does WP get re-installed? Did your crash involve MySQL going down, then coming back minus your WP database?

[ Gravatar Icon ]

Jeff Starr#13

@Mark Jaquith: Apparently MySQL thought that I did, because the install page was definitely served.

@Steve Taylor: That may have been what happened, but I can’t be sure — the whole event happened very quickly and I am not privy to much of what goes on behind the scenes on my shared server.

[ Gravatar Icon ]

Mark Jaquith#14

if MySQL’s unavailable, how does WP get re-installed? Did your crash involve MySQL going down, then coming back minus your WP database?

That must be what happened. It’s strange, to say the least. I’ve never seen that happen before.

[ Gravatar Icon ]

Jeff Starr#15

@Mark Jaquith: Me neither, but it certainly scared the crud out of me — you should have seen me racing through the neighborhood running stop signs so I could get home and fix the issue before someone decided to take my site down. Scary stuff.

[ Gravatar Icon ]

Easy Computer Fix#16

Doesent wordpress already recommend removing install.php upon installation?

[ Gravatar Icon ]

Matthew Muro#17

“Doesent wordpress already recommend removing install.php upon installation?”

After reading this post, I searched through the Codex and wasn’t able to find anything.

[ Gravatar Icon ]

Lemm#18

Thank’s Jeff…So glad you posted this.
I made sure to delete install files on other scripts I use too…there’s really no need for them to be there after you install stuff…it’s like leaving your keys in the door!

[ Gravatar Icon ]

Scott#19

Thanks for sharing this. I’m going w/ option 3 as you’ve certainly made it easy enough to do!

[ Gravatar Icon ]

rc#20

Is it a fair assumption that the bad guys are trolling wp sites looking for a down db? In your investigation, have you been able to identify the signature of any such trolling?

[ Gravatar Icon ]

ignacio#21

I have my wp-admin protected. But thanks for the tip, I renamed the file anyways…

[ Gravatar Icon ]

Matthew Muro#22

This would seem to be a rare issue, but it’s better to be safe than sorry.

[ Gravatar Icon ]

Terje Asphaug#23

Thanks :) Usually I delete the install.php after WP is installed, but had forgot it this time. It’s now deleted ;)

[ Gravatar Icon ]

Blog design Lab#24

Thanks for this tips !

[ Gravatar Icon ]

teddY#25

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!

[ Gravatar Icon ]

Marty Thornley#26

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

[ Gravatar Icon ]

Jonathan Ellse#27

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

[ Gravatar Icon ]

Mezanul#28

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

[ Gravatar Icon ]

Callum#29

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.

[ Gravatar Icon ]

Jeff Starr#30

@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! ;)

[ Gravatar Icon ]

Callum#31

@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).

[ Gravatar Icon ]

Jeff Starr#32

@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.

[ Gravatar Icon ]

Callum#33

@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.

[ Gravatar Icon ]

Jeff Starr#34

@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.

[ Gravatar Icon ]

Callum#35

@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.

[ Gravatar Icon ]

Jeff Starr#36

If deleting a file is too much work for you, you could always add a few lines of code to your HTAccess file. That shouldn’t be too difficult, and there really is no maintenance involved. A simple three-second operation performed one time would protect your site and prevent an attacker from exploiting a known security vulnerability.

Honestly, think about what you’re advocating here: that users should NOT protect their sites against a potential attack. Come on, give it a rest. Five seconds of time to eliminate a possible catastrophe is a no-brainer. You have no idea what the actual odds are, but even a one in a trillion-billion-billion chance is not worth the risk. I’m sorry, but ignoring security vulnerabilities based on odds is just bad policy.

[ Gravatar Icon ]

Terry#37

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 :)

[ Gravatar Icon ]

Matthew#38

Callum, I know you’re trying to play Devil’s Advocate here, but there’s really no advantage to NOT take action.

Plus, who’s to say that a hacker cannot reproduce the exact circumstances here in order to exploit a site?

[ Gravatar Icon ]

Callum#39

@Jeff, Terry, & Matthew: I’m looking at this vulnerability from the perspective of a fix within WP core. I think if it’s a problem that deserves a fix, that fix should be in WP core. From that perspective, I’m not sure it’s a “vulnerability” that needs to be fixed.

I do think adding the option to disable the installer in wp-config.php would be worthwhile given the limited cost of implementation.

@Matthew: I don’t think an attacker could cause MySQL to “lose” the WP tables in order to be able to install a new blog on a server. If they could affect MySQL in that way, I think there would be bigger issues than re-installing WordPress.

I’m following up here because I feel like the “vulnerability” is being exaggerated. I don’t think it represents a significant risk. Specifically, I think this paragraph in the original posts overstates the risk:

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:

In my opinion, this paragraph misrepresents the facts. The paragraph suggests that a mere database failure will open your blog up to the world. That’s simply not true. It is a very specific and very unlikely MySQL failure that causes this issue.

When I first read this article I felt like I needed to take immediate action to secure my blogs. After investigation, I haven’t taken any action because in my opinion there is virtually no risk of this issue arising.

This scenario arises where the option siteurl is not found in the wp_options table. If that value is accidentally deleted, then the site could be compromised. I would support improving the algorithm in is_blog_installed() to better check if WordPress has already been installed. However, I don’t think this represents a security risk worthy of immediate action. I think it falls under the “nice to have, enhancement” category.

[ Gravatar Icon ]

Jeff Starr#40

@Callum:

I’m looking at this vulnerability from the perspective of a fix within WP core.

That explains our differing views on this issue. I am looking at this vulnerability from the user’s perspective. The average user could not care less what’s in the core or how much development costs are, blah blah blah. What is important to them is that their hard work is safe and secure online. When a security issue is discovered, they have a right to know how to fix it. Almost losing my site was enough to scare the pajamas off me, so I figured I would do my readers a favor by sharing the issue and providing a few temporary solutions. From what I can tell, 99% of my readers are grateful for the heads up.

You, on the other hand, insist on downplaying the whole event as if it’s just no big deal — a “nice to have, enhancement” sort of thing. If that is your approach to this particular security issue, then knock yourself out. As someone who is serious about security, I do my best to eliminate every risk, regardless of its perceived improbability.

I appreciate your remarkable efforts in trying to justify your unwillingness to apply a simple fix to a known security vulnerability, but I simply cannot advocate that users just sit idly by and do nothing to protect themselves against a potential attack, especially when the fix requires all of five seconds to implement.

Despite what you may think, security is not a “nice to have, enhancement,” but rather it is absolutely imperative to running a consistent, reliable, and safe website. At least, that’s how I am looking at it.

[ Gravatar Icon ]

Terry#41

I don’t want to start a virtual catfight *meow*, but I wish to get things straight.

I understand your view, Callum. According to your latest reply, you mentioned that the fix should be carried out by the developers of WP and the users of self-hosted WP should not be responsible of the vulnerability. While there is definitely some truth within your statement, there is nothing wrong if one user (like Jeff) wishes to improve upon the current WP installation. And again, we’re back to the same argument - just like you’re the master of your own fate, you’re also the one who’s responsible for the security of your WP blog. If you think that it’s susceptible to attacks, then why not try to fix the problem instead of sitting and wait for the WP team to fix it? Plus, the fix is optional. It’s not a directive (sorry for the typo error previously) and is not mandatory.

A risk is definitely involved for not fixing it, but not fixing it doesn’t mean it’s the end of the world. In Jeff’s point of view, he wants to improve the security of his WP blog and me too, see no problem with that (and your argument too).

A analogy will be discovering a small problem with your window lock that a thief can open from the outside. Of course, the risk of it being picked open by a thief is low (not as if they know which window has a problematic lock) and so you can sit aside and do nothing about it. For Jeff, he spots a problem with the lock and replaces it with a new one. Nobody knows about it either, but it makes his house a tad bit safer :)

I hope this gets the point across. While you have the right to iterate (and reiterate) your point, don’t forget that this is Jeff’s blog, and he has all the right to write anything as long as it does not infringe the right of others (that means, he’s entitled to it because that’s how rights work). The rule is simple: take it, or leave it.

Jeff has also cited anecdotal evidence that he was scared by attacks on his site, and if you do read his archives, he was the victim of multiple hacking attempts. Just like the homeowner whose house was broken into a few times previously, of course he will make a conscious, concerted effort to make his house (or WP installation) safer by switching the window lock (deleting, modifying install.php).

Right? ;) Let’s not pick the old scab and move on. There’s no point arguing back and forth for a simple solution like this.

[ Gravatar Icon ]

mccormicky#42

Is the question really about protecting/deleting/renaming install.php or is it about what is causing install.php to show up to the whole world looking at your website in the first place??

This error, which is repeated many many times in the error log, User XXX has exceeded the ‘max_questions’ resource(etc),… causes the flip to install.
The database constantly burns out and wp goes to install. I check the error logs and they are full of nasties (mainly to do with wp-ecommerce currency list table which is a beast).The next baddie is about utf 8.I read on another website that commenting out define(’DB_CHARSET’, ‘utf 8′); was the way to go. But now the posts all have little black diamonds with question marks in them.Pretty. So define(’DB_CHARSET’, ‘utf 8′); actually does something important. I uncomment it.

I found that by creating a new db user and adding it to the config.php reinstates the normal site. Phew. Only not really because who can sit by their monitor all day making sure the site doesn’t revert to install mode?

I renamed install.php and used your file -got rid of the potentiality of some horrible person wiping out the site. Coincidentally or not suddenly all the posts/pages/users/products/categories were gone from the dashboard. Haxorz! I yelled and scrambled for the backups. I was too late? Everything was gone? Did all my base belong to them? Was I going to have to redo everything from a backup? At 3 am with not enough sleep and very little to eat? I was freaked. Nope all of a sudden all the posts,etc came back.Then they went away again. Then they came back. I had 2 small heart attacks.

The error log is full again. I have 300 emails from your replacement install file telling me the database is down.

What to do?

[ Gravatar Icon ]

mccormicky#43

This might have already been mentioned but I will type it again:

“By simply entering an email address and specifying a blog title, an attacker would administratively re-install WordPress and receive the following message:”

Maybe. But isn’t what actually happens is the potential take over-er gets a message that wp is already installed?

Or are you saying there are other more terrifying ways this file could give someone access to your database. If WP is already installed are the Bad ones given an admin account?

When this 1st happened to me I did go on with trying to re install because I did not know any better. I didn’t get a password sent to me and no new account was made. It does seem as though the real point is nobody but the 1st person to install WP should EVER see this install file. So if your server is crap delete install.php. Perhaps people should make an “out of order page” and name that install.php.
Because in Firefox I do not see anything on your replacement file but a message that “the server is redirecting in a way that will never complete.Try again”

[ Gravatar Icon ]

Jeff Starr#44

@mccormicky: Yikes! That sounds like some serious drama (I also experienced a small heart attack when this happened to my site).. not too sure if I know where to begin on this one. Are you still experiencing the same errors? Have you resolved the database issue(s)? I just checked your site and everything seems to be loading correctly. I couldn’t even find your install.php file. Hmmm..

[ Gravatar Icon ]

Jeff Starr#45

@Terry: Well said. Thanks for keeping things in perspective! :)

[ Gravatar Icon ]

Callum#46

@Terry: I agree, it’s Jeff’s blog and he may write as he likes.

My aim is to play down the risk. I think it was dramatically overstated in Jeff’s post. I think if an average user stumbles across this post they might misleadingly think that their install.php file represents a serious security risk. In my opinion, it does not. I think there are many other things that deserve their security related attention first (security checking plugins for example).

I say, don’t panic, WordPress is just as secure as last week, this is not an exploit that attackers can purposefully target.

Jeff, I do think it’s worth bringing this up. I’d encourage you to submit a ticket to trac with a recommended fix. I think it would be great if WordPress became even a little more secure as a result of your work.

I’ll leave it at that. :-)

[ Gravatar Icon ]

mccormicky#47

Hi Jeff,
No, this is happening to a client site-I should have said that.

I could be wrong, but it might be that on a healthy set up you can’t load install.php directly. My wp-admin is not in the root so maybe that’s why.Though of course you’d have seen that in my source.

This happened the 1st time a week ago and has happened very regularly ever since. It’s a pretty well visited website and the chances that someone with bad intentions has seen install.php are very high.

I found your post and followed your directions for the security risk. Right after that the next time the error log filled up- instead of serving up install.php, wordpress was acting like the database was cleaned out.
At one point it looked like the situation had resolved itself.For an entire hour the error logs were empty even though I was using plugins, updating posts and the product list,etc.

I experimented with using htaccess to redirect to a different file with a custom error message. But you couldn’t load that page either if mysql was functioning. The only good news out of this mess is that now I know that when it looks like the site is dying all I have to do is switch the db user and the site comes back up.

But it is obviously not a fix because I cannot monitor this website 24 hours a day.

[ Gravatar Icon ]

Peter#48

Appreciate the article and thanks for the heads up. Peace.

[ Gravatar Icon ]

Callum#49

@mccormicky, sounds like you’re having strange MySQL issues. I just checked and your install.php file says “Already Installed”.

The test performed by install.php is very specific. It tries to load the siteurl option from the wp_options table. If that is not found, it presumes that WordPress has not been installed. If it cannot talk to MySQL at all, then you get a different error.

So for install.php to be a security risk, MySQL needs to be up and responding, but siteurl is not found in wp_options.

If you’re changing your MySQL username and password, that sounds like you’re having permissions issues. I’d suggest regular backups until you resolve the issues. You might try contacting your hosting company to see if they’re having MySQL problems.

Best of luck with your issues.

[ Gravatar Icon ]

Jeff Starr#50

@mccormicky: On a default installation of WordPress, the install.php file may be accessed directly. If the options table is available, the message will read something like:

Already Installed: You appear to have already installed WordPress. To reinstall please clear your old database tables first.

If the database is unavailable, as Callum points out in the previous comment, the wp-includes/wp-db.php file generates an error message such as:

Error establishing a database connection

This message may be overridden with a custom database message by creating a file named “db-error.php” and placing it in the wp-content directory. This file is just like any other PHP file and may be configured according to your needs (error logging scripts, email alerts, whatever).

In any case, if your database-related issues are complex, you may want to simply start over with a fresh install and troubleshoot from the ground up. If the problems persist on an out-of-the-box installation of WordPress, your web host definitely should be informed of the issue.

If starting over from scratch doesn’t work for you, you may benefit from setting up a new test site where a default installation may be tested. A new setup will eliminate many of the variables that may be confounding the issue, and enable you to retain full control of the testing process from the ground up.

I hope this helps, mccormicky — let me know how it goes.

[ Gravatar Icon ]

Terry#51

@Callum: Thanks for the reply. I’m glad that you’ve finally resolved the difference in opinion between you and Jeff. Of course, I do respect the opinions from both sides - a little constructive input from either side makes a good discussion perfect.

@mccormickiy: I’m wondering if there’s a problem wiht your database. There were several instances where my server was still online but the database was uncontactable, resulting in the installation page in full view of others as well. Sometimes it gives me the “Error in database connection” message, or sometimes I get totally blank pages on my WP blog. If you’re looking to setup a WP blog for testing purposes like what Jeff has suggested, instead of burdening your host with another WP installation (and if you’re lazy to wait for FTP uploads like me, stuck in a country where the max bandwidth is 25% of the promised ones from the ISP </rant>), you can have a local installation of WP on your computer. It’s quite easy, and there are several good tutorials out there (I’d recommend this). I tinker around with my databases locally to, so that I don’t royally screw up the online version heh :)

[ Gravatar Icon ]

mccormicky#52

Hi all and thanks for all the good ideas & trouble shooting.
Thanks also for trying to find the problem on my site but it wasn’t my site I was describing. If it had been my site I would have said “oh well there goes my 6 readers”.

In all seriousness, I’ve dealt with many hosts and set up many sites functioning well on various shared hosting plans, all of which were upgraded to 2.7.1 without a problem.

As for the website I wrote about in my earlier comments,there have been a few troubles since upgrading to 2.7.1 but nothing like this last doozy.

We can’t afford all the downtime. It’s definitely time to step it up to the next level and spring for more hosting power. There have been several red flags during the year and from the beginning I said the hosting wasn’t up to the demands made on it by the client’s customers. Last night’s drama sealed the deal.

[ Gravatar Icon ]

MustLive#53

Jeff

Very interesting vulnerability in WordPress. Which shows that even installers with protection from overinstall is dangerous, because this protection can be bypassed in some cases, so it’s always better to delete installations files after the install.

As I wrote in my article Attack on Abuse of Functionality in WordPress, I have created some variants of the attack on this vulnerability in WP.

It’s possible to attack site even if there is database of engine and there is connection to MySQL, but at that there is crash in one of WP’s tables (which is checking by installer). Particularly, the attack is possible when table wp_options (in WordPress 2.6.2) is damaged, or wp_users (in WordPress 2.0.3 and 2.0.11). I.e. in different versions of WP different tables is checked by installer - it can be table wp_options or wp_users (hardly possible that some other table will be checked by installer in other versions of WP).

Variants of the attack at the site on WordPress (which has installer at the site):

1. In case, if such crash in MySQL was happened on the site and such dialog of installer is showed, then it’s possible to attack the site. Taking into account that it’s very unlikely, and it’s also needed to detect the time of the crash, so better to use other variant of the attack.

2. Make DoS attack on MySQL for the attack on WordPress. Due to DoS attack there will be crash with connection to MySQL and installer potentially can show installation dialog. Though in most cases the connection to DBMS will be lost completely and installer will show corresponding message.

3. Due to automated attack on MySQL (via Insufficient Anti-automation vulnerability in WP) it’s possible to lead to crash in one of checked tables (which also is DoS attack). And in this case installer will work.

Particularly for WordPress 2.0.x and other versions of engine, where installer checked wp_users, this can be done via automated users registration. If to resister user actively at the site, then there can be crash in table wp_users and so engine will can’t read it and show dialog of installer.

[ Gravatar Icon ]

Jeff Starr#54

@MustLive: Thank you for digging into this issue and providing everyone with a much more in-depth look into the vulnerability and how it operates. I am going to update the post with a link to this comment and your article (even though it is written in Russian ? language). Incredible work, my friend — thanks again for looking into this and advancing our knowledge of the issue. Cheers, Jeff

[ Gravatar Icon ]

Sajid#55

Many thanks to MustLive for his work on this security issue!

[ Gravatar Icon ]

Metahuman#56

Your Fix#3, according to me, can cause a mail bomb.
Consider if the attacker actually reads the source of that file and plain refreshes the page, can cause those many mails to be sent.
Also, if he wants to play the vandal, he could just add it to his mailbomber and click bombs away!

[ Gravatar Icon ]

Jeff Starr#57

@Metahuman: I don’t think so.. PHP source is not available to the browser. An attacker would have no way of knowing if their email bomb attempt was working. However, as I have painfully discovered, normal traffic can be just as much of a “mail bomb” when the server goes down. In the course of an hour, my email account receives hundreds of emails telling me that “there is a problem with teh database!” I need a script that will send one email per occurrence, so if you have any ideas, let me know!

[ Gravatar Icon ]

Metahuman#58

Umm. Maybe, set up a cookie?

[ Gravatar Icon ]

Jonathan Ellse#59

Maybe get it to write the value to the database and look in the database to see if its already emailed you - oh hang on……

The cookie idea sounds sensible - a value like have-emailed to true with a cookie that expires in 6 hours or something similar

Then,

if (!isset($cookievalue))
{
send the email etc.
}

would work nicely

[ Gravatar Icon ]

Callum#60

A cookie won’t help, it would only work per user assuming they store cookies, which any kind of attacker probably won’t.

The only option for persistent storage if the database is down is the filesystem. Most WP installs can write files to wp-content/uploads so the script could create a file somewhere there with a timestamp of when the last db warning email was sent. Then one email could be generated per X hour period.

[ Gravatar Icon ]

Jeff Starr#61

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 :)

[ Gravatar Icon ]

Vladimir#62

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 :-)

[ Gravatar Icon ]

Jeff Starr#63

@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. :)

[ Gravatar Icon ]

Vladimir#64

@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.

[ Gravatar Icon ]

PhilB WordPress#65

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.

[ Gravatar Icon ]

Jeff Starr#66

@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.

[ Gravatar Icon ]

Jack Palancio#67

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.

[ Gravatar Icon ]

Strictly Online Biz#68

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

[ Gravatar Icon ]

Jeff Starr#69

@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!

[ Gravatar Icon ]

Greg Sampson#70

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?

[ Gravatar Icon ]

Jeff Starr#71

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

[ Gravatar Icon ]

Chuck#72

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!

[ Gravatar Icon ]

Zia#73

Good to know. Spreading the word.

[ Gravatar Icon ]

Wyteria Jacobo#74

thanks, Jeff. My blog was defaced among other things. I am deleting the install file on my other blogs. On the one that was defaced, I went with option 3 just because I like to pretend that I know code sometimes :-)

Trackbacks / Pingbacks
  1. Possible Security Threat? - WordPress Tavern Forum
  2. Un importante fix di sicurezza per WordPress » Ubuntu block notes
  3. WordPress 2.8 Feature Passwort - WordPress, Sicherheitsrisiko, Standard-Passwort, Daher, Links, Hinweis - WordPress Deutschland Blog
  4. Important Security Fix for WordPress
  5. WordPress 2.8 Feature Password - WordPress, After, Nevertheless - WP Engineer
  6. Important Security Fix for WordPress • Perishable Press | MySQL Security
  7. Wordpress security issue — Blog on Photoblogs
  8. WordPress Weekend Resources - May 8, 2009 | Theme Lab
  9. WordPress Install Files Security Risk
  10. WordPress Install File Poses Security Risk | PsychNet Reviews and Recommendations
  11. Important Security Fix for WordPress • Perishable Press
  12. BlogBuzz May 9, 2009
  13. magnus.de
  14. (Anti) Social-Lists 5/10/09 | (Anti) Social Development
  15. WordPress Weekend Resources - May 8, 2009 | cyooh.com
  16. WordPress 2.8 Will Be Released in May - Indian Domain Names Forum
  17. | Armanni Luca T J T
  18. links for 2009-05-26 | BlueWave Media
  19. Wordpress SEO & Security friendly .htaccess - WebProWorld
  20. Wordpress Security Risk - delete the install.php file! Or possible vulnerability | Security-Exchange News
  21. Custom WordPress Installation
  22. Remove hidden spam links from WordPress | Marty Thornley
  23. Important Security Fix for WordPress « Zia Notice Board
  24. Securing Wordpress: The basics « theriatech.com
  25. Datenbankfehler in WordPress: db-error.php | WeizenSpr.eu
  26. Cómo incrementar la seguridad de una web creada con Wordpress | emenia.es
  27. Custom WordPress Installation » hiiinfo.com
Share your thoughts..

Read Comment Policy

Comment Rules: No spam. No profanity. Use your real name. You may use simple HTML tags for style. Wrap all code in <code> tags. Learn more.



Attention: Do NOT follow this link!