Table of Contents

0x07. Shellcodes 2 (advanced)

Resources

Activities repo

Shellcode repository

Reminder: Shellcode

A shellcode is a little piece of binary data that is meant to be executed by a process as part of an attack vector. An attacker would usually place a shellcode in the process memory and aim to execute it to trigger an advantageous effect for the attacker.

While a shellcode would typically result in the attacker gaining a shell process by the means the of the execve system call, this needn't always be the case. Some shellcodes may result in writing data to a socket, scanning the memory, opening/creating a file and many others.

A shellcode is typically written in assembly language and then compiled into binary object code and fed to the vulnerable program. There are three actions an attacker must undertake to run a shellcode in a vulnerable program:

  1. Write the shellcode: typically done in assembly and then convert it in binary object code.
  2. Inject the shellcode into the memory address space of the vulnerable process. This is fed through some form of input to the process (standard input, program arguments, sockets, I/O, environment variables etc.).
  3. Trigger the running of the shellcode by jumping to the shellcode address, usually done through a buffer overflow.

1. Tutorial: Shellcode Running

Go to 01-tutorial-shellcode/ in the activities archive.

shellcode.S is a simple shellcode doing an exit(42). You can build it in shellcode.bin and print it by running

$ make print
\xbb\x2a\x00\x00\x00\xb8\x01\x00\x00\x00\xcd\x80

The shellcode is already part of vuln.c, compiled into the vuln executable, in the shellcode global variable. It's run by forcing the shellcode variable and by casting it to a function pointer and calling it:

        void (*func_ptr)(void) = (void (*)(void)) shellcode;
        func_ptr();

This is possible due to making the data section executable when linking the vuln executable with the -zexecstack option, as shown in the Makefile.

You can check it works properly by running it and checking the return code:

$ ./vuln
Nice function at 0x8048510
$ echo $?
42

You can also check that by running the vuln program under strace:

$ strace ./vuln
execve("./vuln", ["./vuln"], [/* 27 vars */]) = 0
strace: [ Process PID=11063 runs in 32 bit mode. ]
[...]
write(1, "Nice function at 0x8048510\n", 27Nice function at 0x8048510
) = 27
exit(42)                                = ?
+++ exited with 42 +++

2. Challenge: exec Shellcode

Go to 02-challenge-shellcode-exec/ in the activities archive.

Find a proper shellcode in the shellcode repository (or generate one under GDB PEDA using the shellcode command) and update the shellcode variable in the vuln.c file, build it into the vuln executable using make and check it works properly. Run it by itself and then run it under strace.

3. Challenge: exec Shellcode (x86_64)

Go to 03-challenge-shellcode-exec-x64/ in the activities archive.

Find a proper shellcode in the shellcode repository for Linux and the x86_64 architecture (or generate one under GDB PEDA using the shellcode command) and update the shellcode variable in the vuln.c file, build it into the vuln executable using make and check it works properly. Run it by itself and then run it under strace.

4. Challenge: Shellcode as Argument

Go to 04-challenge-shellcode-argv/ in the activities archive.

Feed the vuln executable a proper x86_64 shellcode as a program argument. Make sure it works by running it by itself and then run it under strace.

5. Challenge: Shellcode at Standard Input

Go to 05-challenge-shellcode-stdin/ in the activities archive.

Feed the vuln executable a proper x86_64 shellcode as a program at standard input. Use the “keep stdin working” construct such as below:

cat <(python -c 'print "TODO"') - | ./vuln

6. Tutorial: Buffer Overflow

Go to 06-tutorial-buffer-overflow/ in the activities archive.

The vuln.c file shows a pretty clear buffer overflow:

        char input[64];
        fgets(input, 256, stdin);

We want to call the function win(). As shown in session 06: Buffer Management we do the following steps:

  1. Find the address of the win() function.
  2. Determine the offset from the start of the buffer to the place storing the function return address.
  3. Create a payload that overwrites the return address with the address of the win function.

We find the address of the win() function by using nm:

$ nm vuln | grep win
00000000004005b7 t win

