Codegate 2017 2D Life Writeup
Description
2D Life
470 points
1 2 3 4 |
|
I didn’t have enough time to solve this challenge since I’m busy at work. It’s a pity that my team didn’t, neither. But I have to say it’s a very challenging one. Combination of crypto and SQL injection.
First Sight
It seemed to be a web challenge because the entrance was a website. So let’s start with HTTP requests and responses. In the source code of the page, a path to secret login page was commented.
1 2 3 4 5 6 7 8 9 |
|
The login page set a cookie like this(using httpie)
1 2 3 4 5 6 7 8 9 10 11 |
|
It’s easy to say that the cookie is two parts of base64 encoded string concatenated by a |
.
1
|
|
Different cookies was returned when repeating the same request. Modify the tail of the cookie will got a message Error has occur from decrypt..
, but the head won’t.
Cryptography
Look at the two parts of cookie:
1 2 3 4 5 6 7 |
|
Now I believe it’s a Padding Oracle
Problem. I’ve read about it in Web Security by White Hats (刺总的《白帽子讲Web安全》). Part 1
is the 8 bytes iv
of encryption, and Part 2
, obviously is 8 blocks of encrypted data, with 8 bytes in each block.
CBC Mode
Every Block cipher can only deal with a message with fixed length (usually the same length as the key), so plain message is divided into several blocks and each block will be encrypted separately. To avoid data pattern sniffing, a vector is added befor encryption in CBC mode.
1 2 3 4 |
|
Vector of each plain data block is the encrypted data of previous block. The Initial Vector for the first data block is provided additionally.
PKCS#5 Padding
Length of every block must be exactly the same with the key. In this case, the length is 8 bytes. If there is less than 8 bytes(or just equal to 8 bytes) in the last block, a padding is introduced.
1 2 3 4 5 6 7 8 9 |
|
While decrypting, cipher will check the value of the last byte in the decrypted message. Assume that value is 0x04, then check the value of the last 4 bytes. It will be fine if they all equal to 0x04 and the 4 bytes will be directly removed to recover the original length of plain message. Otherwise a decryption exception occured as I tried above.
Padding Oracle Attack
We know a bad padding format of the last block will cause exception, so if we craft a fake data which can make the padding match the right format, the data will be accepted by the server without throwing a decryption exception(This does not means it will be completely accepted by server without any other excpetions because the data is totally a mess). At this moment we know the last few bytes in the decrypted message, is one of the padding format.
We’ve got last bytes of plain block and the vector(we craft it), so we can get the last bytes of intermediate value of the corresponding encrypted block by
1
|
|
and then, the real plain block
1
|
|
To make it clear, we can brute force every byte in a block, from the last byte to the first one.
1 2 3 4 5 6 7 8 9 |
|
Start with the first block ea e0 c6 90 3e 55 b4 49
, enumerate the last byte of iv, from 0x00 to 0xFF.
1 2 3 4 5 |
|
visit secure login page with the fake cookie:
1
|
|
got the message Error has occur from decrypt..
continue trying with different iv(this can be done with a piece of script)
1 2 3 4 5 |
|
a different message showed up when trying 0x1f
as the last byte in iv.
1
|
|
BINGO! It means the padding is 0x01 now(not quite), more clearly, the last byte of the plain message is 0x01.
PS: If the second to last byte in the plain message just happen to be 0x02, then the last byte may be 0x02, too. Both 0x01 and 0x02 are valid at this situation. Just change the last 0xff in iv to any other value and try again, which will break the combination of 0x02 0x02
padding (into 0x?? 0x02
). If nothing different with 0xff(no decrypt error occuring), 0x01 is the right answer.
the last byte of intermediate value can be calculated by
1 2 |
|
and then calculate the last byte of original plain message by the original iv
1 2 |
|
The last byte of the first plain block is 0x20
!
Next byte, we need to make the plain message have a value of 0x02 in the last byte, to test the 0x02 0x02
padding. So last byte of iv must be 0x02 (+) 0x1e = 0x1c
Trying like this
1 2 3 4 5 |
|
ff ff ff ff ff ff e4 1c
will make the sense. 0xe4 (+) 0x02 (+) 0xa3 = 0x45
Finally we can get the first block:
1 2 3 4 |
|
Continue with the next block:
1
|
|
Notice that the original vector of this block is the previous enctyped block ea e0 c6 90 3e 55 b4 49
, not the iv.
After all the entire message came out:
1
|
|
SQL Injection
We didn’t got the flag but a hint
1 2 |
|
It should be a SQL injection attack.
I dinn’t solve this until the server was shut down. TAT