Solving wapiflapi's reversing challenges

I 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
  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)

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:


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

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:


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.