Tags: cryptography 

Rating:

The authentication system in this challenge uses a JSON blob with the user's username, password, and admin status (in that order), encrypted under an unknown key with AES-192 in CBC mode. The authentication cookie can be modified, and we have a partial decryption oracle which gives us the resulting values of "username" and "admin" in the JSON blob. Additionally, we have an encryption oracle that we can use to get a good value for modifications. If we attempt bit flipping on the cookie, we can see successful changes being made to the username and admin status values. In order to make targeted edits to any block of plaintext when tampering with encrypted data in CBC mode, we must randomize the previous block.

We know that our blob will look something like this:

{"username":"foobar","password":"hunter2","admin":"false"}
We start by logging in with a long repetitious password, 32 'A' characters. Since AES-192 has a block size of 16 bytes, this guarantees that we have at least one block of data in our resulting auth cookie that we can mostly randomize without consequences. However, two problems remain: The bit flips that change our admin status to "true" might result in a block that contains a double quote; this will occur roughly 1 in 16 times. The other problem is that we have to change a 5 byte value to a 4 byte value. On line 39 of the code provided, we can see that any character other than valid JSON characters and alphanumeric characters is discarded after decryption and before the data is parsed in JSON:

<span>var auth = decrypt(req.cookies.auth).replace(<span>/[^0-9a-zA-Z{}":, ]+/g</span>, '');</span>
We can use this property to solve both problems! There are many characters that are not within the acceptable character range, we can change four of the characters of false to true, and try each possible value for the last character until we reach a scenario where the sacrificed block does not contain a double quote and the last character is discarded by the regex.

One more issue remains: The string ","admin":"false is exactly 16 characters long, and we need to align it such that it is within its own block, since we don't want to deal with our random block needing only one double quote at the end and we want to modify all the characters of "false". We begin by entering different passwords to generate auth cookies until the cookie grows in length, indicating that the end of the JSON blob is aligned with the end of the second-to-last block. We add another two 'A' characters to the end of the password to push the trailing "} of the JSON blob into the last block. We don't know if there is any whitespace padding in the JSON blob, so we attempt to bit flip the last character in the second to last block to a few different values to see if we're modifying the "admin" value or destroying the JSON structure. We can try all 256 possible values, if none of them work it is very likely that we're destroying the JSON structure. As it turns out, there is whitespace and we must add one more character to our password to align the value of "admin" to the end of the second to last block. We flip the value of "false" to "truee" and then flip the last character until the JSON remains intact and the last character of the "admin" value is discarded, leaving us with "true", and matching the conditions for revealing the flag.