Buffer overflow assignment
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