Home Buffer Overflows
Post
Cancel

Buffer Overflows

Buffer Overflows

What is a Buffer Overflow

A buffer overflow attack is one that exploits the location of a variable in memory to overwrite data. Some deprecated C functions are vulnerable to overflow attacks because they do not enforce sizes. In this writeup, I will demonstrate this vulnerability with the gets function from the stdio header.

man gets gives the following description:

1
2
3
4
5
6
 DESCRIPTION
    Never use this function.
  
    gets()  reads  a  line from stdin into the buffer pointed to by s until either
    a terminating newline or EOF, which it replaces with a null byte ('\0').
    No check for buffer overrun is performed (see BUGS below).

The last line describes the critical error we can exploit. Because there is no size check (unlike fgets), we can read in as many bytes as we want from a file as long as there is no newline character [0x0d].

The Stack

In the C memory model, the stack is the section of memory that stores local variables, preserves the value of certain registers, and contains the return address. This can be seen when looking at a disassembled binary (with objdump -d).

Note that the stack grows downard, so subtraction and negative offsets are growing the stack

1213:	55                   	push   %rbp
1214:	48 89 e5             	mov    %rsp,%rbp
1217:	48 83 ec 10          	sub    $0x10,%rsp
121b:	89 7d fc             	mov    %edi,-0x4(%rbp)
...
1270:	c9                   	leave  
1271:	c3                   	ret    

In the above example, the address of the previous stack frame (%rbp) is pushed onto the stack. Afterwards, the current address of the stack from (in %rsp) is copied into %rbp. Next, the stack pointer %rsp is subtracted by 0x10 to make space for local variables. The register %edi stores the first argument to the function, so it gets copied to 4 bytes from the address of the stack base -0x4(%rbp).

This probably means that the variable that was stored in %edii was a 4-byte variable.

Finally, the leave instruction copies the base pointer %rbp into the stack pointer %rsp and pops the return address that was pushed onto the stack into %rbp, while ret pops the return address to the instruction pointer register %rip. This allows the program to continue where it left off.

GDB

Using GDB, we can examine the value of items in the stack. I have attached a binary of a program to demonstrate this.

You can download the source code and the makefile from the same directory and build by running make clean and then make.

First, we want to start a gdb session for the file with gdb program For this example, try only using the binary (i.e., don’t use layout src or look at the source code).

You can use layout asm to see the assembly, which you would have access to through objdump -d

Now let’s set a breakpoint where gets is called (b *main+41). We can run the program with r and hit the breakpoint. Looking at the next instructions, we can see that a local variable is being compared with a constant.

0x555555555200 <main+87>        cmpl   $0xdeadbeef,-0x14(%rbp)
0x555555555207 <main+94>        je     0x555555555224 <main+123>

This is followed by a je instruction, meaning that we are checking if the variable that is 20 (0x14) bytes from %rbp is equal to $0xdeadbeef, then going to a certain code block. Let’s examine the value of the local variable!

(gdb) x $rbp-0x14
0x7fffffffdebc: 0xffffffff

We can clearly see that the value of the variable is not equal to 0xdeadbeef, so we can conclude that the default behaviour is to not jump to main+82.

Sidenote: hex2raw

To make it easy to enter raw values, you can redirect a file into the provided hex2raw utility. If it does not work for you, you can find an online hex2raw converter and copy the outputs into a file, then redirect it into the program.

Use ./hex2raw < input.txt | ./program to pipe the output of hex2raw into the program.

This post is licensed under CC BY 4.0 by the author.