This is an old revision of the document!
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:
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 +++
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
.
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
.
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
.
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
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:
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:
gdb ./vuln
.pattc
.run
.RBP
(we can't do it for RIP
on x86_64).patto
.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 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.
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
.
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.
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
.
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.
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.
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.
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.
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.
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.
setarch
.
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.
argv
parameter to the process()
call in pwntools to pass an argument to the program. Read more here.
setarch
before running the exploit.
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.
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.