Before you start, review the course syllabus for the Lateness, Collaboration, and Ethical Use policies.
You may optionally work alone, or in teams of at most two and submit one project per team. If you have difficulties forming a team, post on Piazza’s Search for Teammates forum. Note that the final exam will cover project material, so you and your partner should collaborate on each part.
The code and other answers your group submits must be entirely your own work, and you are bound by the University’s Student Code. You may consult with other students about the conceptualization of the project and the meaning of the questions, but you may not look at any part of someone else’s solution or collaborate with anyone outside your group. You may consult published references, provided that you appropriately cite them (e.g., in your code comments). Don't risk your grade and degree by cheating!
Complete your work in the CS 4440 VM—we will use this same environment for grading. You may not use any external dependencies. Use only default Python 3 libraries and/or modules we provide you.
In this project, you'll get hands-on experience with exploiting real-world security vulnerabilities in application software. You will perform attacks to gain total control of a system by exploiting a series of application targets containing critical security bugs. Attacks you will perform include buffer and integer overflows, as well as workarounds to popular defenses such as Data Execution Prevention and Address Space Layout Randomization. We will provide a series of vulnerable programs and a virtual machine environment in which you will write, test, and deploy your exploits.
Before you begin, please carefully read through the following sections for important setup information and guidelines about this project.
Your target programs for this project are small C programs with (mostly) clear security vulnerabilities. We have provided source code, as well as a build.sh
file that compiles all the targets.
Before you start writing your exploits, set up your application targets as follows:
$ wget http://cs4440.eng.utah.edu/files/project2/targets.tar.gz
$ tar -xf targets.tar.gz
to extract the assignment files. You should see a targets/
directory which contains the assignment code.$ ./build.sh
. It will prompt you for the password (cs4440
) and your group’s UID(s) (i.e., u#######
). Make sure both UIDs are correct! If you working solo, enter your UID once.cookie
—you will submit it along with your solution files!$ ./build.sh clean
followed by $ ./build.sh
.Constructing your Exploits. For each target, you must develop and submit a single working exploit as a Python 3 script (named sol#.py
, where #
refers to the target's number). Your exploits must work against the targets as compiled and executed within the provided CS 4440 VM.
To help you get started, we provide a skeleton template (sol.py) for you to implement your attacks in:
import sys
from struct import pack
from shellcode import shellcode
# Implement your attack here!
payload = ''
# Launch the attack!
sys.stdout.buffer.write(payload)
Working with Python 3. Python 3 works with bytes rather than Unicode strings. To construct a byte literal, you can use the following: b"\xNN"
(where NN
is a 2-digit hex value). To repeat a single byte a total of x
times, use this syntax: b"\xNN" * x
. To output a sequence of bytes, use the following:
import sys
sys.stdout.buffer.write(b"\x61\x62\x63")
For this assignment, you should refrain from using Python's print()
function (except for in your own debugging statements)—use the sys.stdout.buffer.write()
function to print bytes instead!
root
! For Targets 2–8, verify that your opened shell is indeed a root shell by running the whoami
command—it should return root
!
Note that if you are testing your exploit within GDB, any spawned shells will be non-root—because GDB blocks setuid()
from escalating the process privilege. To verify that your exploit opens a root shell, test it outside of GDB!
sudo
! If you run Targets 2–8 under sudo
(e.g., $ sudo ./target2
), then any shells spawned will always be spawned as root—whether you have accomplished the task of opening a root shell or not! To get full points, your attacks must open a root shell outside of sudo
!objdump
. While you can also perform disassembly using GDB's disas
directive, you may also wish to use the built-in objudmp
utility to disassemble an entire executable. To do so, launch a terminal and run the following: $ objdump -D fileToDisas > disas.txt
. You can then view the whole-program disassembly in the resulting file disas.txt
.setuid
when cloned—preventing root shells! Upload just your solution scripts (e.g., sol1.py
).Targets 0–1 are designed to build up your confidence for the remaining parts of this assignment. To help you get started, we've provided some exercises on using GDB to inspect your target programs—a critical step of any pre-exploitation target reconaissance!
Test your exploits with the commands below. We'll use the same commands to grade your solutions.
This program takes input from stdin
(i.e., your keyboard) and prints a message. In this exercise, you will provide input to instead make this program print: "Hi UID! Your grade is A+"
. Your input will need to overwrite stack variable grade[]
to achieve the intended behavior.
With your GDB Cheat Sheet in hand, work through the following exercise in crafting your exploit:
target0.c
. Where does the buffer overflow occur?$ gdb ./target0
Disassemble function _main
with the following command. What is _main
's starting address?
(gdb) disas _main
Set a breakpoint at the beginning of _main
and run the program:
(gdb) break _main (gdb) run
name[]
and grade[]
stored relative to each other?name[]
affect the value contained in grade[]
?./target0
on the command line with several different inputs.Your task: Create a Python 3 program named sol0.py
that causes the target to print "Hi UID! Your grade is A+"
(where UID
is replaced with one of your team's UIDs; or if working solo, just your UID). Test your program with the command line: $ python3 sol0.py | ./target0
.
This program takes input from stdin
and prints a message. In this exercise, you will provide input to instead make this program print: "Your grade is perfect"
. Your input will need to overwrite function vulnerable()
's return address and redirect execution to function print_good_grade()
.
Here is one approach you might take in developing your exploit:
target1.c
. Where does its buffer overflow occur?print_good_grade
. What is its starting address?vulnerable
and run the program.vulnerable
and draw the stack. Where is variable input[]
relative to the ebp
register? How long must an input be to overwrite this value and the return address?Use the following to inspect registers esp
(the stack pointer) and ebp
(the saved frame pointer):
(gdb) info reg
What are the current values of ebp
and the return address from the stack frame? To examine two words of memory at location ebp
, you can use the command shown below.
(gdb) x/2wx $ebp
print_good_grade
?pack()
function with "<I"
encoding (e.g., pack('<I', 0xDEADBEEF)
).
Your task: Create a Python 3 program named sol1.py
that causes the target to print "Your grade is perfect"
. Test your program with the command line: $ python3 sol1.py | ./target1
.
sol#.py
) that exploits it to print the expected string.
The remaining target binaries are owned by the root user (with their setuid
bits set accordingly). Your goal is to exploit them each to invoke a shell with root privileges—how most attacks today work! This and the following targets will all take input as command-line arguments (rather than input from stdin
). Unless otherwise noted, you should use the shellcode we have provided in shellcode.py
(included with your targets.tar.gz
). Successfully placing this shellcode in memory and redirecting execution to the beginning of the shellcode (e.g., by returning or jumping to it) will spawn a root shell!
To verify your opened shell is indeed a root shell, run the whoami
command—it should return root
! Be sure to review the information in Important Guidelines regarding how to correctly spawn root shells!
This program contains a straightforward buffer overflow vulnerability. Use your skills from Targets 0–1 to construct an exploit that loads your shellcode into the vulnerable buffer and redirects execution to it!
Your task: Create a Python 3 program named sol2.py
that causes the target to open a root shell. Test your program with the command line: $ ./target2 $(python3 sol2.py)
.
Here, the buffer overflow is restricted and cannot directly overwrite the return address. You need to find another way! Your input should cause the provided shellcode to execute and open a root shell.
Your task: Create a Python 3 program named sol3.py
that causes the target to open a root shell. Test your program with the command line: $ ./target3 $(python3 sol3.py)
.
This target takes as its command-line argument the name of a data file it will read. The file format is a 32-bit count
followed by that many 32-bit integers. Create a data file that causes the provided shellcode to execute and open a root shell.
read_elements()
breaks out of the for
-loop once the end of the file is reached... so the 32-bit count
variable does not need to be truthful!
cs4440
) shell, you are very close! What part of the provided shellcode is your exploit skipping over?
Your task: Create a Python 3 program named sol4.py
that outputs the contents of a file to be read by the target. Test your program with the command line: $ python3 sol4.py > tmp; ./target4 tmp
. Your exploit should not output file tmp
—we will pipe your exploit's output to tmp
(as in the command).
sol#.py
) that exploits the target to open a root shell.
Targets 5–6 up the difficulty level: you'll be implementing workarounds to several common real-world application defenses! As usual, you'll be expected to exploit these targets into opening root shells.
This program resembles Target 2, but it has been compiled with Data Execution Prevention (DEP) enabled. DEP means that the processor will refuse to execute instructions stored on the stack. You can overflow the stack and modify values like the return address, but you can’t jump to any shellcode you inject. You will need to find another way to run the command /bin/sh
and open a root shell!
Your exploit must not cause the program to display any output other than a root shell; this shell must also close gracefully—without segfaulting! If not, your attack is incorrect. Your solution cannot depend on you manually setting environment variables; you cannot assume that the autograder will run your solution with the same environment variables that you have set.
exit()
and _exit()
.
Your task: Create a Python 3 program named sol5.py
that causes the target to open a root shell. Test your program with the command line: $ ./target5 $(python3 sol5.py)
.
When we constructed the previous targets, we ensured that the stack would be in the same position every time the vulnerable function was called, but this is often not the case in real targets. In fact, a defense called Address Space Layout Randomization (ASLR) makes buffer overflows harder to exploit by changing the starting location of the stack and other memory areas on each execution. This target resembles Target 2, but the stack position is randomly offset by 0—255 bytes each time it runs. You need to construct an input that always opens a root shell despite this randomization.
Your exploit must not cause the program to display any output other than a root shell; this shell must also close gracefully—without segfaulting (at least half of the time)! If not, your attack is incorrect.
Your task: Create a Python 3 program named sol6.py
that causes the target to open a root shell. Test your program with the command line: $ ./target6 $(python3 sol6.py)
.
sol#.py
) that exploits the target to open a root shell.
The following targets offer an extra challenge for those wanting to test their exploitation skills. If you find these interesting, definitely come play Capture the Flag (CTF) competitions with us in UtahSec!
Target 7 is identical to Target 2, but it is compiled with DEP enabled. Implement a ROP-based attack to bypass DEP and open a root shell. You may find the objdump
utility helpful.
Your exploit must not cause the program to display any output other than a root shell; this shell must also close gracefully—without segfaulting! If not, your attack is incorrect.
Your task: Create a Python 3 program named sol7.py
that causes the target to open a root shell. Test your program with the command line: $ ./target7 $(python3 sol7.py)
.
Target 8 implements a doubly-linked list on the heap, and takes three command-line arguments. Find a way to exploit it to open a root shell. You may need to modify the provided shellcode slightly.
Your task: Create a Python 3 program named sol8.py
that prints lines to be used for each of the command-line arguments to the target. Your program should take a single numeric argument that determines which of the three arguments it outputs. Test your program with the command line:
$ ./target8 $(python3 sol8.py 1) $(python3 sol8.py 2) $(python3 sol8.py 3)
.
sol#.py
) that exploits the target to open a root shell.
Upload to Canvas a tarball (.tar.gz
) named project2.uid1.uid2.tar.gz
, replacing your team's UIDs accordingly (if working alone, provide only your UID once). Each UID must be in u#######
format. Your tarball must contain only the files listed below. These will be autograded, so make sure that your solutions conform to the expected filenames, formatting, and behaviors.
Failure to follow assignment instructions (e.g., submitting a corrupted tarball; wrong, missing, or broken code; improper formatting; etc.) will be ineligible for regrades. External dependencies are prohibited. You may use only default Python 3 libraries and/or modules we provide you. Your solutions must work as-is in the CS 4440 VM. Make sure to thoroughly test your code before submitting!
Generate the tarball in your VM terminal using this command (be sure to first cd
to the directory that contains your files):
tar -zcf project2.uid1.uid2.tar.gz cookie sol[012345678].py