Tags: length sha256
> attachment: https://drive.google.com/open?id=11lK6aKJZEq6QhrD6L7SSew4KhvXz_cz0
> nc cpushop.2018.teamrois.cn 43000
After playing a bit with the menus, we see that the script lets us see a list of items we can purchase:
1. List Items
0 - Intel Core i9-7900X $999
1 - Intel Core i7-7820X $599
2 - Intel Core i7-7700K $349
3 - Intel Core i5-7600K $249
4 - Intel Core i3-7350K $179
5 - AMD Ryzen Threadripper 1950X $999
6 - AMD Ryzen 7 1800X $499
7 - AMD Ryzen 5 1600X $249
8 - AMD Ryzen 3 1300X $149
9 - Flag $99999
The flag is right there, but we need 99999 money to buy it, while the script always sets our budget to a random number in \[1000, 10000\]. However, the purchase is split into two parts - ordering and paying. We can order the flag, and get a string like:
But if we actually try to use this order when paying, we get told we don't have enough money.
After some brief analysis of how the order token is created, we see it is a textbook case of length extension attack, even down to the same query string format as presented on [Wikipedia](https://en.wikipedia.org/wiki/Length_extension_attack).
So the script creates a SHA-256 hash of `signkey` + `query`, where `signkey` is its private randomised key, and `query` is something like `product=Flag&price=99999×tamp=1526990158350980`. The problem is that this is not a HMAC, nor is the hash encrypted before given to us. The hash / message digest produced by SHA-256 is actually its entire internal state at that point, i.e. we know that if we knew `signkey`, and created the SHA-256 hash of `signkey` + `query`, we would have no more data in our memory that what the digest provides.
So we need to find a SHA-256 implementation, edit it so we can change its internal state, and attach our extra string at the end, in this case `&price=1`, which will override the previous price of the flag product and make it affordable for us. This sort of attack also requires duplicating the way the padding algorithm works, but luckily, the Python script reads our input via `raw_input()`, which lets us input null bytes and binary data.
For the exploit I chose to use Haxe, since it is a language I am familiar with and it has a simple, native implementation of SHA-256 in its standard library.
We don't know what the length of `signkey` is, only that it is between 8 and 32 characters long. So the exploit will check all 24 possibilities (which differ in padding and length information only).
([full exploit here](https://github.com/Aurel300/empirectf/blob/master/writeups/2018-05-19-RCTF/scripts/cpushop/Main.hx))
After compiling, we can execute `neko exploit.n remote`, and with that we get: