Exploiting. Part 1: Applications ================================ We discuss exploiting concents and then focus on application exploiting (i.e. runtime/binary application exploiting). Slides for this session: * handout_ * handout_4on1_notes_ * handout_8on1_ .. _handout: http://elf.cs.pub.ro/sis/res/03-exploit-1-handout.pdf .. _handout_4on1_notes: http://elf.cs.pub.ro/sis/res/03-exploit-1-handout-4on1-notes.pdf .. _handout_8on1: http://elf.cs.pub.ro/sis/res/03-exploit-1-handout-8on1.pdf 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 | less To disassemble a raw shellcode, use:: objdump -D -b binary -m i386 -M intel 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"")' > /tmp/payload cat /tmp/payload - | ./ Same outcome, without writing the payload to the file:: cat <((python3 -c 'import sys; sys.stdout.buffer.write(b"")')) - | ./ To send the payload to a remote server, use:: cat <((python3 -c 'import sys; sys.stdout.buffer.write(b"")')) - | nc Caution: Use ``sys.stdout.buffer.write()`` instead of ``print()`` when working with **raw** bytes in ``Python3``. For details, see `this stackoverflow question`_. .. _`this stackoverflow question`: https://stackoverflow.com/questions/28429062/python3-adds-extra-byte-when-printing-hex-values Lab Key Takeaways ----------------- 1. x32 `Linux system call convention`_: place the system call number in ``eax``, then its arguments, in order, in ``ebx``, ``ecx``, ``edx``, ``esi``, ``edi``, and ``ebp``, then invoke ``int 0x80``. 2. On x32 Linux, function call parameters are **pushed on the stack** from the rightmost to the leftmost as they appear in C code. 3. On x32 Linux, system call parameters are copied into registers (they are not *pushed* into registers). 4. Based on points 2 and 3 above: on x32 Linux function calling convention is different than system call calling convention. 5. x64 `Linux system call convention`_: place the system call number in ``rax``, then its arguments, in order, in ``rdi``, ``rsi``, ``rdx``, ``r10``, ``r8``, and ``r9``, then invoke ``syscall``. 6. On x64 Linux, the `x64 Linux function call convention`_ states that parameters are pushed into ``rdi``, ``rsi``, ``rdx``, ``rcx``, ``r8``, ``r9`` respectively. 7. 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 ``r10`` and ``rcx``. See the `question about r10 and rcx registers`_. 8. A list of both x32 and x64 syscalls is `Linux system call convention`_. .. _`x64 Linux function call convention`: http://6.s081.scripts.mit.edu/sp18/x86-64-architecture-guide.html .. _`question about r10 and rcx registers`: https://stackoverflow.com/questions/32253144/why-is-rcx-not-used-for-passing-parameters-to-system-calls-being-replaced-with Tasks ----- Download the `session task archive`_. .. _`session task archive`: http://elf.cs.pub.ro/sis/res/03-exploit-1.tar 1. Open the `session task archive`_ and access the ``buffer-overflow/`` subfolder. Exploit the ``vuln`` executable to jump to the ``warcraft()``, ``diablo()`` and then ``starcraft()`` functions, one at a time. When calling ``starcraft()`` you are only interested in printing the message via the ``puts()`` call. The steps are: 1. Use ``nm`` or ``objdump`` to find the addresses of ``warcraft()``, ``diablo()`` and the ``puts()`` call inside of ``starcraft()``. 2. Identify the location and the length of the buffer overflow. 3. Craft payloads to exploit the buffer overflow. 4. Send each payload to the binary. 2. Open the `session task archive`_ and access the ``shellcode/`` subfolder. The ``hello-shellcode/vuln`` binary is compiled from ``hello-shellcode/vuln.c`` using ``make``. The ``gen-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 use ``make`` to assemble it as a raw file called ``shellcode.bin``. You can disassemble the raw file by using ``objdump -D -b binary -m i386 -M intel shellcode.bin`` and see that it matches ``shellcode.S``. You can list the contents of ``shellcode.bin`` in hexadecimal by using ``make print``. Your goal is to write shellcodes in ``asm``, assemble the shellcodes and send the shellcodes to the ``vuln`` binary. At each subchallenge, perform the following: 1. Craft your shellcode inside the right asm source file. 2. Compile and print your shellcode. 3. Send the raw shellcode to the ``vuln`` binary to exploit it. The subchallenges are: a. The ``gen-hello-shellcode/shellcode-mundi.S`` file is a copy of ``gen-hello-shellcode/shellcode.S``. Update the ``gen-hello-shellcode/shellcode-mundi.S`` shellcode to write ``Salut, Mundi!`` to standard output. Then compile the shellcode with ``make print-mundi`` and send it to the ``vuln`` binary. b. Now make the ``vuln`` binary exit properly (without printing *Segmentation fault*) after running your shellcode. To do so, update the ``gen-hello-shellcode/print-exit-shellcode.S`` source to also run ``exit(0)`` after printing ``Salut, Mundi!``. Then compile the shellcode with ``make print-exit-print`` and send it to the ``vuln`` binary. 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 the ``eax`` register and parameters are stored in ``ebx``, ``ecx``, ``edx``, ``esi`` and ``edi``. Check information on `Linux system calls`_ and `Linux system call convention`_. .. _`Linux system calls`: https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md .. _`Linux system call convention`: http://cs.lmu.edu/~ray/notes/syscalls/ c. 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.S`` shellcode to spawn a shell by running ``execve("/bin/sh", 0, 0)``. Then compile the shellcode with ``make execve-print`` and then send it to the ``vuln`` binary. 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 ``nc`` alongside ``python3`` to generate and send the payload to the remote server. The address is ``141.85.224.104:30000``. For a better understanding of what the execve shellcode does, go through the article `Demystifying the Execve Shellcode (Stack Method)`_. .. _`shell-storm.org`: http://shell-storm.org/shellcode/ .. _`this one`: https://shell-storm.org/shellcode/files/shellcode-811.html .. _`more complicated one`: http://shell-storm.org/shellcode/files/shellcode-606.php .. _`Demystifying the Execve Shellcode (Stack Method)`: http://hackoftheday.securitytube.net/2013/04/demystifying-execve-shellcode-stack.html d. (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.S`` source to read a fixed length string onto the stack and print it to standard output. Then compile the shellcode with ``make read-print-exit-print`` and send it to the ``vuln`` binary. 3. Open the `session task archive`_ and access the ``ret-to-libc/`` subfolder. Your goal is to create shells by calling ``system()``. Do this in three different ways: a. Call ``system("/bin/bash")`` from function ``enroth()``. b. Comment out ``enroth()`` and call ``system("sh")`` using the call to ``system()`` in function ``erathia()`` and the ``sh`` string already in the executable. c. Also comment out ``erathia()`` and call ``system("sh")`` by using the address of ``system()`` in the standard C library. Disable ASLR by using ``echo 0 | sudo tee /proc/sys/kernel/randomize_va_space``. 4. Open the `session task archive`_ and access the ``integer-overflow/`` subfolder. Provide the proper input to the ``intover`` executable to create a shell. Craft a working payload locally and then submit it to the remote server. The address is ``141.85.224.104:30001``