Exploiting. Part 1: Applications
We discuss exploiting concents and then focus on application exploiting (i.e. runtime/binary application exploiting).
- Slides for this session:
Cheatsheet
Before you start, enable support for x32 binaries with:
# allow the execution of x32 binaries on x64 Linux
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386
# also allow the compilation of x32 binaries on x64 Linux
sudo apt-get install gcc-multilib
To disassemble a binary, use:
objdump -M intel -d <name-of-the-binary> | less
To disassemble a raw shellcode, use:
objdump -D -b binary -m i386 -M intel <file-containing-raw-shellcode>
Compute a payload, write it to a file and then send the payload to a binary:
python3 -c 'import sys; sys.stdout.buffer.write(b"<your-payload>")' > /tmp/payload
cat /tmp/payload - | ./<vulnerable-binary>
Same outcome, without writing the payload to the file:
cat <((python3 -c 'import sys; sys.stdout.buffer.write(b"<your-payload>")')) - | ./<vulnerable-binary>
To send the payload to a remote server, use:
cat <((python3 -c 'import sys; sys.stdout.buffer.write(b"<your-payload>")')) - | nc <IP> <PORT>
Caution: Use sys.stdout.buffer.write() instead of print() when working with raw bytes in Python3.
For details, see this stackoverflow question.
Lab Key Takeaways
x32 Linux system call convention: place the system call number in
eax, then its arguments, in order, inebx,ecx,edx,esi,edi, andebp, then invokeint 0x80.On x32 Linux, function call parameters are pushed on the stack from the rightmost to the leftmost as they appear in C code.
On x32 Linux, system call parameters are copied into registers (they are not pushed into registers).
Based on points 2 and 3 above: on x32 Linux function calling convention is different than system call calling convention.
x64 Linux system call convention: place the system call number in
rax, then its arguments, in order, inrdi,rsi,rdx,r10,r8, andr9, then invokesyscall.On x64 Linux, the x64 Linux function call convention states that parameters are pushed into
rdi,rsi,rdx,rcx,r8,r9respectively.Based on points 5 and 6 above: in terms of parameter order on x64, the only difference between function calling and syscall calling is the overlap between
r10andrcx. See the question about r10 and rcx registers.A list of both x32 and x64 syscalls is Linux system call convention.
Tasks
Download the session task archive.
Open the session task archive and access the
buffer-overflow/subfolder. Exploit thevulnexecutable to jump to thewarcraft(),diablo()and thenstarcraft()functions, one at a time. When callingstarcraft()you are only interested in printing the message via theputs()call.The steps are:
Use
nmorobjdumpto find the addresses ofwarcraft(),diablo()and theputs()call inside ofstarcraft().Identify the location and the length of the buffer overflow.
Craft payloads to exploit the buffer overflow.
Send each payload to the binary.
Open the session task archive and access the
shellcode/subfolder. Thehello-shellcode/vulnbinary is compiled fromhello-shellcode/vuln.cusingmake. Thegen-hello-shellcode/subdirectory contains boilerplate code to help you easily compile shellcodes.See the assembly source code in
gen-hello-shellcode/shellcode.S, and then usemaketo assemble it as a raw file calledshellcode.bin. You can disassemble the raw file by usingobjdump -D -b binary -m i386 -M intel shellcode.binand see that it matchesshellcode.S. You can list the contents ofshellcode.binin hexadecimal by usingmake print.Your goal is to write shellcodes in
asm, assemble the shellcodes and send the shellcodes to thevulnbinary.At each subchallenge, perform the following:
Craft your shellcode inside the right asm source file.
Compile and print your shellcode.
Send the raw shellcode to the
vulnbinary to exploit it.
The subchallenges are:
The
gen-hello-shellcode/shellcode-mundi.Sfile is a copy ofgen-hello-shellcode/shellcode.S. Update thegen-hello-shellcode/shellcode-mundi.Sshellcode to writeSalut, Mundi!to standard output. Then compile the shellcode withmake print-mundiand send it to thevulnbinary.Now make the
vulnbinary exit properly (without printing Segmentation fault) after running your shellcode. To do so, update thegen-hello-shellcode/print-exit-shellcode.Ssource to also runexit(0)after printingSalut, Mundi!. Then compile the shellcode withmake print-exit-printand send it to thevulnbinary.The system call ID of
exit()is on the Linux system calls page or inside of/usr/include/asm/unistd_32.h. The system call ID is stored in theeaxregister and parameters are stored inebx,ecx,edx,esiandedi.Check information on Linux system calls and Linux system call convention.
Printing is boring. Spawning shells is fun. Create a new shellcode that spawns a shell. To do so, update the
gen-hello-shellcode/execve-shellcode.Sshellcode to spawn a shell by runningexecve("/bin/sh", 0, 0). Then compile the shellcode withmake execve-printand then send it to thevulnbinary.If you are out of ideas for the shellcode, take a look at this one. Check a more complicated one if things are too easy. For the curious, more fun shellcodes are found on shell-storm.org.
After you get a working shellcode, test the same payload on the remote server. Use
ncalongsidepython3to generate and send the payload to the remote server. The address is141.85.224.104:30000.For a better understanding of what the execve shellcode does, go through the article Demystifying the Execve Shellcode (Stack Method).
(BONUS) Create a shellcode that reads a fixed-length message from standard input and then prints it and exits properly. Update the
gen-hello-shellcode/read-print-exit-shellcode.Ssource to read a fixed length string onto the stack and print it to standard output. Then compile the shellcode withmake read-print-exit-printand send it to thevulnbinary.
Open the session task archive and access the
ret-to-libc/subfolder. Your goal is to create shells by callingsystem(). Do this in three different ways:Call
system("/bin/bash")from functionenroth().Comment out
enroth()and callsystem("sh")using the call tosystem()in functionerathia()and theshstring already in the executable.Also comment out
erathia()and callsystem("sh")by using the address ofsystem()in the standard C library. Disable ASLR by usingecho 0 | sudo tee /proc/sys/kernel/randomize_va_space.
Open the session task archive and access the
integer-overflow/subfolder. Provide the proper input to theintoverexecutable to create a shell. Craft a working payload locally and then submit it to the remote server. The address is141.85.224.104:30001