This CTF marked the start of my journey on preparing myself to convert my role from Software Engineer to Security Engineer for the next 4 years.
On this CTF, I managed to solve 2 Crypto Challenge
Today, I will explain my solution on solving the GoodHash.
I think I made a good hash function based on AES. Could you test this?
nc good-hash.chal.perfect.blue 1337
Author: rbtree
file:main.py
I really love this challenge, because I learn a lot of new things during taking my time to solve this challenge. After I read the source code, some key notes that we could infer:
goodhashGOODHASH
ACCEPTABLE
variable"admin": true
if we want to get the flag\0*32
digest
methodAfter reading many articles in the internet about AES-GCM, below is the encryption scheme.
Further analysis:
pt1^Ek(Counter 1) = Ek(Counter 1)
because of null plaintext)iv||counter
, where iv is 12 bytes and counter is 4 bytes.'{"token":"xxx", "admin": false}'
) and our input length will always be longer than 12 bytes, which mean our nonce will be always hashed by GHASH method. (This is important note)For detailed info, you can read wiki, but I'll try to explain it.
Defined as GHASH(H,A,C), it requires 3 inputs:
H: The secret key, calculated by Ek('\0'*16)
A: Associated data
C: Message that we want to authenticate
Notes:
The multiplication of xor(x1,x2)•H is happen on defined by polynomial
So, if you read the source code, you can see that:
{"token: "xxxxxxxxxxxxxxxx", "admin": false}
, which length is 61.I've explained above that if the nonce is not equal to 92 bit, by default AES-GCM will GHASH the nonce. Let's try to simulate it:
{"token: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "admin": false}
. Length is 61GHASH(H,bytes(),nonce)
(without any associated data)\x00
. final_x
will be used as the AES-GCM first block input (substitute of iv||counter
)The vulnerability of this code is we know the secret key H
. Because we know the secret, we can do preimage attack. Consider the below equation, which is how GHASH works:
where A is the input that we control.
If we know state , and we want the next state equals to let say
We can get the correct by multiplying it with .
So, based on that preimage attack, we can craft a valid nonce which hash collides with the given hash, yet contains of "admin": true
Connect to the server, we got this:
First, we need to calculate the GHASH of the body
. By doing the previous simulation, we will have:
target_x1
(hash of block1, {"token": "c6ffd
)target_x2
(hash of block2, ed97492efee4eca4
)target_x3
(hash of block3, 2ac218a7088", "a
)target_x4
(hash of block4, dmin": false}\x00\x00\x00
)target_finalx
(hash of nonce length)After calculating each block hash, we can start crafting our payload:
block_1
with '{"admin":true, "'
(len = 16). So:
new_block1='{"admin":true, "'
new_x1=xor(x0, block_1)•H
new_block2
with random string length of 16, which all the characters are allowed. Example, the random string is aaaabbbbccccdddd
, so:
new_block2='aaaabbbbccccdddd'
new_x2=xor(x1, block_2)•H
new_block3
, so that the new_block3
hash value will be the same as target_x3
.
new_block3=xor((target_x3⋅H^-1), new_x2)
new_block4
, reuse the given block4
, which is dmin": false}\x00\x00\x00
new_x3
is equals to target_x3
, which mean the value of new_finalx
will be equals to target_finalx
, and because it is the same hash collision will happen!!Important note during crafting the payload:
new_block2
string until the correct value of new_block3
consist of the allowed characters.So, here is my full solution.
Running the code will give you this
Putting one of the found payload will give you the flag
Flag: pbctf{GHASH_is_short_for_GoodHash_:joy:}
Follow me on Twitter