Buffer overflow assignment

profileshreyachand
Lecture-5_slides.pptx

Buffer overflow

INFSYS 3868/6868 Lecture-5

Overview

Vulnerable program

/* stack.c */

/* This program has a buffer overflow vulnerability. */

/* Our task is to exploit this vulnerability*/

#include <stdlib.h>

#include <stdio.h>

#include <string.h>

int bof(char *str){

char buffer[24];

/* The following statement has a buffer overflow problem */

strcpy(buffer, str);

return 1;

}

int main(int argc, char **argv){ char str[517];

FILE *badfile;

badfile = fopen("badfile", "r");

fread(str, sizeof(char), 517, badfile);

bof(str);

printf("Returned Properly\n");

return 1;

}

Call to the function bof with input str

str being size 517 overflows buffer which is size 24

strcpy copies str (source) to buffer (destination)

Exploit

Process

Process is a program/software in execution

In order for a program/software to become a process, it is brought by the operating system (Windows, Linux, Mac, etc.) from the hard drive to the RAM.

Every process is allocated some space on RAM called process memory

Process memory layout

Heap
Stack
Global Data
Program code

Local Data

(local to a function)

Available space on stack
Function f3’s stack frame (f3 called by f2)
Function f2’s stack frame (f2 called by f1)
Function f1’s stack frame

Stack frame layout of a function

Buffer[23]
.
.
Buffer[1]
Buffer[0]
.
.
Caller’s frame address
Return address
Argument
.
.

(top of the stack) esp

address of Buffer[23]

stored in the register esp

(current stack frame) ebp

address of caller’s frame address store in register ebp

Registers are inside the processor

Stack is on the RAM

Attack sequence

The exploit writes the cleverly crafted string containing the shellcode in the badfile

The vulnerable program reads the badfile and the content of the badfile overwrites the buffer

When the vulnerable function (bof) returns, it returns to the address in the cleverly crafted string that overwrote the buffer

If there was no overflow, the program would return to the main function where the call was made

Since the address in the cleverly crafted string points to the shellcode, the shellcode executes, and the attacker gets a shell ($ sign at the prompt)

Stack frame

Before overflow

After overflow

Buffer[0]
.
.
Buffer[22]
Buffer[23]
.
.
Caller’s frame address
Return address
Argument
.
.
str[0]
.
.
str[23]
str[24]
.
.
str[..]
str[..]
str[..]
.
.

Return address has been overwritten; attacker would like to have an address here that points to his code instead

Crafting the overflow string

NOP
NOP
.
.
.
.
NOP
NOP
0xffffbcd8
shellcode
shellcode
.

0xffffbcd8

0xffffbcd4

0xffffbcd0

0xffffbcdc

addresses of stack locations

Notice this is the address of the stack location where shellcode begins

So, when the function returns, it returns to the location where shellcode is and the program starts executing the shellcode

Exploit - NOPs

The following statement in exploit.c creates a string (named buffer) of 517 NOPs. NOP stands for the No Operation instruction. If executed, this instruction does nothing. The machine language code for NOP is \x90.

memset(&buffer, 0x90, 517);

Location of return address

How can the attacker find the location of return address to overwrite it?

We can use a debugger to find the location of the start of the buffer

The command p &buffer in the debugger gdb prints the location of the start of the string named buffer (remember the one of size 24)

We can use a debugger to find the location of caller’s frame address. It is usually stored in a register called ebp on the processor.

The command p $ebp in the debugger gdb prints the contents of the register ebp

We know that the return address is adjacent to the caller’s frame address (ebp). So if we know how far ebp is from buffer, we can add 4 (memory is byte addressed and addresses are 32 bits long which is 4 bytes) to that distance and determine how far it is from the buffer.

&buffer – $ebp

Location of return address - calculation

Buffer[0]
.
.
Buffer[22]
Buffer[23]
.
.
Caller’s frame address
Return address
Argument
.
.

Addr(Return address) = &buffer + (&buffer - $ebp) + 4

Address of Buffer[0] is written as

&buffer

Address of caller’s frame address is stored in the register ebp

Overwrite return address with Shellcode address

Buffer[0]
.
.
Buffer[22]
Buffer[23]
.
.
Caller’s frame address
Return address
Argument
.
.

Attacker can put the shellcode here (usually NOPs are put here followed by the shellcode)

Address = $ebp

Address = $ebp +4

Address = $ebp +8

Overwrite return address

*((long *) (buffer + 36)) = 0xbfffedc8 + 0x8 + atoi(argv[1]);

Address of return address

&buffer + (&buffer - $ebp) + 4

New address pointing to the address of shellcode

$ebp on my computer

Offset – need to be determined by hit and trial

On my computer, I got 36

Can use a shell script to determine this

Copy shellcode

memcpy(buffer + sizeof(buffer) - sizeof(shellcode), shellcode, sizeof(shellcode));

Copy shellcode all the way at the end of the string.

Making enough space for the shellcode to fit at the end

Overflow string (written to badfile)

NOP
NOP
.
.
.
.
NOP
NOP
0xffffbcd8
NOP
.
NOP
Shellcode
Shellcode
Shellcode

NOPs are executed before the shellcode

Offset

*((long *) (buffer + 36)) = 0xbfffedc8 + 0x8 + atoi(argv[1]);

Why do we need this offset?

Because, sometimes the compiler shifts the contents on stack by a few places

Determining offset

Command-line arguments

argv[0] is the name of the program and argv[1] is the argument. For example: ./exploit 12. Here argv[0] is “exploit” and argv[1] is “12”

atoi (alphanumeric to integer) converts, for example, the string “12” to the integer “12”

Shell script to determine offset

The shell script tries different arguments to determine the offset that would give a shell.

#!/bin/bash

for((i=0;i<=1000;i=i+4)); do

rm badfile

./exploit $i

./stack

done