====== 0x07. Shellcodes 2 (advanced) ======
===== Resources =====
/*[[http://security.cs.pub.ro/summer-school/res/arc/08-shellcodes-advanced-skel.zip|Activities archive]]*/
[[https://github.com/hexcellents/sss-exploit|Activities repo]]
[[http://shell-storm.org/shellcode/|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 [[http://man7.org/linux/man-pages/man2/execve.2.html|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 [[http://security.cs.pub.ro/summer-school/res/arc/08-shellcodes-advanced-skel.zip|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 [[http://security.cs.pub.ro/summer-school/res/arc/08-shellcodes-advanced-skel.zip|activities archive]].
Find a proper shellcode in the [[http://shell-storm.org/shellcode/|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 [[http://security.cs.pub.ro/summer-school/res/arc/08-shellcodes-advanced-skel.zip|activities archive]].
Find a proper shellcode in the [[http://shell-storm.org/shellcode/|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 [[http://security.cs.pub.ro/summer-school/res/arc/08-shellcodes-advanced-skel.zip|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 [[http://security.cs.pub.ro/summer-school/res/arc/08-shellcodes-advanced-skel.zip|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 [[http://security.cs.pub.ro/summer-school/res/arc/08-shellcodes-advanced-skel.zip|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|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 for ''RIP'' 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 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 [[http://security.cs.pub.ro/summer-school/res/arc/08-shellcodes-advanced-skel.zip|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 [[http://security.cs.pub.ro/summer-school/res/arc/08-shellcodes-advanced-skel.zip|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 [[http://security.cs.pub.ro/summer-school/res/arc/08-shellcodes-advanced-skel.zip|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 [[http://security.cs.pub.ro/summer-school/res/arc/08-shellcodes-advanced-skel.zip|activities archive]].
We use [[https://github.com/Gallopsled/pwntools|pwntools]] (documented [[http://docs.pwntools.com/en/stable/|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 [[http://docs.pwntools.com/en/stable/|documentation]] and especially [[https://github.com/Gallopsled/pwntools-tutorial/tree/master/walkthrough|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 [[http://security.cs.pub.ro/summer-school/res/arc/08-shellcodes-advanced-skel.zip|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 [[http://security.cs.pub.ro/summer-school/res/arc/08-shellcodes-advanced-skel.zip|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 [[http://security.cs.pub.ro/summer-school/res/arc/08-shellcodes-advanced-skel.zip|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 [[http://docs.pwntools.com/en/stable/|documentation]] and especially [[https://github.com/Gallopsled/pwntools-tutorial/tree/master/walkthrough|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 [[http://security.cs.pub.ro/summer-school/res/arc/08-shellcodes-advanced-skel.zip|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 [[http://security.cs.pub.ro/summer-school/res/arc/08-shellcodes-advanced-skel.zip|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 [[http://security.cs.pub.ro/summer-school/res/arc/08-shellcodes-advanced-skel.zip|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 [[http://docs.pwntools.com/en/stable/tubes/processes.html|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 [[http://security.cs.pub.ro/summer-school/res/arc/08-shellcodes-advanced-skel.zip|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 [[http://security.cs.pub.ro/summer-school/res/arc/08-shellcodes-advanced-skel.zip|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.
/*
==== 10.b. Challenge: Buffer is too small: Use another buffer for storing the shellcode ====
It may so happen that the buffer we overflow is to small to store the entire payload (including the shellcode). In such a situation we can't store the shellcode and then do the overflow in the same buffer. Similar to one of the tasks above we used for storing the shellcode in a global variable.
In the ''small-stack-buffer/'' folder in the tasks archive you have a setup where the local ''buffer'' variable is only 20 bytes large, unable to store the entire payload. However you may use the ''input_buffer'' variable for storing the shellcode.
Make the attack and get a shell.
One of the nastiest aspects of this task is the way you create the payload. Think where you will store the shellcode when feeding it as standard input to the program. One of most troublesome thing is the possible overwriting of the ''input_buffer'' variable when using ''strcpy()''. Be careful!
==== 10.c. Challenge: Buffer is too small: Use environment variable to store the shellcode ====
Let's make things a bit more challenging. Let's assume we have no room to store the shellcode in the local buffers.
In the ''shellcode-in-envvar/'' subfolder of the tasks archive both the ''input_buffer'' and the ''buffer'' variables are now ''28'' bytes wide and ''8'' bytes wide, respectively. We have no room to store the shellcode.
In this case we will can use another trick. We can define an environment variable, fill it with the shellcode, determine its address and overwrite the function return address with that address.
We recommend you create a file named ''shellcode_payload'' storing the shellcode. The contents of this file are to fill an environment variable.
Use the command bellow to define an environment variable and fill it with data:
export SHELLCODE=$(cat shellcode_payload)
In order to identify the address where this environment variable is stored, we recommend you add a padding prefix and look for that. For example, when creating the ''shellcode_payload'' file, use ''32'' ''A'' characters and then add the shellcode.
In GDB in order to find a string you may use:
gdb-peda$ find "AAAAAAAA" $esp $esp+1000
This above command means look for the ''%%"AAAAAAAA"%%'' string in the ''$esp, $esp+1000'' range (i.e. starting from the value of the ''esp'' register and ending ''1000'' bytes later).
You should also create an ''overflow_payload'' file for the actual attack, overflowing the ''buffer'' variable and rewriting the return address with that of the start of the shellcode.
In the end you would be able to run the attack through a command such as
cat overflow_payload - | SHELLCODE=$(cat shellcode_payload) ./vuln
===== Extra: io.netgarage.org (former io.smashtestack.org) =====
One of the most interesting and approachable security wargames is [[https://io.netgarage.org/|io.netgarage.org]]. It's highly recommended you go through as many tasks as possible.
For this session, we will use the 3rd and 5th tasks. They are found in the ''io.smashthestack.org/'' subfolder in the tasks archive.
Go through them, exploit them and profit (i.e. get a shell running).
You must disable ASLR for the tasks to go. You may do that using **either** of the two commands below:
$ echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
$ linux32 -3 -R bash -l
===== TODO =====
Tutorial: Prevent NUL-bytes in shellcode for string management functions
Bonus: Call trampoline
===== Tasks =====
When running the vulnerable executables for the tasks below make sure you have disabled ASLR in your shell:
$ linux32 -3 -R bash -l
==== Brute-forcing the buffer address ====
Switch to the //2-bruteforce// directory.
In this scenario we won't be using gdb to find the buffer address, since this method is very theoretical anyway. Instead we'll try every address in some range. To find out this range, we'll use a simple program that prints the value of its stack pointer.
You'll use the //exploit.py// script from the previous task for generating the payload. However, you'll have to modify it so that the return address won't be hardcoded in the script anymore, but taken as a parameter.
Then you have to edit the //run.sh// script to do the following:
* iterate over a range of values for the return address
* for each value use the exploit.py script to generate a payload
* run the vulnerable binary using that payload
== Guessing a range of values for the return address ==
You'll do this using the //print_stack// executable. Keep in mind that you can't use the value you get from //print_stack// directly. There are a couple of facts you have to take into account:
*the //vuln// binary has the buffer on the stack. That translates into a "sub esp, something" instruction. You have to find this value and subtract it from the value you get from //print_stack//
*you're passing the payload as a paramter. This also causes the stack pointer to decrease. So make sure you also run //print_stack// with a parameter of the same length as the one you're passing to //vuln//
==== NOP sled ====
In order to minimize the number of tries required for a successful exploitation you'll use a NOP sled. Start from the //exploit.py// file from the previous task. You can also use //print_stack// to obtain a guess for the return address. In the end you should be able to run the exploit with a line like:
$ ./vuln "$(exploit.py 0xbfffXXXX)"
==== Environment variables ====
In this scenario the stack buffer is too small to hold the entire shellcode. To overcome this you'll have to place the shellcode in an environment variable. To increase the chance of succeeding you will also prepend the shellcode with NOPs, as much as the system will allow for an environment variable (around 128K).
The //shell.py// file will generate the shellcode to be placed in the environment var.
The //exploit.py// file will generate the actual exploit for the overflow.
This is how you run them:
export A="$(./shell.py)"
./vuln "$(./exploit.py 0xbfffXXXX)"
== Finding the environment variable address ==
You still have to know what value to overwrite the return address with, that is, the address of the environment variable. To do this, you can write a small program that searches the variable in the environment and prints its address. Then you use the same address (or something around it) in your exploit, assuming that the environment is roughly the same when passed from the shell to its child processes.
*/