Team size: 3
Start and deadline for this lab: see tuwel
In this lab exercise you will strengthen and deepen the knowledge prepared for you during the lectures on advanced application attacks. More specifically, you will gain practice regarding exploit techniques for several classes of memory corruption vulnerabilities, learn how to circumvent mitigation techniques, and gain insight into real-world vulnerabilities.
Each student team can choose to complete either a larger number of entry-level exercises, or a smaller number of more difficult exercises in order to obtain the required number of points for getting the best grade. More specifically, we have 3 vulnerability categories:
Category A: Stack-based Buffer Overflow
Category B: Format-String Vulnerability
Category C: Heap Corruption (Heap Overflow, Use-After Free, Double Free, Type Confusion)
You can, for example, choose to create working exploit code for each vulnerability category at an entry level (with all mitigation mechanisms disabled), or choose from two vulnerability categories at an intermediate level (with some mitigation mechanisms turned on), or create a single exploit at an advanced/elite level. See section 'Scoring' below for details.
Each exercise in this lab will start with a research phase on real-world vulnerabilities, where you will learn to apply your knowledge about vulnerabilities to understand and analyze real-world bug reports and real-world code.
For each exercise in this lab, you need to complete the following phases:
Phase 1: Research Real-World Vulnerabilities
Search the internet for known, reported and documented, exploitable memory corruption vulnerabilities in the respective vulnerability category A, B, or C in real-world, open-source programs or libraries (academic examples of vulnerable programs like CTF challenges do not count as real-world programs. A good indication is the existence of a CVE number for the vulnerability. If in doubt whether a vulnerability fits the lab criteria, ask us). Select one exploitable vulnerability and document your choice in your submission protocol, including links to the source code repository and the vulnerable revision, as well as all directly relevant bug reports and exploit tutorials / PoC exploit code you are able to find. In your submission report, you can also succinctly document alternatives you considered, and why you rejected them (e.g., 'difficult to exploit because attack input gets expanded from utf-8 to utf-16 encoding').
Phase 2: Create Exemplary Vulnerable Program
Build a minimalistic C or C++ program that exemplarily reconstructs the vulnerability but abstracts away from all original functionality of your choice in phase 1: Technicalities relevant to the vulnerability selected in phase 1, such as buffer size, the vulnerable memory/pointer operation, size calculations that are relevant to the vulnerability should adhere closely to the selected vulnerability. However, use command line parameters or standard input as the source for attacker-controlled input, and you need not implement any actual functionality of the original program or library. Your submission must contain a Makefile, all the sources necessary for building your vulnerable program and your submission protocol, see example submission in tuwel.
Phase 3: Create Control-Flow Hijacking Exploit
Your vulnerable program from phase 2 will be installed with the setuid-bit set for a user named 'privileged' in our grading machine. You will create an exploit for your vulnerable program that achieves 'arbitrary code execution' in the privileged, vulnerable process. Your Makefile in the submission, which we will run under a user named 'unprivileged', will build your exploit and run it against your vulnerable program. Your exploitation attempt is successful if your exploit manages to write into a file in the directory '/home/privileged', which has write permission only for user 'privileged'. If it turns out that the vulnerability is not exploitable at your chosen difficulty level, you start over with phase 1. There is an important constraint: Your exploit/shellcode is not allowed to invoke system or execve, or call a shell, but your exploit must write without using those tools to the target directory.
For each vulnerability category A, B, or C, you will decide on a difficulty level for your exploit development. The selected difficulty level may influence the choice of real-world vulnerability in phase 1 (not every exploitable vulnerability is necessarily exploitable at every difficulty level), your modeling of the vulnerability and coding of the vulnerable program in phase 2 (for example, to circumvent ASLR and W^X you may need an additional information disclosure vulnerability so that your exploit code is able to learn about randomized addresses in the vulnerable process), and of course the specifics of your exploit techniques in phase 3, since you need to circumvent mitigations techniques at higher difficulty levels.
Your exploit must run successfully and robustly on a current Debian Bullseye image. Any addresses your exploit uses should be computed by your exploit. Hard-coded addresses might brake your exploit. We provide a vagrant file in tuwel to create a virtual machine image, where you can test your vulnerable program. We will use a similar environment to build and test whether you can write into /home/privileged as user 'unprivileged'.
Entry Level
At the entry level, your vulnerable program can be compiled with all relevant security options turned off, and ASLR support will be turned off in the system.
ASLR disabled
# echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
Allowed gcc compiler options
-D_FORTIFY_SOURCE=0 -Wl,-z,execstack,-z,norelro -no-pie
Heap manager, malloc implementation: You may use any old, not hardened version of dlmalloc, or ptmalloc. Just include the library in your submission archive and use it in your vulnerable program (look at the Makefile from the example submission in tuwel to see how this might be done).
Medium Level
At the medium level, your vulnerable program must run with W^X protection, and Partial RELRO. ASLR support will be turned off in the system.
ASLR disabled
# echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
The following gcc compiler options must be included (and any overriding compiler flags are prohibited)
-fno-stack-protector -D_FORTIFY_SOURCE=0 -Wl,-z,relro -no-pie
Heap manager, malloc implementation: You may use any old, not hardened version of dlmalloc, or ptmalloc.
Advanced Level
In addition to the protection mechanisms at medium level, at the advanced level, your vulnerable program must be compiled as a position independent executable and with full RELRO enabled as well as with Stack Cookie protection, and ALSR support must be enabled in the system.
ASLR enabled
# echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
the following gcc compiler options must be included (and overriding compiler flags are prohibited)
-fstack-protector-all -D_FORTIFY_SOURCE=0 -Wl,-z,relro,-z,now -fpie -pie
Heap manager, malloc implementation: You must use the default ptmalloc implementation that comes with the installed libc.
Elite Level
In addition to the protection mechanisms at advanced level, your vulnerable program will be protected by clang's Control Flow Integrity (CFI) protection, as well as by clang's Safe Stack protection mechanism
clang must be used, and the following compiler options must be set:
-fstack-protector-all -D_FORTIFY_SOURCE=2 -Wl,-z,relro,-z,now -fpie -pie -flto -fsanitize=cfi -fsanitize=safe-stack -fuse-ld=gold
The following table tells you how many points you are able to achieve at most per exercise.
Category A | Category B | Category C | |
---|---|---|---|
Entry | 15 | 15 | 20 |
Medium | 25 | 25 | 35 |
Advanced | 50 | 35 | 50 |
Elite | 50* | 50* | 50* |
Only the maximum score counts per category: I.e., if you write a stack based buffer overflow exploit (category A) at both the entry level as well as the medium level, you will get 25 points, not 25+15.
Maximum points in total for this lab: 50 points.
The awesomeness star (*) means that you will get special recognition.
All code must build and run on Debian Bullseye, and will be tested on a VM created by the provided Vagrantfile.
Your submission archive will be extracted into the folder /home/unprivileged inside the VM. You must include a GNU Makefile with predefined targets for building your vulnerable programs, and building and executing your exploits. You should use and extend the Makefile from the submission example in tuwel. This Makefile defines the targets that must be supported. Your Makefile will be executed as user 'unprivileged', in the directory /home/unprivileged. You succeed if an exploit_* target manages to write into the directory /home/privileged.
Suppose you do one category A exercise at medium level difficulty, and one category B exercise at entry level difficulty. Then your Makefile must support the following targets:
make vuln_stackoverflow-medium
:
make vuln_formatstring-entry
:
The user of your vulnerable programs will be set to 'privileged' and the setuid-bit will be set (see the 'install') target by the grading robot. The following targets must be supported
make exploit_stackoverflow-medium
:
make exploit_formatstring-entry
:
All other analogous targets should output the string 'NOT IMPLEMENTED' to stdout.
A team that chooses to implement exercises from different categories or at different difficulty levels must implement the respective targets in the Makefile analogously to the example above.
Note: Replace TEAM-NUMBER in the following instructions with your own team number which you can find in tuwel.
Create a (zip|gzip|bzip2) compressed archive, lab1_TEAM-NUMBER.(zip|tgz|tar.gz|tbz2|tar.bz2) with the following content.
Makefile and all the sources of your vulnerable program and exploit according to the section 'Specification and Automated Grading Lab1' above.
Short documentation of each exercise that you solved or tried to solve in your submission report.
Submission report in PDF format (lab1_TEAM-NUMBER.pdf), a template can be downloaded from tuwel.
Your report must include the following entries.