We determine the offset from the start of the buffer to the place storing the function return address by using GDB PEDA doing the following steps:

  1. Run the program under GDB with gdb ./vuln.
  2. Create a cyclic pattern using pattc.
  3. Run the program using run.
  4. Feed the cyclic pattern to it (copy-paste).
  5. Extract the substring value from RBP (we can't do it for RIP on x86_64).
  6. Get the offset from the start of the buffer to the saved RBP using patto.
  7. Add 8 to the offset (the size of saved RBP) to determine the offset from the start of the buffer to the place storing the function return address.

We create the payload by using Python:

python -c 'print "A"*88 + "\xb7\x05\x40\x00\x00\x00\x00\x00"'

where:

  1. 88 is the computed offset
  2. \xb7\x05\x40\x00\x00\x00\x00\x00 is the little endian x86_64 bit address of the win() function

We exploit the program by feeding the input to the vuln executable by running

python -c 'print "A"*88 + "\xb7\x05\x40\x00\x00\x00\x00\x00"' | ../src/vuln

The exploit script is in sol/exploit.sh. You can run it:

$ ./exploit.sh
Have a number: 50
Hello! Gimme input: Glad to meet you!
Congrats!
./exploit.sh: line 3: 11652 Done                    python -c 'print "A"*88 + "\xb7\x05\x40\x00\x00\x00\x00\x00"'
     11653 Segmentation fault      | ../src/vuln

The printing of the Congrats! message above means the exploit succeeded and we were able to call the win() function.

7. Challenge: Buffer Overflow

Go to 07-challenge-buffer-overflow/ in the activities archive.

Similarly to the tutorial in 06-tutorial-buffer-overflow/, create a buffer overflow that ends up calling the win() function and printing the Congrats! message. You can use the skeleton exploit script in sol/exploit_template.sh.

8. Challenge: Buffer Overflow

Go to 08-challenge-buffer-overflow/ in the activities archive.

Similarly to the tutorial in 07-tutorial-buffer-overflow/, create a buffer overflow that ends up calling the win() function and printing the Congrats! message. You can use the skeleton exploit script in sol/exploit_template.sh. This time you don't have acces to the source code, only to the executable.

9. Challenge: Buffer Overflow and Shellcode

Go to 09-challenge-buffer-overflow-shellcode/ in the activities archive.

Exploit the buffer overflow in the input variable on the stack and feed it a shellcode in the shellcode global variable and execute the shellcode. You can use the skeleton exploit script in sol/exploit_template.sh.

10. Tutorial: Buffer Overflow in pwntools

Go to 10-tutorial-buffer-overflow-pwntools/ in the activities archive.

We use pwntools (documented here) to make it easier to write exploits.

The sol/exploit.py script exploits the executable from activity 6. Go through it, run it, understand how pwntools works.

Check the documentation and especially the walkthroughs to get an insight into how pwntools works and how you can use it.

11. Challenge: Buffer Overflow in pwntools

Go to the 11-challenge-buffer-overflow-pwntools/ folder in the activities archive.

Create a sol/exploit.py script that exploits the vuln executable from activity 7. Use the sol/exploit.py script from the tutorial above as a starting point.

12. Challenge: Buffer Overflow and No Code in pwntools

Go to the 12-challenge-buffer-overflow-no-code-pwntools/ folder in the activities archive.

Create a sol/exploit.py script that exploits the vuln executable from activity 8. Use the sol/exploit.py script from the tutorial above as a starting point.

13. Tutorial: Buffer Overflow and Shellcode in pwntools

Go to the 13-tutorial-buffer-overflow-shellcode-pwntools/ folder in the activities archive.

This tutorial uses pwntools to craft a shellcode and then feed it to the program while also creating a buffer overflow payload. Go through it, see what it does.

Check the documentation and especially the walkthroughs to get an insight into how pwntools works and how you can use it to craft shellcodes, inject then into a program and exploit the program.

14. Challenge: Your Turn

Go to the 14-challenge-your-turn/ folder in the activities archive.

Create a simple C program using a buffer overflow and able to store a shellcode into a global (data) variable. Compile it both for 32 and 64 bits. Then create exploits for them using pwntools. Do something similar to the challenge 9, but vary buffer sizes. Do a pwntools-based exploit for 32 bits and one for 64 bits.

Create your simple C program in src/vuln.c altering the provided skeleton.

The Makefile in src/ builds the vuln.c file two executables vuln32 and vuln64 for 32 and 64 bits. Create your exploits starting from the sol/exploit32_template.py and sol/exploit64_template.py scripts.

15. Tutorial: Shellcode on Stack

Go to the 15-tutorial-shellcode-on-stack/ folder in the activities archive.

We often use the stack to store the shellcode. That's what we use now.

For that to happen easily we need to disable ASLR using setarch and then check it does work by using ldd and making sure no library address change:

$ setarch x86_64 -R /bin/bash

$ ldd vuln
        linux-vdso.so.1 (0x00007ffff7ffb000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffff7c16000)
        /lib64/ld-linux-x86-64.so.2 (0x0000555555554000)
$ ldd vuln
        linux-vdso.so.1 (0x00007ffff7ffb000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffff7c16000)
        /lib64/ld-linux-x86-64.so.2 (0x0000555555554000)

Then we go into GDB and determine the offset and at the same time find the buffer address:

$ gdb ./vuln

Use pattc and patto for that and determine the offset and, during the crash, find the buffer address in RAX.

In the sol/exploit.py script, we fill the approximate_buffer_address variable with the address from GDB and then we run along that. We run the program multiple times, until we find the proper address that is able to get us the shellcode to run.

To reenable ASLR, simply exit the shell you created using setarch.

16. Challenge: io.netgarage.io level05

Go to the 16-challenge-io.netgarage.io-level05/ folder in the activities archive.

It's a buffer overflow that may end up calling a shellcode placed on the stack buffer.

Create a sol/exploit.py script that exploits the vuln executable. Use the sol/exploit.py script from the tutorial above as a starting point.

Use the argv parameter to the process() call in pwntools to pass an argument to the program. Read more here.
Disable ASLR using setarch before running the exploit.

17. Challenge: Shellcode on Stack

Go to the 17-challenge-shellcode-on-stack/ folder in the activities archive.

Update the sol/exploit.py script that exploits the vuln executable. It's similar to challenge 15.

18. Challenge: Shellcode on Stack (32 bit)

Go to the 18-challenge-shellcode-on-stack-32/ folder in the activities archive.

It's similar to the challenge above, except that it runs on 32 bits. Copy and update the exploit.py script from the solution above and update it to make it work on 32 bits.