From bug to exploit - Bakery SSO
Heine Mon, 2013/02/18 - 12:59
I recently evaluated the Bakery Single Sign-On System aka Bakery SSO aka Bakery on behalf of clients. This article describes how I moved from finding a small weakness in version 2.x-alpha-3 to an exploit.
If you haven't updated all your sites to Bakery 2.0-alpha4 (6.x, 7.x), go and do so now.
About Bakery
Bakery provides a "single sign on" feature for Drupal based sites that are on the same second-level domain (i.e. example.com, subsite.example.com, subsite2.example.com). The Drupal.org family of sites uses Bakery so you can login on Drupal.org then visit and browse groups.drupal.org, association.drupal.org or drupalconlatest.drupal.org as an authenticated user without having to login again.
Very handy indeed.
How Bakery works
Bakery knows two types of sites, the master and slaves. The Bakery master is the only site that actually handles user authentication via username and password. Slaves forward login data to the master, but do not authenticate the user themselves via username and password.
Bakery works by passing around data in what it calls "cookies", confusingly both in actual (shared) cookies and in POST requests. These cookies contain the passed data together with a signature (a HMAC) in a base64-encoded encrypted string. The signature should ensure message authenticity (ie actually originates from master or slave). The encryption and signing key is shared between master and slaves. While Bakery makes a number of cookies, the two most important for this entry in the bug2exploit series are Oatmeal and Chocolatechip.
If you login on slave S, it will forward the username and password to the Bakery master M in Oatmeal via a browser redirect. The Bakery master does some checks, logs in the user and creates a Chocolatechip cookie only to redirect the user back to the slave. The slave checks the Chocolatechip cookie, updates relevant information and logs the user in.
While Chocolatechip serves to authenticate a user to the slaves, it also works to authenticate to the master. The cookie carries the username, mail and init together with some housekeeping fields.
Encryption & signatures
Before we continue to the weakness in Bakery, we need to take a look at the structure of cookies. Whenever Bakery bakes a cookie it starts by building an array with the data to send and a HMAC-based signature.
The next step in baking a cookie is to serialize the array structure, encrypt it via AES in ECB mode using the shared private key and finally base64-encode the result.
When the Bakery master receives such a cookie, it runs the process in reverse. First, it base64-decodes the contents, decrypts the result, then unserializes this into a $cookie array. Bakery then calculates a signature and checks if it matches the signature delivered in the cookie array.
Weakness: Unserialize on cookie value
Any security researcher will perk up when she sees an unserialize on user-supplied data. Such a call can be used to invoke object destructors with values chosen by an attacker. On Drupal 7, this can be used to delete files from the filesystem. As signature verification happens after unserializing the data, it cannot interfere with our nefarious purposes.
Alas, as Bakery decrypts the cookie before it calls unserialize on it, we need to find a way to get our payload encrypted with the private key or the decryption step will result in random garbage. This is where the next weakness comes in.
Weakness: ECB mode
AES is a block cipher that can be used in a variety of modes. In Electronic cookbook mode (ECB) used by Bakery, the plaintext is split in 128-bit blocks and each block is separately encrypted with the key. The content of one block doesn't influence the ciphertext of the next block.
Here's a short example, with a reduced blocksize of 64-bits to illustrate the consequences. A string that fits into 2 64-bit blocks has been encrypted in ECB mode. We then take the ciphertext, split it in 64-bit blocks and swap their positions. When we decrypt, the corresponding plaintext blocks have also switched position.
h e i n e j a n d e e l s t r a
4e2387db4b9713f0 5e9275df5301bf8b
5e9275df5301bf8b 4e2387db4b9713f0
d e e l s t r a h e i n e j a n
This property of ECB means that we can swap, shuffle and delete blocks or even insert them into other encrypted messages to get control over the decryption result. All we need to take care of is to align injected data to block borders and to identify these blocks in the ciphertext. Note that injected data may span multiple blocks.
So, in order to exploit the unserialize call, we need to have Bakery encrypt our payload and make sure this payload gets aligned to an AES block start. The best candidate is the Oatmeal cookie, as it contains data we supply via the login form.
Here's the PHP data-structure of Oatmeal after a slave login, just before it is sent to the encryption pipeline:
// data comes from form_state['values']
'data' => array (
'name' => 'username',
'pass' => 'mypassword',
'op' => 'Log in',
),
'name' => 'username',
'calories' => 320,
'timestamp' => 1358887168,
'master' => 0,
'slave' => 'http://slave.yawning-angel/',
'signature' => 'complicated_looking_signature_here',
);
Under special circumstances 'data' may contain additional fields. On most sites however, this is what we have to work with. Serialized and split in 128-bit blocks this looks like:
a:7:{s:4:"data";
a:3:{s:4:"name";
s:8:"username";s
:4:"pass";s:10:"
mypassword";s:2:
"op";s:6:"Log in
";} .......... }
Remember that these blocks correspond 1:1 with the blocks after encryption. Supplying an 8 character 'username' gets the password value aligned to the start of the fifth block. One can simply delete the first four blocks to get a decryptable cookie that starts with user-supplied data. Because unserialize doesn't care about trailing characters after valid input, there's no need to worry about the extra blocks on the end, provided the "password" itself can be unserialized.
With all that encryption out of the way, lets see if we can do something fun with the payload that works on both Drupal 6 and Drupal 7 sites. Can we bypass the signature check and forge valid Chocolatechip authentication cookies?
Yes we can.
Weakness: Type juggling
Here's a typical signature comparison executed by Bakery. The received signature is compared to a hash derived from several received fields and the Bakery secret key. The age of the cookie is determined as well.
if ($signature == $cookie['signature'] &&
$cookie['timestamp'] + variable_get('bakery_freshness', '3600') >= $_SERVER['REQUEST_TIME']) {
$valid = TRUE;
}
The comparison operator == has some interesting type juggling properties. If you supply a boolean TRUE on one side and a non-empty string on the other (such as 'impressive hash' == TRUE), the expression evaluates to TRUE. Because an attacker can control the type of $cookie['signature'] via the unserializable payload the signature check is no obstacle. A serialized boolean true for signature would look like s:9:"signature";b:1;
.
Exploit
In order to forge a valid Chocolatechip for a targeted user account, the payload needs to meet the following requirements:
- contain boolean signature
- contain a valid timestamp
- contain the username of the target
- contain the email address of the target
- all this serialized …
- …but in 128 chars or less
Here's a payload to attack the user account 'deelstra' with info@ustima.com as mail address. The exponential notation for a future timestamp (2E9) allows additional room to spend on username and email.
Using the string above as the value for password gets us the following structure of Oatmeal. The actual Oatmeal cookie is of course encrypted, but will correspond to this block layout. Throw away the first 4 blocks and Oatmeal is transformed into a valid Chocolatechip cookie.
a:7:{s:4:"data";
a:3:{s:4:"name";
s:7:"usernam";s:
4:"pass";s:112:"
a:4:{s:9:"signat
ure";b:1;s:9:"ti
mestamp";s:3:"2E
9";s:4:"name";s:
8:"deelstra";s:4
:"mail";s:15:"in
fo@ustima.com";}
";s:2:"op";s:6:"
Log in";} .....}
While this specific exploit depends on knowing the email address of a targeted user, do not base your upgrade decision on this detail. Exploits that do not require any additional information beyond the user id are also possible.
Solution
Bakery 2.x-alpha4 has been modified to guard against these types of attacks. It still uses ECB (this will change in another release) but cookie handling has markedly improved:
- Signature is sent outside of the encrypted data
- Signature verification uses the === operator
- Signature covers the entire encrypted cookie (block order, all fields)
- Cookie type information has been added to the cookies and is checked on receipt