Skip to main content

Day One - Buffer Overflow Introduction

The Follow YouTube provides a brief introduction on what a buffer overflow actually is, before we begin to look at them technically. #

Buffer Overflow YouTube Video

The Assembly Instruction Cycle #

InstructionDescription
1. FETCHThe next machine instruction address is read from theInstruction Address Register(IAR). It is then loaded from theCacheorRAMinto theInstruction Register(IR).
2. DECODEThe instruction decoder converts the instructions and starts the necessary circuits to execute the instruction.
3. FETCH OPERANDSIf further data have to be loaded for execution, these are loaded from the cache orRAMinto the working registers.
4. EXECUTEThe instruction is executed. This can be, for example, operations in theALU, a jump in the program, the writing back of results into the working registers, or the control of peripheral devices. Depending on the result of some instructions, the status register is set, which can be evaluated by subsequent instructions.
5. UPDATE INSTRUCTION POINTERIf no jump instruction has been executed in the EXECUTE phase, theIARis now increased by the length of the instruction so that it points to the next machine instruction.

The Program’s Memory Layout #

img.png

.text #

The.textsection contains the actual assembler instructions of the program. This area can be read-only to prevent the process from accidentally modifying its instructions. Any attempt to write to this area will inevitably result in a segmentation fault.

.data #

The.datasection contains global and static variables that are explicitly initialized by the program.

.bss #

Several compilers and linkers use the.bsssection as part of the data segment, which contains statically allocated variables represented exclusively by 0 bits.

The Heap #

Heap memoryis allocated from this area. This area starts at the end of the “.bss” segment and grows to the higher memory addresses.

The Stack #

Stack memoryis aLast-In-First-Outdata structure in which the return addresses, parameters, and, depending on the compiler options, frame pointers are stored.C/C++local variables are stored here, and you can even copy code to the stack. TheStackis a defined area inRAM. The linker reserves this area and usually places the stack in RAM’s lower area above the global and static variables. The contents are accessed via thestack pointer, set to the upper end of the stack during initialization. During execution, the allocated part of the stack grows down to the lower memory addresses.

Modern memory protections (DEP/ASLR) would prevent the damage caused by buffer overflows. DEP (Data Execution Prevention), marked regions of memory “Read-Only”. The read-only memory region is where some user-input is stored (Example: The Stack), so the idea behind DEP was to prevent users from uploading shellcode to memory and then setting the instruction pointer to the shellcode. Hackers started utilizing ROP (Return Oriented Programming) to get around this, as it allowed them to upload the shellcode to an executable space and use existing calls to execute it. With ROP, the attacker needs to know the memory addresses where things are stored, so the defense against it was to implement ASLR (Address Space Layout Randomization) which randomizes where everything is stored making ROP more difficult.

Users can get around ASLR by leaking memory addresses, but this makes exploits less reliable and sometimes impossible. For example the“Freefloat FTP Server”is trivial to exploit on Windows XP (before DEP/ASLR). However, if the application is ran on a modern Windows operating system, the buffer overflow exists but it is currently non-trivial to exploit due to DEP/ASLR (as there’s no known way to leak memory addresses.)

CPU Registers #

Registers are the essential components of a CPU. Almost all registers offer a small amount of storage space where data can be temporarily stored. However, some of them have a particular function.

These registers will be divided into General registers, Control registers, and Segment registers. The most critical registers we need are the General registers. In these, there are further subdivisions into Data registers, Pointer registers, and Index registers.

Data registers #

32-bit Register64-bit RegisterDescription
EAXRAXAccumulator is used in input/output and for arithmetic operations
EBXRBXBase is used in indexed addressing
ECXRCXCounter is used to rotate instructions and count loops
EDXRDXData is used for I/O and in arithmetic operations for multiply and divide operations involving large values

Pointer registers #

32-bit Register64-bit RegisterDescription
EIPRIPInstruction Pointer stores the offset address of the next instruction to be executed
ESPRSPStack Pointer points to the top of the stack
EBPRBPBase Pointer is also known asStack Base PointerorFrame Pointerthats points to the base of the stack

The vulnerable program #

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int bowfunc(char *string) {

	char buffer[1024];
	strcpy(buffer, string);
	printf("Buffer: %s\n", buffer);
	return 1;
}

