Solving wapiflapi's reversing challenges
09 Dec 2015I have been spending quite some time solving reverse engineering based challenges and have been hacking on these recently. The first few ones were really easy and I particularly enjoyed solving challenges 5 and 6.
I have used gdb-peda and r2.
Challenge 5:
Loading up the binary in r2 and running aaa
analyses all functions and symbols. We then look at the main function.
...
[0x004005b0]> pdf@main
| | 0x004007f9 e89ffeffff call sym.check_password
| | 0x004007fe 85c0 test eax, eax
| |,=< 0x00400800 750c jne 0x40080e
| || 0x00400802 bf12094000 mov edi, str.password_OK
| || 0x00400807 e834fdffff call sym.imp.puts
| ,===< 0x0040080c eb0a jmp 0x400818
| ||| ; JMP XREF from 0x00400800 (sym.main)
| ||`-> 0x0040080e bf1e094000 mov edi, str.password_KO
...
This is the main part of the code where the input string is passed to the function sym.check_password
. If the function returns 0, we have “Password OK” else we have a “Password KO”.
The disassembly of check_password
function is pretty big but we can break it down in parts.
Checking if length of the string matches a predefined value
...
| 0x004006b1 e89afeffff call sym.imp.strlen
| 0x004006b6 4889c3 mov rbx, rax
| 0x004006b9 bfe0106000 mov edi, str.this_is_not_even_interesting_its_garbage
| 0x004006be e88dfeffff call sym.imp.strlen
| 0x004006c3 4839c3 cmp rbx, rax
| ,=< 0x004006c6 740a je 0x4006d2
| | 0x004006c8 b8ffffffff mov eax, 0xffffffff ; -1 ; -1
| ,==< 0x004006cd e9dd000000 jmp 0x4007af
...
According to the above condition the length of the string should be 40, otherwise the function returns (-1) to main
.
Function logic
# check_password references bytes at memory location 0x6010a0. Let us assume we load those up in an array 'arr'
# The function modifies the string 'this_is_not_even_interesting_its_garbage' in place
str = 'this_is_not_even_interesting_its_garbage'
var = 1
while(var)
var = 0
for i in 0...40
if arr[i]!=0
res = rand() % arr[i] + 1
arr[i] = arr[i] - res
var |= res
str[i] = chr(ord(str[i]) - res)
end
end
end
After the function executes, the resulting string is then compared to this_aint_that_simple_but_good_luck_haha
.
So our motive is to pass it a string such that we get the resulting string. We can execute the above program and note down results since the seed to rand() is constant.
On doing so, we get the key:
well_done_now_go_on_irc_and_ask_for_more
Challenge 6:
This challenge is literally gg.
Again, on analysing the main function, we can see that we want check_password
to return 0.
Checking if length of the string matches 21
If the input string length is not the same, it immediately returns (-1) to main
.
Function logic
The disassembly looks very terrifying but it actually breaks down to the following:
# Let's say that we have two arrays key1 and key2 consisting of bytes and characters respectively.
# Let str be the input string
# Also let us assume a variable result = 0 in the beginning. This result value is returned back to main
result = 0
for i in 0...21
var = 0
var ^= key1[i] # ; key1 is a set of bytes at memory location 0x601050
var ^= key2[i] # ; key2 is the string "this_might_be_related"
var ^= str[i]
result += var
end
return result
So the function iterates over all indexes and adds XOR of all three values to the result. Since we want to return 0 ideally, we would have to make sure that the value var
added in each loop is 0. Hence the ith
value in the input string should be key1[i]^key2[i]
.
Knowing that we can construct the resulting key:
dude_you_killed_it_gg
I wanted to write more comprehensive solutions explaning the assembly breakdown but I guess these are quite explanatory. Challenges 7, 8 and 9 look very tough and I hope to solve them in due course of time.