Skip to content

Project 2: Memory Corruption

Due: Sunday 18 October, 11:59 PM

Goal

Welcome to the special 24th anniversary remix edition of the memory corruption targets/sploits project!

The goal of this assignment is to gain hands-on experience with the effect of buffer overflow bugs and format string bugs.

You are given the source code for five exploitable programs, target1, target4, target5, and target6. Your goal is to write four exploit payloads (egg1.raw, egg4.b64, egg5.b64, and egg6.b64), that, when supplied to the corresponding target program should result in execution of shellcode. You are also given a warmup towards exploiting target5, called target5easy; here the goal is just to get the target to print out a success message when given egg5easy.b64; shellcode is not involved.

Because we live in the future, there is no need to construct these payloads using a C sploit program. Instead, you will write short Python scripts (make-egg1.py, make-egg4.py, make-egg5easy.py, make-egg5.py, and make-egg6.py) that write the corresponding payload file.

Background

You have already read Aleph One’s “Smashing the Stack for Fun and Profit.”

For background on exploiting malloc metadata corruption, see the two classic articles by Maxx and anonymous in Phrack 57. For the theory behind malloc metadata, see the survey by Paul Wilson and friends.

For background on exploiting format string bugs, see the classic paper by scut and Team TESO. Read this paper closely, especially the sections on short writes (4.1) and direct parameter access (4.3).

You should read these papers for inspiration, not as recipes to follow literally. The 32-bit x86 environment they describe is different from our 64-bit RISC-V VM, and of course umalloc is not the same as 2001-era dlmalloc.

The environment

We assume that you have set up the course virtual machine environment for CSE 291Y as described in the project 1 writeup.

Running as the user (not as root), download and unpack the project tarball in the VM using the command

    curl -O https://cseweb.ucsd.edu/classes/fa26/cse291Y-002/dist/proj2.tar.gz

and unpack the tarball using the command tar xavf proj2.tar.gz.

Run make to build the targets as well as the driver program.

If you run a target program directly from the shell, the base addresses of its stack and heap will be randomized by ASLR. If you run a target program through GDB, ASLR will be disabled but stack addresses will still vary depending on the execution environment. To make exploits consistent, we will launch the targets through the driver program. The driver program takes the following command-line arguments: the target binary to run; a filename to pass to the target as an argument; and your TSN. For example, I might run target1 on egg1.raw using the command line:

$ ./driver target1 egg1.raw 200001337

Using GDB

You can arrange to debug the target when launched in exactly this way, so that you can use GDB to read addresses, as follows.

First, run GDB on the driver program: gdb driver. Next, before telling GDB to run the program, ask to get control back when on execve: catch exec. Now have GDB launch the driver, specifying command-line arguments as above: run target1 egg1.raw 200001337.

If all goes well you should get control on exec. Now is the time to set breakpoints in the target program (if you tried to do it earlier, you’d have gotten breakpoints in the driver program instead): break foo. Then continue.

Note that the length of the argument to the target program will affect the placement of stack frames (because, as you know from your operating systems course, the kernel places arguments at the very top of the stack). Try to figure out how long your egg should be early on, then fine-tune its contents once stack layout is stable.

As we mentioned in class, GDB is a powerful tool that will help you in this class and beyond. Time spent reading the GDB manual is time well spent.

The assignment

Your goal is to craft a binary payload, in egg1.raw, that will cause target1 to execute shellcode when run as

./driver target1 egg1.raw <your-tsn>

Replace <your-tsn> with your actual TSN (this is your new Triton Student Number, starting with a 2, that replaced the SIS ID that started with an A.)

Likewise, your goal is to craft a base64-encoded payload in egg4.b64 that will cause target4 to execute shellcode when run as

./driver target4 egg4.b64 <your-tsn>

to craft a base64-encoded payload in egg5easy.b64 that will cause target5easy to print a success message when run as

./driver target5easy egg5easy.b64 <your-tsn>

to craft a base64-encoded payload in egg5.b64 that will cause target5 to execute shellcode when run as

./driver target5 egg5.b64 <your-tsn>

and to craft a base64-encoded payload in egg6.b64 that will cause target6 to execute shellcode when run as

./driver target6 egg6.b64 <your-tsn>

The reason that egg4, egg5easy, egg5, and egg6 are base64-encoded is so that you can include 0 bytes in the decoded version; by contrast, you should have no 0 bytes in egg1.

Note that, in target5easy and target5, snprintf will still stop processing its format string argument at the first 0 byte in encounters. The base64 encoding will let you get 0 bytes onto the stack (in the rawegg buffer) but not into the format string itself.

We’ve also included a target5hard that resembles target5 but does accepts a raw rather than base64-encoded input. Exploiting this target is possible but much harder. You may try it for an extra challenge, but you do not need to exploit it for full credit on this project.

The make-egg scripts

In principle, you could create the exploit payloads egg1.raw, etc., by hand using a hex editor, or write programs in any programming language to create them. For concreteness, we ask you to use Python to create the exploit payloads. The script make-egg1.py should write the egg1.raw file; the script make-egg4.py should write the egg4.b64 file; the script make-egg5easy.py should write the file egg5easy.py; the script make-egg5.py should write the egg5.b64 file; and the script make-egg6.py should write the egg6.b64 file.

You will submit the make-egg scripts along with the actual payloads; we will check that running the scripts regenerates the payloads files.

Rather than hardcoding a specific shellcode, your scripts should incorporate the binary string shellcode.SHELLCODE (which is defined for you in shellcode.py). The particular shellcode we’ve provided you is 170 bytes long, but your scripts should work with other reasonably sized shellcode, up to 200 bytes, which means you should adapt your egg according to len(shellcode.SHELLCODE). Our autograder will test your exploits using different shellcode than what is included in the project tarball, so make sure that you avoid hardcoding either the shellcode itself or its length in your solution. (You can easily generate additional shellcode using the tooling from project 1.)

As shown in the starter files, Python has convenient facilities for base64 encoding. Feel free to use any other Python features you find handy, like bytearrays and struct.pack (for the latter you’ll need to add an import struct), but don’t rely on any Python library that isn’t part of the base system.

Logistics

You will submit using Gradescope. You should submit exactly the following files:

  • egg1.raw, egg4.b64, egg5easy.b64, egg5.b64, and egg6.b64: These are your exploit payloads.

  • make-egg1.py, make-egg4.py, make-egg5easy.py, make-egg5.py, and make-egg6.py: These scripts should regenerate the submitted egg files exactly.

  • TSN: a text file with exactly one line, containing only your TSN, exactly as you supplied it to the driver program.

Grading

TBD.