int main(int argc, char *argv[]) {
	
	printf("Input: %s\n",argv[1]);
	bowfunc(argv[1]);
	printf("Done.\n");
	return 1;
}

Disabling ASLR #

Address Space Layout Randomization (ASLR) is a modern protection that randomizes the memory addresses used within program to prevent buffer overflow vulnerabilities. For our purposes, we’ll disable this.

In a real-life scenario, you’d likely need to write code in your exploit to map out memory of the system and attack the specific memory addresses that you are looking for.

student@lab-machine:~$ sudo su
root@lab-machine:/home/student# echo 0 > /proc/sys/kernel/randomize_va_space
root@lab-machine:/home/student# cat /proc/sys/kernel/randomize_va_space

img.png

Vulnerable Binary Compilation #

We can now compile our executable into a 32-bit ELF Binary.

In order to compile for 32-bit linux, we first need to install gcc-multilib, which provides the ability to compile for 32-bit and 64-bit.

Within the compilation command, we’re also disabling several additional protections.

sudo apt update
sudo apt install gcc-multilib -y

gcc bow.c -o bow32 -fno-stack-protector -z execstack -m32

Vulnerable Functions to Avoid #

  • strcpy(dest, src) - Copies a string (src) into another (dest), including the null terminator ‘\0’.
char src[] = "Hello";
char dest[10];
strcpy(dest, src); // dest now contains "Hello"
  • gets(buffer) - Reads a line from standard input (stdin) into buffer until a newline or EOF, then appends ‘\0’.
char name[20];
gets(name);
printf("Hello %s\n", name);
  • sprintf(buffer, format, ...) - Writes formatted output (like printf) into a string buffer instead of printing to the screen.
char message[50];
int age = 25;
sprintf(message, "I am %d years old", age);
// message contains "I am 25 years old"
  • scanf(format, ...) - Reads formatted input from standard input (stdin).
int age;
scanf("%d", &age);
  • strcat(dest, src) - Appends the string src to the end of dest (overwriting the ‘\0’ in dest).
char dest[20] = "Hello, ";
char src[] = "world!";
strcat(dest, src); // dest becomes "Hello, world!"

GNU Debugger #

GDB, or the GNU Debugger, is the standard debugger of Linux systems developed by the GNU Project. It has been ported to many systems and supports the programming languages C, C++, Objective-C, FORTRAN, Java, and many more.

GDB provides us with the usual traceability features like breakpoints or stack trace output and allows us to intervene in the execution of programs. It also allows us, for example, to manipulate the variables of the application or to call functions independently of the normal execution of the program.

GNU Debugger(GDB) is used to view the created binary on the assembler level.

We can run the debugger on our program using the following command:

gdb -q bow32

To view the assembly instructions of the main function, we run:

(gdb) disassemble main

disassembling_main.png

  • The first column is the memory address
  • The second column is the address jump in memory in Bytes, used for the respective instruction. Note that this is based on our current position in the program.
  • The third column is the assembler instructions (mnemonics)
  • The forth column is the register for the instruction and any operation suffixes.

By default, gdb uses AT&T which can be difficult to interpret, so we can switch it to Intel syntax, by exiting and then setting the default format

q 
(gdb) exit

echo 'set disassembly-flavor intel' > ~/.gdbinit

gdb ./bow32 -q
Enable debuginfod for this session? (y or [n]) y

(gdb) disassemble main

disassembling_after_intel_syntax.png

n the intel Syntax, assembly is written destination first, then source

0x0000058d <+11>:	mov    ebp,esp

The destination register is ebp, the source is esp. In this line, 0x0000058d refers to the location in memory and the action is 11bytes into the start of our current function.

Stack Frames #

Since the stack starts with a high address and grows down to low memory addresses as values are added, theBase Pointerpoints to the beginning (base) of the stack in contrast to theStack Pointer, which points to the top of the stack.

As the stack grows, it is logically divided into regions calledStack Frames, which allocate the required memory in the stack for the corresponding function. A stack frame defines a frame of data with the beginning (EBP) and the end (ESP) that is pushed onto the stack when a function is called.

Since the stack memory is built on aLast-In-First-Out(LIFO) data structure, the first step is to store theprevious EBPposition on the stack, which can be restored after the function completes. If we now look at thebowfuncfunction, it looks like following in GDB:

