Tags: aes wasm
Rating: 4.5
Solved by: krystalgamer
# WASM(nasm) Fans Only
### Requirements
- wabt - (wasm2c) converts wasm to c
- c compiler
- chromium based browser (couldn't get it to work on firefox)
- disable any script/ad-blocker since it may block the wasm file
- decompiler (not necessary but helpful)
### verifyFlag.js
Analyzing the file there's two interesting functions: `lose` and `win`.
Set a breakpoint on `lose` and press login. According to call-stack it was called from the wasm module, more precisely the `verify_flag` function.
If the call to `function23` returns 0 then it doesn't even go into the big loop.
### Deep diving
Download the wasm module run:
`wasm2c verifyFlag.was -o converted.c`
Converted module dependecy: https://github.com/WebAssembly/wabt/blob/master/wasm2c/wasm-rt.h
`cc -c converted.c`
#### function23
Calls `function25` and checks whether the result is 24. After that goes into a loop and checks for character `'}'`.
#### function25 (strlen)
Starts by checking memory alignment of the passed `ptr` and if it finds any zero byte, quits immiediatelly returning 0.
Then it goes into a loop, incrementing a pointer, and performing this check: `v - UINT64_C(0x0101010101010101)) & ~(v) & UINT64_C(0x8080808080808080);`, which is a zero byte checker, used as a `strlen` performance enhancement. It then returns `ptr-argument`. It's a `strlen`.
Conjugating this knowledge, this function must be checking the structure of the data passed. 24 characters and ending with a `'}'` -> `utflag{16_chars_here}`.
### Backtracking `lose` calls
Besides the one mentioned above there's another one, that is called in a tight loop check.
```
while(..)
if(x!=y)
return lose()
win()
```
Two buffers are being compared, the result of `function9` and another that was already in memory in position `5245584`(the encrypted flag!).
#### function9
The real name of this function is `aes_encrypt_block` and takes three arguments. `(buffer_to_encrypt, key, output_buffer)`.
The `buffer_to_encrypt` is a ptr to the password field, starting after the `'{'`, the key is `nasmfans.ga` and the output_buffer is `5245568` (contiguous to the encrypted flag!).
#### Encrypted flag
Having the key `nasmfans.ga` and the encrypted key `[15, 174, 248, 89, 132, 177, 40, 103, 40, 24, 136, 23, 100, 211, 37, 42]`. We can just decrypt it.
```
from Crypto.Cipher import AES
encrypted = [15, 174, 248, 89, 132, 177, 40, 103, 40, 24, 136, 23, 100, 211, 37, 42]
key = bytearray()
for c in 'nasmfans.ga':
key.append(ord(c))
while len(key) != 16:
key.append(0)
cipher = AES.new(bytes(key), AES.MODE_ECB)
enc_buffer = bytearray()
for e in encrypted:
enc_buffer.append(e)
print(cipher.decrypt(bytes(encrypted)))
```
Which yields `fPRv38aICAz31Ix7`. The flag is `utflag{fPRv38aICAz31Ix7}`
Feel free to reach out to me for any questions.
##### Final notes on function10
I can't leave without talking about this piece of shit, that took me a lot of time to understand. It just converts an array to a matrix. For some reason I thought the name `aes` was just to troll...
Hi,
I used a very similar approach, except I compiled with `-m32 -fno-PIC -O2 -c -fno-reorder-functions -fno-inline-functions-called-once -fno-inline-small-functions` in order to simplify most of useless variables (and tweaked the get_intXX/set_intXX to have direct x-refs to data in IDA instead of computing offsets manually).
Nice writeup :)