Tags: aes wasm
Rating: 4.5
Solved by: krystalgamer
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.
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
Calls function25
and checks whether the result is 24. After that goes into a loop and checks for character '}'
.
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}
.
lose
callsBesides 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!).
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!).
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.
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 :)