(gdb) disas bowfunc 

Dump of assembler code for function bowfunc:
   0x0000054d <+0>:	    push   ebp       # <---- 1. Stores previous EBP
   0x0000054e <+1>:	    mov    ebp,esp   # <---- 2. Creates new Stack Frame
   0x00000550 <+3>:	    push   ebx
   0x00000551 <+4>:	    sub    esp,0x404 # <---- 3. Moves ESP to the top
   <...SNIP...>
   0x00000580 <+51>:	leave  
   0x00000581 <+52>:	ret   
  • TheEBPin the stack frame is set first when a function is called and contains theEBPof the previous stack frame.
  • Next, the value of theESPis copied to theEBP, creating a new stack frame.
  • Then some space is created in the stack, moving theESPto the top for the operations and variables needed and processed.

Epilogue #

(gdb) disas bowfunc 

Dump of assembler code for function bowfunc:
   0x0000054d <+0>:	    push   ebp       
   0x0000054e <+1>:	    mov    ebp,esp   
   0x00000550 <+3>:	    push   ebx
   0x00000551 <+4>:	    sub    esp,0x404 
   <...SNIP...>
   0x00000580 <+51>:	leave  # <----------------------
   0x00000581 <+52>:	ret    # <--- Leave stack frame

During the epilogue, theESPis replaced by the currentEBP, and its value is reset to the value it had before in the prologue.

Index registers #

Register 32-bitRegister 64-bitDescription
ESIRSISource Index is used as a pointer from a source for string operations
EDIRDIDestination is used as a pointer to a destination for string operations

Another important point concerning the representation of the assembler is the naming of the registers. This depends on the format in which the binary was compiled. We have used GCC to compile thebow.ccode in 32-bit format. Now let’s compile the same code into a64-bitformat.

gcc bow.c -o bow64 -fno-stack-protector -z execstack -m64
gdb -q bow64

(gdb) disassemble main

64-bit-assembly-view.png

You’ll notice when looking at this, that the addresses are twice as large as the 32-bit binary, and there are half as many instructions as with the 32-bit binary.

When looking for buffer overflow possibilities, the instructions we are most focused on are the call instructions as these are used to execute functions by performing two operations:

  • it pushes the return address onto thestackso that the execution of the program can be continued after the function has successfully fulfilled its goal,
  • it changes theinstruction pointer(EIP) to the call destination and starting execution there.

Endianness #

During load and save operations in registers and memories, the bytes are read in a different order. This byte order is calledendianness. Endianness is distinguished between thelittle-endianformat and thebig-endianformat.

Big-endianandlittle-endianare about the order of valence. Inbig-endian, the digits with the highest valence are initially. Inlittle-endian, the digits with the lowest valence are at the beginning. Mainframe processors use thebig-endianformat, some RISC architectures, minicomputers, and in TCP/IP networks, the byte order is also inbig-endianformat.

Now, let us look at an example with the following values:

  • Address:0xffff0000
  • Word:\xAA\xBB\xCC\xDD
Memory Address0xffff00000xffff00010xffff00020xffff0003
Big-EndianAABBCCDD
Little-EndianDDCCBBAA

This will be very important for us when entering our code in the right order later on when we have to tell the CPU to which address it should point to. This doesn’t matter much for today’s lesson.

Causing a Crash #

How the program is expected to run:

./bow32 "Hello world"

bow_successful_run.png

So how do we exploit this buffer overflow? Simply provide it more characters than it can handle.

./bow32 $(python3 -c "print('\x55' * 1200)")

buffer-overflow-crash.png

Now why did this actually happen? #

Let’s open this binary in gdb again

gdb -q bow32

We’ll then tell the debugger to run it with the same input as before:

(gdb) run $(python3 -c "print('\x55' * 1200)")

buffer_overflow_crash_gdb.png

This confirms that it did crash again (duh), but let’s look at the CPU registers to determine what happened.

(gdb) info registers

registers_post_crash.png

Looking at the data in the current memory registers, you see that the eip register, which is used to store the offset address to the next instruction to executed has been overwritten with uuuuu or hex 0x55.

img.png

This is what ultimately causes the crash because the CPU doesn’t know what instruction to execute next.

In the next lesson, we’ll work on taking over the EIP register in order to insert our own code and achieve code execution.