pwnable.kr (bof) Buffer Overflow
Tutorial Notes
What you’ll learn:
- Introduction to arrays, basic buffer overflows
- Introduction to the
gnu
debugger
Directions:
Download the Assignment 2 virtual machine from OWL Brightspace. Refer to the VM setup instructions to install and setup and access the assignment virtual machine on your host device.
Using username bof
and password bof
, use ssh to log into the Assignment 2 virtual machine. Once logged in, use ls -l
to print the directory:
~ ssh bof@<IP address of VM>
bof@<IP address of VM>'s password:
NOTE: This is a *simulation* of a pwnable.kr CTF challenge
____ __ __ ____ ____ ____ _ ___ __ _ ____
| \| |__| || \ / || \ | | / _] | |/ ]| \
| o ) | | || _ || o || o )| | / [_ | ' / | D )
| _/| | | || | || || || |___ | _] | \ | /
| | | ` ' || | || _ || O || || [_ __ | \| \
| | \ / | | || | || || || || || . || . \
|__| \_/\_/ |__|__||__|__||_____||_____||_____||__||__|\_||__|\_|
- Site admin : daehee87.kr@gmail.com
- IRC : irc.netgarage.org:6667 / #pwnable.kr
- Simply type "irssi" command to join IRC now
- files under /tmp can be erased anytime. make your directory under /tmp
- to use peda, issue `source /usr/share/peda/peda.py` in gdb terminal
bof@box:~$ ls -la
total 24
drwxr-x--- 3 root bof 120 Feb 4 17:39 .
drwxr-xr-x 5 root root 100 Feb 4 16:48 ..
d--------- 2 root root 40 Feb 4 16:09 .ash_history
-r-sr-x--- 1 bof_pwn bof 15408 Feb 4 17:39 bof
-rw-r--r-- 1 root root 321 Feb 4 17:39 bof.c
-r--r----- 1 bof_pwn bof_pwn 28 Feb 4 16:14 flag
The directory structure and permissions look the same as the collision challenge. Let’s have a look at the source code in bof.c
:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void func(int key){
int k = key;
char overflowme[32];
printf("overflow me : ");
gets(overflowme); // smash me!
if(k == 0xcafebabe){
system("/bin/cat flag");
}
else{
printf("Nah..\n");
}
}
int main(int argc, char* argv[]){
func(0xdeadbeef);
return 0;
}
We can see the main()
function calls a function func()
with the 32-bit hexidecimal value 0xdeadbeef
. Then func()
accepts up to 32 characters of input from the user. Finally, it compares the input integer key
to the hex value 0xcafebabe
. If they’re equal, it prints the flag, otherwise it exits.
On first glance it seems like we’re stuck. The user can’t control the key
variable, and therefore cannot make the if statement evaluate as True…. or can it? Let’s try to debug the program and find out.
So, as before, our strategy will be to compile the program in the /tmp
directory. This time, we will compile bof.c
using the -g
flag. That will add debugging information to the bof
executable, which will make debugging a little easier:
bof@box:/tmp/mytmp$ gcc -g bof.c -o bof
bof.c: In function 'func':
bof.c:7:2: warning: implicit declaration of function 'gets'; did you mean 'fgets'? [-Wimplicit-function-declaration]
7 | gets(overflowme); // smash me!
| ^~~~
| fgets
/usr/local/bin/ld: /tmp/ccwLlLd4.o: in function `func':
/tmp/mytmp/bof.c:7: warning: the `gets' function is dangerous and should not be used.
Interesting. The compiler is warning us about the gets()
function in func()
, saying its dangerous. First let’s look at what gets
does. From the documentation it says:
Reads characters from the standard input (stdin) and stores them as a C string
into str until a newline character or the end-of-file is reached ... and does
not allow to specify a maximum size for str (which can lead to buffer overflows).
So basically gets
is going to take every character you type and write it to memory in sequential order. But more importantly it’s going to do it in an oblivious (i.e., stupid, dangerous) way: it will just keep writing to memory like a runaway freight train, overwriting anything in its path.
Great. Let’s see how we can exploit this. In order to see what our “freight train” overwrites, we need to be able to look into the memory. For this we’re going to use gdb
, the gnu debugger.
If this is your first time using gdb
, watch this tutorial video to help you get started. Next let’s try opening bof
in gdb
and running it:
bof@box:/home/bof# gdb bof
GNU gdb (GDB) 8.2.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "i486-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from bof...(no debugging symbols found)...done.
(gdb) run
Starting program: /home/bof/bof
overflow me : AAAAAAAAAAAA
Nah..
[Inferior 1 (process 1248) exited normally]
(gdb)
We see the program completed successfully. We can review the program’s source code using the list
command:
(gdb) list
3 #include <stdlib.h>
4 void func(int key){
5 int k = key;
6 char overflowme[32];
7 printf("overflow me : ");
8 gets(overflowme); // smash me!
9 if(k == 0xcafebabe){
10 system("/bin/cat ./flag");
11 }
12 else{
(gdb)
13 printf("Nah..\n");
14 }
15 }
16 int main(int argc, char* argv[]){
17 func(0xdeadbeef);
18 return 0;
19 }
Next let’s put a breakpoint at the if
statement so we can look at what’s going on during the comparison, and run the program again:
(gdb) b 9
Breakpoint 1 at 0x804915d: file bof.c, line 9.
(gdb) run
Starting program: /tmp/mytmp/bof
overflow me : AAAAAAAAAAAA
Breakpoint 1, func (key=-559038737) at bof.c:9
9 if(k == 0xcafebabe){
With the program paused at the breakpoint, let’s look at the contents of the key
variable. We can print the contents in hexidecimal as follows:
(gdb) print/x k
$1 = 0xdeadbeef
And now the contents of the overflowme
char array displayed as hex:
(gdb) print/x overflowme
$2 = {0x41 <repeats 12 times>, 0x0, 0xfc, 0xff, 0xbf, 0xc3, 0x72, 0xeb, 0xb7, 0xfc, 0x33, 0xfd, 0xb7, 0xff, 0xff, 0xff, 0xff, 0x0, 0x80, 0x0, 0x0}
or as characters:
(gdb) print/c overflowme
$3 = {65 'A' <repeats 12 times>, 0 '\000', -4 '\374', -1 '\377', -65 '\277', -61 '\303', 114 'r', -21 '\353', -73 '\267', -4 '\374', 51 '3', -3 '\375', -73 '\267', -1 '\377', -1 '\377', -1 '\377', -1 '\377', 0 '\000', -128 '\200', 0 '\000', 0 '\000'}
This shows our input “AAAAAAAAAAAA” as a string of 12 bytes of value 0x41
, followed by 0x0
terminating the string. The remaining bytes are effectively random.
Ok so let’s try this again, but this time let’s act like a runaway freight train and give the overflowme
variable too much input and see what happens:
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /tmp/mytmp/bof
overflow me : AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ
Breakpoint 1, func (key=1313754702) at bof.c:9
9 if(k == 0xcafebabe){
Let’s print the contents of overflowme
as characters:
(gdb) p/c overflowme
$4 = {65 'A', 65 'A', 65 'A', 65 'A', 66 'B', 66 'B', 66 'B', 66 'B', 67 'C', 67 'C', 67 'C', 67 'C', 68 'D', 68 'D', 68 'D', 68 'D', 69 'E', 69 'E', 69 'E', 69 'E', 70 'F', 70 'F', 70 'F', 70 'F', 71 'G', 71 'G', 71 'G', 71 'G', 72 'H', 72 'H', 72 'H', 72 'H'}
Ok, so the overflowme
array consumed all input up to the “H”’s, meaning the input from “IIII” on to “ZZZZ” overran the boundaries. So where did it go? What did it overwrite?
Well, let’s see if anything happened to the key
variable.
(gdb) p/x k
$5 = 0x49494949
Something happened! Recall when we ran it the first time, key
contained 0xdeadbeef
. Now it contains 0x4d4d4d4d
.
Very interesting. This means we were able to overwrite the contents of k
with our input! We can use this fact to our advantage.
We can write a simple Python program that runs from the command line as a way of generating a mixed string of ASCII and raw hex bytes. We can then send this output into the standard input of the bof
executable as follows:
bof@box:/tmp/mytmp$ python -c "print 'SOMETHING' +'\x01\x02\x03\x04'" | ./bof
overflow me : Nah..
Your challenge is to modify the inputs SOMETHING
and \x01\x02\x03\x04
to capture the flag.