Stack Buffer Overflow-01 (csaw18_boi)
Last updated
Was this helpful?
Last updated
Was this helpful?
This is a challenge from big collection of Binary exploitation challenges. This one is from CSAW 2018 and its called boi about finding an offset to a target variable and overflowing it with a specific value. You can get the binary . I plan to solve all the Nightmare challenges using radare2, because I don't see many articles talking about the power of Radare2. Without further ado, let's get started!!
We start with decompiling the binary using ghidra and looking at themain
function. To see the decompiled code:
Open Ghidra and create a new project by going to File> New Project
Import the file in Ghidra by going File> Import File
Double click on the file imported (this is shown in the Active Project pane inside Ghidra)
Click ok
a couple of times and your binary should be imported in the current project
Double Click the binary and Ghidra will ask if you want it to analyze. Click yes> Analyze
Give ghidra some time (depending on your machine) to analyze the binary
Now over on the left side of ghidra window, You can list all the functions under Symbol Tree> Functions
Under Functions
click on main
and you should be presented with the disassembly of the binary in the middle pane and on the right should be the decompiled version of the binary
We can see just after the puts instruction (line 18), there is a read()
function (line 19) which is reading 0x18
bytes from stdin (0)
storing the output in local_38
Then there's an if statement that checks the input to a constant value
Enough of static code analysis, lets debug the binary in radare2.
We open the binary with radare2 in debug mode with the following command
Before we start debugging in radare2 we need to do a quick setup.
We will be using a file to provide the stdin to radare2 and we will be redirecting the stdout to a different terminal (different from the one where radare2 is running). This makes it very easy to change the stdin given to radare2 and it does not clobbers our radare2 terminal screen.
Before proceeding, it is recommended to use tmux or any terminal multiplexer
We will be creating a rarun2 profile with the following contents
Line2: Enter the name of the file you want to use as input
Line3: Make a new pane (in tmux) and run
tty
comand to get the id of the terminal. This is where the output will be redirected when we run the binary in radare2
and save it as tty.rr2 (The name really doesn't matter)
(the name really doesn't matters)
Now we can keep changing the contents of stdin.txt with the command (on another pane)
This is how my terminal looks like after making all the splits:
So we have 4 panes with the following purposes:
Pane 1:This is where we will work with radare2
Pane 2: We will be getting the stdout here
Pane3: We will use this to modify the contents of input file (stdin.txt)
Pane4: To look at the contents of stdin.txt all the time
Before proceeding, there's one more thing we need to take care of, when the std output comes on pane 2, zsh will try to execute them, so instead what we can do is, halt the execution of any commands on that terminal using sleep 999
. Enter this command in pane3 and everything should be setup for us now.
To import the rarun2 config file with radare2 run the following command:
Now we are presented with gdb commandline interface.
We start by analyzing the binary with aaa
And then we can list the available function from the symbol tree with afl
Let's disassemble the main function with pdf @ main
or we can seek (move) to the main function with s main
and then do a simple pdf
which prints the disassembly of the function from the current position in the binary.
You can see your current position inside the memory at the start of the radare2 prompt (marked in red in the above image)
We see a read instruction at the address 0x004006a0
, this is where binary asks for an input.
Let's proceed by making a breakpoint at the read instruction0x004006a0
Now we can execute the binary with dc
(debugger continue) and we are presented with the output in r2 window saying hit breakpoint at: 0x4006a0
Note, sometimes binary might not execute with dc, just run dc command again
Now comes the time to show the true power of radare2, we will be going into "Visual Mode"
To enter into visual mode
and you should be presented with the following screen
Get yourself acquainted with this window, we will be spending a lot of time here. The most important parts here are
Disassembly View: This is the Disassembly view of the binary and you can see many comments starting with ;
, you can even add in your own comments on the current instruction with ;
and enter in your comment and press enter, this will add the comment on the first instruction that you see in the disasembly view.
Stack View: This is the "live" view of the memory used by the binary, this starts at the top of the stack by default, but you can scroll with the navigation keys
Register View: You can see the value of each register here, the blue highlight means the register was changed in the last instruction
This is where current RIP points to
These are all the local variables defined in the current function
A few tips for working in the visual mode more productive:
You can scroll up and down with arrow keys or use vim keys
h
j
k
andl
To cycle between panes, use the tab key and you should see the blue outline switching to different panes
The left column is the hex address in the virtual memory
The middle 16 columns are the hex values stored in the corresponding memory location
The last column is the ASCII representation of the hex data
You can cycle through the color scheme with
shift + c
, I prefer the third (pressshift + c
twice) which even shows you rsp and rbp in the stack viewRemeber you can edit the command being ran in any pane with
e
and then enter the command likedrr
, this gives you much more freedom over the visual modeYou can toggle into
Window mode
withw
and then you can resize the panes withshift + arrow/vim keys
Now, let's start the debugging process. At this point the program is halted because of our breakpoint. We need to step the binary to the next instruction with shift + s
(this does not follows the function, s
follows function calls). As soon as we hit shift + s
program reads the input from stdin.txt and the input is placed onto the stack.
You can even see 0xdeadbeef
stored onto the memory in little-endian 0xefbeadde
Let's keep stepping with shift + s
until we hit the compare statement at 0x004006a8
Here you can see the register eax
is being compared with the constant 0xcaf3baee
, so lets see what's stored in the register rax/eax
As you can see rax holds 0xdeadbeef
and since 0xdeadbeef
is not equal to 0xcaf3baee
This compares instruction will be false and the program ends.
At this point, it should be clear that we want to overwrite the 0xdeadbeef
with 0xcaf3baee
. We have at least two ways to go about it. First includes manually finding the difference between the start of our input and the start of the string 0xdeadbeef
, but this is not at all elegant and will be very time consuming in upcoming challenges. So let's semi-automate this task.
We will be using Metasploit's pattern_create.rb
and pattern_offset.rb
script to generate a cyclic pattern and then find the offset.
using pattern_create.rb
, create a cyclic pattern of length say 100:
Copy the output from the above command into stdin.txt. You can use echo
command to do so
Now we need to rerun the binary inside radare2, we can do this first by going into command mode from visual mode by pressing :
and then we can enter the following:
And we don't have to place the breakpoints again. After running these commands you can switch back to visual mode from command mode by simply pressing Enter
key
Now let's step in again until we hit the compare instruction again
If you look at the rax
register, this time it is filled with a different value. This is the input from our cyclic pattern.
Copy the value that rax
holds, in this case 0x37614136
and run the following command
We should get an exact match at an offset of 20. This means we need to have 20 characters in our input before we can start the overflow.
After knowing the offset we can craft a simple exploit as follows
we already know the offset which is 20, and the overflow value is 0xcaf3baee
, before we can exploit we need to do two things:
We need to convert 0xcaf3baee
into little-endian
As soon as the python command finishes executing there is nothing in the stdin buffer, so we won't get a shell back and the program finishes executing without any error
After getting the above two tasks, this is how our final exploit should look like
We use cat
command to get in our stdin and pass it out to shell
In my opinion, radare2 is very powerful and its ability to look at the memory and registers dynamically makes it so much powerful as a debugger.
This was my first walkthrough on Nightmare's challenges, so any feedback and questions are welcome, You can find me on Twitter at @smash8tap
.
PS: I would like to Thank Lorenzo_apd for proofreading the write-up and making it better.