We have the server source code and its network address. As usual, let’s examine the code:
So the server allows us to perform two operations: generate token and verify token. We can generate token only once per connection but we can verify it as many times as we want (we need to verify token just once anyway).
For token generation we send server arbitrary hex string and it performs the following actions:
- Get 16 random bytes of IV.
- Decode hex to bytes and check that it doesn’t contain
gimmeflag
. - Initialize AES-CBC with generated IV and KEY and encrypt received bytes.
- XOR
tag
with ciphertext (astag
is initially contains only zeroes, after XOR operation it will be equal to ciphertext) - XOR
tag
withiv
- XOR
tag
withentropy
, whereentropy
is some random byte string. - Return
tag + ciphertext
For verification we must send to server two hex strings: token
and tag
. And verification is the following:
- Decode
token
andtag
from hex to bytes. - XOR
tag
withtoken
- XOR
tag
withentropy
- Set
iv
totag
. - Initialize AES-CBC with
iv
and KEY, decrypttoken
and return result.
If verification process returns any string that contains gimmeflag
server will print flag.
Let’s summarize:
- We can generate token from arbitrary hex string
- We can verify arbitrary token with arbitrary tag
- Once verification process returns byte string that contains
gimmeflag
we get the flag - We cannot generate token from
gimmeflag
.
Solution
So we need to somehow forge token. There is no chance to determine the key (or maybe there is, but why bother?), so don’t even think about it. All we can do is somehow manipulate generated token or tag and that’s what we will do. Let’s remember CBC encryption mode scheme
Consider the decryption scheme. Note that in this challenge we know both plaintext (not just know, but can choose it) and ciphertext (also can choose it). So we know that, for example, first block of ciphertext XORed with whatever comes out of block cipher decryption for second block gives us second block of plaintext. Let’s write it down in math:
where D
is block cipher decryption function (AES in our case). I assume that other notations are clear. Now observe that manipulating first ciphertext block we can get second plaintext block to be anything we want:
So, if we want second block to decrypt into some P2'
we do:
Now the attack draws itself:
- Generate token with arbitrary bytes
- Change first block of token so that second plaintext block decrypts into
gimmeflag
. - Send edited token to server
- Voila
This is a well known bit flipping attack against CBC. Exploit code is below. I use xor function from my own package ptCrypt
. You can find analogue in popular libraries, such as PyCrypto
(or implement it yourself).
Script run result: