Password Masking

I’ve written up a little page on password masking to read at your convenience.

This was born out of an article written by Jakob Nielsen titled “Stop Password Masking“. In it he argues that masking passwords does little to really prevent password theft, but does quite a lot of harm in the form of user errors as they are unable to tell if they’ve made a mistake in typing their password. Security pro Bruce Schneier weighed in as well, initially agreeing with Nielsen, then rethinking his ideas a bit.

I decided to see what we, on the web side of things, might be able to do to make it a little easier on users, but still stay secure.

New Gargoyles Comic

A shameless plug for Gargoyles: Clan Building Vol. 2, which contains issues 6-12 of the Gargoyles comic. The comic is written by series co-creator Greg Weisman and it continues where the TV series left off after season two. It’s an awesome book. Very rich. Very dense. This is not just for kids, it appeals to adults as well. It’s well worth your time to check it out.

Sadly this will probably be the last Gargoyles story told in an official capacity. The book’s publisher, SLG, were not able to renew their license with Disney after Disney increased the license fee on Gargoyles. Some rumors are afoot that say if the book sells really well then SLG might decide to take a chance at renewing the license (and risk considerable money if the book doesn’t sell).

But I’ve got a feeling this is going the way of the DVDs, which ended after the first half of season 2 was released 4 years ago and fans were left without the second (and concluding!) half of season 2.

Although it seems Disney is quite good at screwing up quality products other than Gargoyles. For example they’ve yet to green-light a third season of Spectacular Spider-Man. A complete no-brainer and also a show produced by Greg Weisman. Makes me wonder if there’s some sort of bad relationship between Weisman and Disney.

WordPress Exploit – PHP Programming

A recent exploit was discovered for all versions of WordPress prior to and including 2.8.3. The vulnerability is in how variables are checked while processing a password reset request. The offending code is quite simple and by itself looks very innocent:

if ( empty( $key ) )

If the $key variable is empty, an error is generated. If $key is not empty it’s passed to a SQL query that looks up the user whose key is being reset. Seems innocent enough, right?

The variable $key is a sanitized copy of $_GET[‘key’]. And what happens if the value passed on the url for key is an array?

Well the sanitization step is a simple preg_replace, like so:

$key = preg_replace('/[^a-z0-9]/i', '', $key);

The PHP documentation states that if the subject ($key) is an array, the return value will be an array as well. It also states that if no matches are found, the original subject is returned. Since $key is empty to begin with, thus no matches will be found, a copy of $key is returned. Essentially $key remains unchanged. So what happens when you pass an empty array to PHP’s empty() function? Well the documentation says it should return FALSE. So what’s the problem?

The problem is the array isn’t really empty. It contains a single node which contains an empty string. So the empty() call returns FALSE, thus no error is generated for an empty $key.

Surely the SQL check will return no entries since there is no key to search for, right?

Wrong. Here’s the code:

$user = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wpdb->users WHERE user_activation_key = %s", $key));

The $wpdb->prepare()  is used to insert values into SQL statements. $key is treated like an empty string. Therefore the SQL that is generated looks like:

SELECT * FROM wp_users WHERE user_activation_key = ''

And that query will return every user who has an empty activation key — that’s everyone not currently trying to reset their password. Since the first row returned is the record that gets reset, you will almost always be resetting the admin account. If you keep making the same request over and over you will eventually wind up resetting the password of every user in the system!

Reset passwords are auto-generated and then e-mailed out to the users. This sort of attack could be used as a weak style of denial-of-service (or pain-in-the-ass attack as I prefer to call it) or an attacker who has access to a victim’s e-mail account (or can packet sniff their e-mail downloading)  could take control of the blog.

A fix is already on the way as you can see by viewing wp-login.php in WordPress’ CVS. The fix seems simple enough:

if ( empty( $key ) || !is_string( $key ) )

So $key must be a string and it must be empty. Seems very straightforward. Although I would prefer they went the added step of simply modifying the SQL query to check against only those user_activation_code fields that are not empty strings. In other words add something like

$user = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wpdb->users WHERE user_activation_key = %s AND user_activaction_key <> ''", $key));

That would more correctly fix the issue, as it would protect against even those attacks that are somehow able to bypass the initial empty() check.

WordPress appears to also be adding a username to the password reset request so that simply passing an empty key on the URL and hitting reload a bunch of times won’t result in everyone having their password reset — probably something they should have done in the first place.

So there it is. Patch your WordPresses, either by manually editing wp-login.php or waiting for the official patch to come out sometime later this week.