textpattern cookie weakness

Textpattern cookie weakness in all releases to g1.19

Below is an article I wrote on September 12, after discovering an issue with Textpattern login cookies. I sent a draft to Dean that same day, and he promised to fix the issue in the next release.

Textpattern 1.0rc1 is out. According to the release notes, the new version uses random nonces, which should mean it’s now safe to publish this.
—-

Let me clarify first: there is not, as far as I’m aware, an open vulnerability in Textpattern’s cookie login system.

There is, however, a weakness in txp’s cookie hash function. This weakness isn’t exploitable by itself, but it makes an attacker’s job easier, and ensures that the damage caused by a stolen cookie is unnecessarily high. A weak link in the security chain combined with a significant potential payoff probably means that the motivation to attempt to steal a Textpattern publisher’s cookie is quite high.

Background

When a user logs in to the administration interface, Textpattern helpfully offers to remember the user’s login details by storing a browser cookie. The cookie contains the user name and a hash value, which is a one-way encrypted string unique to that user. Next time the user visits the site, Textpattern checks that the cookie hash value is valid; if so, the user is admitted without a password check.

So far, so good. This is standard practice, and secure enough for most purposes. The user has the opportunity of refusing the cookie, should circumstances demand a higher level of security.

The weakness

In order for an attacker to impersonate an authorized user, she needs to guess the hash value stored in that user’s cookie. Guessing the hashed value itself is unreasonably difficult; but, since the hash function is known, the attacker can attempt to guess the cleartext values that are passed to the hash function.

In Textpattern releases to g1.19, the hash function looks like this:

md5($c_userid.$secret_word)

..that is, a MD5sum of the user’s login name and the Textpattern ‘secret word’ value. So, assuming that an account login name is known or easily inferred, an attacker can forge a login cookie merely by guessing the secret word. This has several consequences:

  1. The secret word is entered by the user during the Textpattern installation process. The user is not alerted as to its purpose or importance. It’s likely that many users accept the default word (‘spinach’), use a dictionary word, or leave the field blank. The Textpattern installer imposes no minimum length or quality check on its value.
  2. The secret word is identical for all users. Once it’s known, an attacker can forge login cookies for all users on that Textpattern install, even users who have never requested a cookie.
  3. Since the cookie hash is not dependent on the user’s password, a stolen or forged cookie will continue to work even if the password is reset.
  4. An attacker who has limited privileges (e.g. an occasional contributor to a group blog) can launch an offline dictionary attack against their own cookie hash to discover the secret word. Once they’ve succeeded, they can impersonate any user on that Textpattern installation.

This last issue is probably the most significant weakness.

The solution

The solution is to ensure that cookie hashes include a value that is (1) unique to each user, (2) unknown to the attacker, and (3) available to the Textpattern code.

There is already a value that appears to meet these criteria: the users encrypted password. txp_auth.php can retrieve the encrypted password and include it in the hash (something like md5($p_userid.$secret_word.$encrypted_password)) when the user’s cookie is set (lines 106 and 112), and again when the cookie is validated (line 86).

Unfortunately, while this reduces the severity of the problem, it doesn’t solve it in all circumstances. An attacker who has an account with user privileges can still launch a dictionary attack against their own cookie hash to recover the secret word, since she knows her own password. The consequences of this are greatly reduced, since the attacker can’t forge other user’s cookies without knowing their passwords. However, it does allow the attacker to launch an online dictionary attack, hashing cookies offline and submitting them to the server. (txp_auth.php attempts to thwart online dictionary attacks by inserting a 3 second delay in the authentication process; however, this only applies in the code that processes the login form, not the code that validates cookies)

A better solution is to generate a random nonce for each user, to be stored in the txp_users table. Since the nonce is known only on the server, an attacker with user privileges is no longer able to use brute force to recover the secret word.

The nonce should be set at account creation time, and retrieved and included in the hash value in the same manner as the encrypted password solution above. To help limit the damage caused by a stolen cookie, the nonce should be changed when a user’s password is reset.

Really nice Zem—well thought out and refined. I’ll be keeping all of this in mind for future and past applications… However, I would have waited a little longer before publishing this—RC1 is a little unstable, so many g1.19 users would be holding off a little longer, or may not even realise 1.0RC1 is available.

Justin French    Sep 21, 09:05 am    #

Fair enough, I should’ve mentioned that there’s a workaround for older versions:

Edit config.php and make sure your secret_word is a secure password. 12 or more random characters, including numbers, caps and symbols, should be sufficient for most purposes. This will cause existing cookies to break, so make sure you haven’t forgotten your password.

The offline brute force attack against group blogs is still possible, but a secure secret_word should buy plenty of time to evaluate and test the new release before upgrading.

PS – I’m aware of the risks inherent in publishing security vulnerabilities. I’m also keenly aware of the risks of not publishing them. I might be the first to have written this up, but I’m almost certainly not the first to have discovered it.

zem    Sep 21, 09:20 am    #

Hi zem,

nice find. Thank you writing it up and a explaining it thoroughly. :)

Sencer    Sep 21, 01:19 pm    #

so, in other words, this is an analogue to digest authentication as implemented for http?

iis    Oct 17, 11:45 am    #

Commenting is closed for this article.