Contents

BinExploit 0x1 - ROP Chain

Contents

This post will be the first of many (hopefully) binary exploitation posts.

I am writing this so I can refer in the future and maybe someone will learn something from it too.

I will be using the binary from github.


Part 1 - Functions & Static Analysis

Loading the binary into gdb and looking at the functions reveals 4 interesting functions

  • flag
  • vuln
  • win_function1
  • win_function2

info func

Lets look into the 4 functions and what they do.

decompile main

Looking at the decompiled code with ghidra shows shows that main just initialises the programme & calls the vuln function.

decompile vuln

vuln essentially just asks for user input and the programme ends.

decompile flag

From the decompiled code, we can see that in order to solve this challenge, we have to run flag with win1=true, win2=true & flag argument must be as specified.

This means that we need to chain win_function1 -> win_function2 -> flag in order to succeed.

Part 2 - Buffer Overflow in vuln

Loading the binary into gdb and looking at the assembly reveals a gets to receive user input.

disas vuln

The problem with gets is that it does not check the length of user input, thus resulting in potential buffer overflow and redirection of code flow.

We can modify the EIP by finding the point of overflow and change it to any address we want.

For this case, we want it to go to the win_function1 first.

We can do that by sending a padding till the point of overflow, then follow up by 0x080485e6 to redirect the code execution to win_function1.

By sending in 32 character, we can see that we overflowed the return address on the stack which is then used by the EIP.

buffer1

As per the picture, we can see that we can control the EIP with the last 4 bytes of our 32 character input.

With that, we can redirect the code flow to the win_function1

eip-win1

As we can see now, the programme crashes as the return address is not set yet.

So how can we set the return address?

In order to do that, we must first understand how functions are called in assembly.

Before a function is called, the parameters for the function are pushed onto the stack. The function is then called.

When the call instruction is executed, the address of the (EIP + 1) is pushed onto the stack as a return address.

1
2
3
4
5
6
argument N <-- EBP
...
...
argument 2
argument 1
return address <-- ESP

After that, EBP is pushed and EBP is set to the ESP and the ESP moves downwards to create space for local variables within the function.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
argument N
...
...
argument 2
argument 1
return address 
previous ebp address <-- EBP
local var space 
local var space
local var space
local var space <-- ESP

When the function ends or when it exits, the ESP is shifted back up by how many address space it allocated, effectively destroying the local variable space. The EBP is then then restored with a pop instruction.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
argument N
...
...
argument 2
argument 1
return address 
previous ebp address <-- ESP <-- EBP
destroyed
destroyed
destroyed
destroyed
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
argument N <-- EBP
...
...
argument 2
argument 1 <-- ESP
destroyed
destroyed
destroyed
destroyed
destroyed

From the visualization above, we see that we can by padding the local var space, we can eventually overwrite the return address for vuln, achieving code redirection.

We can then overwrite the return address to redirect the code flow to win_function1

Part 3 - Chaining Redirection to win_function2

Now that we know how a function call works, let’s figure out why the programme crashes when it exits win_function1

Let’s set a breakpoint right before the EBP is pop-ed and take a look at the stack.

bp-win1

From the picture, we can see that when EBP is pop-ed, EBP will be set to 0x41414141 and the ESP will move up.

bp2-win1

From the picture, we can see that when win_function1 reaches the ret instruction, 0x00000000 is at the top of the stack. This means that EIP will restore that address to continue the programme flow. 0x000000 is not a valid location for instructions and the programme crashes.

We now need to add on to our payload to see if we can overflow this return address to go to win_function2.

We can simply pad the end with “B” to see how much we need to pad in order to reach this return address.

return-win1

We see that by adding on to our payload, we have already overwritten the return address. We can now change the “B” to 0x80485fd, the adress of win_function2.

Part 4 - Setting Arguments for win_function2 and Chaining to flag function

Let’s take a look at what win_function2 does before we move on.

disas-win2

Looking at the disassembly of win_function2, we can see that there is a

1
cmp DWORD PTR [ebp+0x8],0xbaaccaad

We can see that win_function2 requires the argument to match 0xbaaccaad. We can deduce that as function arguments are normally referenced as [ebp+0xN] in assembly.

We can set a breakpoint right before the compare to see where the argument is stored.

bp-win2

Currently, the argument passed is 0xffffca1c. Since our “A” padding is only 8 bytes away from the argument, we can overflow it to set it to 0xbaaccaad.

bp2-win2

We have successfully overwritten the argument and we can continue to chain to the flag function.

By setting a breakpoint before the return instruction, we can see where the return address is stored and attempt to overwrite it.

bp3-win2

At the top of the stack is our padding of “C” before we set the argument for win_function2. By changing the “C” to the address of flag, we can redirect the code execution there.

Part 5 - Setting Arguments for flag function & Getting Flag

disas-flag

Looking at the disassembly of the flag function, we can see that we need to set the function argument to 0xdeadbeef.

Let’s set a breakpoint before the compare and see where the argument is in the stack.

bp-flag

We can see that the current argument is 0x00000000. We can set it by continuing to pad the payload and overwrite the argument.

bp2-flag

We have now overwritten the argument. Let’s see what happens now.

solved

The Challenge has been solved!!


I hope that this article has made the understanding of the ROP Chaining attack easier as it was really a struggle for me to understand this and I hope that you have benefited from my suffering :)

Again, if anything was wrong in this article, please contact me so I can change and learn from it. (^o^)

See you in the next BinExploit!