107 lines
5.3 KiB
Markdown
107 lines
5.3 KiB
Markdown
# bufoverflow.c demo
|
|
|
|
This demo corresponds to the **Buffer Overflow: Code Execution** slide of the Software Security lecture.
|
|
The function `mystr()` is vulnerable as it has a buffer overflow when calling `fread()` with a wrong maximum lenght: only 512B would fit into the buffer but 1024B are passed as maximum length argument.
|
|
Attackers can therefore overflow the buffer `char mystr[512]` by providing input files larger than 512B.
|
|
|
|
**NOTE:** Before starting here, make sure you have your `.gdbinit` file configured correctly (see Moodle).
|
|
|
|
## Demo usage
|
|
|
|
To compile the demo, use an x64 system with `gcc` and `nasm` installed and invoke `make`. Then:
|
|
|
|
`./bufoverflow <filename>`
|
|
|
|
If the file is too large, the program should receive a segmentation fault during execution.
|
|
Otherwise, the program just exits gracefully.
|
|
|
|
## Exploiting the program WITHIN a debugger
|
|
|
|
Once we got the program running, we have to write the shellcode.
|
|
We face two challenges here.
|
|
1. We have to investigate how many bytes of shellcode we can place and what offset the saved `rip` has.
|
|
2. We have to compute the stack pointer to our shellcode that overwrites the saved `rip`.
|
|
We devote each a subsection for each challenge.
|
|
|
|
### Determinining the offset
|
|
|
|
### Computing the correct stack pointer
|
|
|
|
To compute the stack address, simply use `gdb`. To this end, we open the program in a debuger:
|
|
|
|
`gdb --args ./bufoverflow <path-to-input-file>`
|
|
|
|
Within `gdb`, we then set a breakpoint on the `mystr` function like this:
|
|
|
|
`break mystr`
|
|
|
|
You can then execute the program using the gdb command `run`. The program will execute until hitting the breakpoint. If you don't see registers or assembly you, simply enter `layout asm` and `layout regs`. You should then see the program before executing the `mystr` function prologue. Now, you can single-step using the `gdb` command `ni`. Single-step until after the function prologue (i.e., right after`sub rsp,0x200`). You then see the stack frame looking at `rbp` and `rsp`, where `rsp` points to the top of the stack and thus the single local function variable (the 512B buffer) in our function. Note down this value (in my case, `0x7fffffffe090`) and exit the debugger with SIGINT (i.e., CTRL+D) or using `exit` (noone does that). Update the last line of the shellcode accordingly (the `dq` contains the stack pointer).
|
|
|
|
### Compiling the shellcode
|
|
|
|
After having adapted the shellcode, compile the shellcode using `make`.
|
|
To double-check if the shellcode was compiled correctly, you can disassemble it using the command
|
|
|
|
`objdump -D -b binary -M intel -d -m i386:x86-64 ./shellcode | less -S`
|
|
|
|
It should start with a (long) `nop` chain, followed by the system call, the shell string (misinterpreted as code) and the shellcode pointer (likewise misinterpreted as code).
|
|
|
|
Once you're satisfied, invoke
|
|
|
|
`gdb --args ./bufoverflow ./shellcode`
|
|
|
|
and then use the `run` command to get your shell.
|
|
|
|
Exit with SIGINT (CTRL+D); the first SIGINT will exit the shell, the second the debugger.
|
|
|
|
|
|
## Shellcode for exploits OUTSIDE OF the debugger
|
|
|
|
When you now try to exploit the program *without* using `gdb`, you will likely observe a segmentation fault like this:
|
|
|
|
```
|
|
$ ./bufoverflow ./shellcode
|
|
Segmentation fault
|
|
```
|
|
|
|
This has **two** reasons, both of which you'll have to address.
|
|
|
|
### Stack randomization
|
|
|
|
First, by default, Linux randomizes the stack location to make attacks like yours harder.
|
|
In `gdb` your attack worked as `gdb` disables this randomization to make debugging easier.
|
|
You can disable this randomization by executing the program like this:
|
|
|
|
`setarch \`uname -m\` -R ./bufoverflow ./shellcode`.
|
|
|
|
### Adjusting the stack pointer
|
|
|
|
Second, the stack pointer you obtained using `gdb` is invalid when the program is being run without `gdb`.
|
|
This is as `gdb` places environment variables on the stack that change (decrease!) the stack (i.e., `rsp` value).
|
|
There are (at least) two solutions to this problem:
|
|
|
|
#### Variant A: Guesstimate the right stack address
|
|
|
|
We can take into account that `gdb` places environment variables that change (decrease!) the `rsp` value (see [here](https://stackoverflow.com/questions/17775186/buffer-overflow-works-in-gdb-but-not-without-it) for details and possible workarounds that however only work for programs without arguments).
|
|
Thankfully, given that we have sufficient room for a `nop` sled in our sled, we can adopt to this and try to guesstimate a valid `rsp` value.
|
|
Try to increase the value by 256B (0x100).
|
|
This address should now point somewhere in the middle of our shellcode, i.e., into our NOP sled.
|
|
Then recompile the shellcode and give it a try.
|
|
|
|
#### Variant B: Inspecting a core dump.
|
|
|
|
If you're really after the exact correct `rsp` value, you can also record a so-called `core` dump of the vulnerable program upon crash, and then inspect the core dump using `gdb`.
|
|
To enable core dumps, run:
|
|
|
|
`ulimit -S -c unlimited`
|
|
|
|
Then, run the program and let it crash.
|
|
|
|
`setarch `uname -m` -R ./bufoverflow ./shellcode`
|
|
|
|
Now you should see a file called `core`, which is our core dump. We can inspect this using `gdb`:
|
|
|
|
`gdb ./bufoverflow ./core`
|
|
|
|
By inspecting the registers or memory content, you may be able to find out the correct `rsp` value. Note however that the core dump was taken *after* the function's epilogue and hence the `rbp` and `rsp` values will be clobbered. So it's always a little nasty to find the exact value and there's no systematic answer how to find it.
|