Loading [MathJax]/jax/output/HTML-CSS/jax.js

Tags: aes-cbc crypto padding-oracle 

Rating:

We start by observing that there are two classes of possible responses. If we submit the given cookie, it gives Nop. :( or Don't be rude!. If we submit a random hex string, it gives That cookie looks burned!. This suggests a potential padding oracle attack. Typically, a padding oracle attack is used to decrypt messages, but in this case we use it to encrypt.

We start by looking at how AES-CBC decryption works.

Let Cn be the nth ciphertext block. Let Pn be the nth plaintext block. Let D be the AES decryption function. We see that Pn=D(Cn)Cn1 This means that we can control Pn by selecting Cn1=D(Cn)P1n, where P1n is the desired plaintext. However, this means we have to determine D(Cn).

Next, we have to understand how PKCS #7 padding works. Let's assume the block size is 16 bytes, and the last block of plaintext is only 9 bytes long. We pad the remaining 7 bytes with the hex of the number of bytes remaining, 07, which becomes 07 07 07 07 07 07 07. If the last block of plaintext is exactly 16 bytes, we still need to add padding to signal that the previous block is unpadded. In this case, we append a block of 16 null bytes.

If a program gives a different output when this padding is wrong, then we can use that to get D(Cn).

Let A[i] be the ith byte of A. We start by sending a 2 block message, a block of null bytes and the Cn we want to decrypt. The first block will be treated as the IV, and the second block will be decrypted and xored with the IV. 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 D(Cn)

The only way this could have correct padding is if D(Cn)[16]IV[16] has a last byte of 01 (or 02 02, etc... but these are rare). Then we have determined the last byte of D(Cn), since D(Cn)[16]IV[16]=1, D(Cn)[16]=IV[16]1=01=1

Otherwise, we increment the last byte of the IV.

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 D(Cn)

Again, if this has correct padding, we have determined the last byte of D(Cn), since D(Cn)[16]IV[16]=1, D(Cn)[16]=IV[16]1=11=0

In general, we continue incrementing the last byte of IV until we get correct padding.

Once we get the last byte of D(Cn), we can extend this process to get the second last byte.

First, we select the last byte of IV such that D(Cn)[16]IV[16]=2, which simply means IV[16]=2D(Cn)[16].

Again, if this has correct padding, it means that D(Cn)[15]IV[15]=2, as 02 02 would be the only valid padding here. Using similar logic as before, we continue incrementing IV[15] until we have correct padding and then determine D(Cn)[15]. We can then repeat this whole process for every byte until we have D(Cn). We can then select Cn1=D(Cn)P1n.

Combining all of this, we can create our exploit. We start by creating a random block of 16 bytes which will be our Cn. We select a Cn1 such that Pn is what we desire. We repeat this process to find a Cn2, etc... until we reach C1. That block will be our IV. We append all blocks together to get a ciphertext that decrypts to the desired plaintext, in this case, Maple Oatmeal Biscuits, and submit to get the flag.