Project 2: Software Vulnerabilities

  • Due: 04/06/2025 (Sun) 11:59pm

Introduction

In this project, you will perform a series of software vulnerability exploits. You will explore unsafe and insecure programming techniques and evaluate the efficacy of operating system defenses against them.

Getting started

If you have not finished setting up your project environment, please follow the instructions in Project Setup to set it up first.

We expect you will work on this project remotely in the project server through SSH.

[host] $ ssh NetID@csa-chk22b.utdallas.edu

Once you are successfully logged in, fetch the source code of Project 2 in the project server. To do that, use Git to commit changes you've made since handing in Project 1 (if any), fetch the latest version of the course repository, and then create a local branch called project2 based on our project2 branch origin/project2:

$ cd ~/infosec
$ git pull
Already up-to-date.
$ git add -A
$ git commit -am 'changes to project1 after handin'
Created commit 734fab7: changes to lab1 after handin
     3 files changed, 28 insertions(+), 7 deletions(-)
$ git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 4 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 308 bytes | 308.00 KiB/s, done.
Total 3 (delta 2), reused 0 (delta 0), pack-reused 0
remote:
To ssh://s3lab.utdallas.edu:2224/cxk200010/infosec.git
   88682b1..494dc56  lab1 -> lab1
$ git checkout -b project2 origin/project2
Branch project2 set up to track remote branch refs/remotes/origin/project2.
Switched to a new branch "project2"
$ git clean -dfx
$

The git checkout -b command shown above actually does two things: it first creates a local branch project2 that is based on the origin/project2 branch provided by the course staff, and second, it changes the contents of your project directory to reflect the files stored on the project2 branch. Git allows switching between existing branches using git checkout branch-name (change branch-name to the branch name you want to switch to), though you should commit any outstanding changes on one branch before switching to a different one. The git clean -dfx command deletes all unsaved files from the project1 branch (e.g., *.class files), which you do not need for this project.

You will now need to bring your student.info file from the project1 branch into the project2 branch, as follows:

$ git checkout --patch project1 student.info
$ git commit -am "Bring student.info from project1"

Project 2 includes two directories: targets and exploits. In the targets directory, you will find the source files of target programs (target1.c, target2.c, target3.c, and target4.c). These programs are written in C and contain hidden software vulnerabilities that you need to identify and exploit. Please do not modify the source code of the target programs. The source code of your exploits needs to be placed in the exploits directory.

You need to access a virtual machine (VM) that is set up to run the targets and your exploits, namely attackme. This VM has the tools (e.g., gcc and gdb) that we expect you to use to examine the target programs and write the exploits. We do not provide full-featured hacking and code analysis tools, such as Ghidra and IDA in this VM. You will not gain your knowledge/techniques to identify and exploit the software vulnerabilities if you use these automated tools, which is against the main goal of this project. If you need legitimate tools and the VM does not have the tools installed, you can request the CS 6324 staff to install them, subject to the instructor's approval.

You can access the VM through SSH from inside the project server:

$ ssh attackme
[attackme] $

The VM shares your home directory (~/) with the project server. Any changes in your home directory including project files in the infosec directory will be synchronized between the VM and project server automatically.

To compile the source code of the target programs, run the following commands.

[attackme] $ cd ~/infosec/targets
[attackme] $ make
gcc -ggdb -Wall    target1.c   -o target1
gcc -ggdb -Wall    target2.c   -o target2
gcc -ggdb -Wall    target3.c   -o target3
gcc -ggdb -Wall    target4.c   -o target4
target4.c: In function 'main':
target4.c:12: warning: unused variable 'a'
target4.c:12: warning: unused variable 'b'
target4.c:12: warning: unused variable 'c'
target4.c:12: warning: unused variable 'd'

Hand-in procedure

You will turn in your project by pushing your progress to the repository and tag the final version of the project.

When you are ready to hand in your project code and report, please place the report in a file called report-project2.pdf in the top level of your directory before handing in your work. After that, add your report to the Git repository with git add report-project2.pdf and git commit.

If you have obtained help of any kind while working on this project, make sure to write the names or URLs of your sources in references-project2.txt in the top level of your directory, and add it to the repository with git add references-project2.txt and git commit.

You need to update Makefile in the exploits directory. This Makefile will be used by the CS 6324 staff to run and grade your exploits. Your exploits can be written in any programming/scripting language that the VM supports (e.g., Bash, C, Perl, and Python).

For example, you can create a shell script for your exploit1 and add the following command into Makefile to run it:

exploit1:
     @echo "# exploit1: Add your commands to run the exploit below"
     ./exploit1.sh

Or, you can add the following commands into Makefile for us to run your exploit1 written C:

exploit1:
     @echo "# exploit1: Add your commands to run the exploit below"
     gcc exploit1.c -o exploit1
     ./exploit1

When the CS 6324 staff runs the following command in the VM, your Makefile should allow running your exploit1:

$ make exploit1

Failure to follow these rules will not give you any credit.

Note

Tip If you write an exploit in C, you should use a function like execve to launch the target, but not a function like system. When using execve, pass in NULL for the environmental variables so that it will be consistent and repeatable from run to run.

Make sure your exploits run successfully in the VM since that is where we will grade them. Run the following commands to check if your exploits run successfully in the attackme VM.

[attackme] $ cd ~/infosec/exploits
[attackme] $ make

After checking your exploits run successfully in the VM, add the new exploit files and any other new files you have created into the Git repository, commit, and push. For example,

[attackme] $ git add exploit1.sh exploit2.c
[attackme] $ git commit -am 'project2 complete'
[attackme] $ git push

Tag your final commit as project2-final and push the tag to the repository to submit your progress. We only grade the commit with this tag.

[attackme] $ git tag project2-final
[attackme] $ git push
[attackme] $ git push origin --tags

# if you want to change the final tag,
[attackme] $ git tag -d project2-final # this will delete the local tag
Deleted tag 'project2-final' (was 75411c7)
[attackme] $ git push origin :refs/tags/project2-final # this will delete the remote tag
To ssh://s3lab.utdallas.edu:2224/cxk200010/infosec.git
- [deleted]         project2-final

Simple Command Line Buffer Overflow (20 pts)

target1 is a program that takes a directory as input, and tells the user how to use the command ls to list the contents of the directory. You will login as a normal user, and your goal is to pass an argument to the program so it will start a shell by exploiting a buffer overflow vulnerability.

Suppose that this program is setuid root, then it would be possible to start a "root" shell. Although the target program does not actually have the setuid bit set or owned by root (to avoid potential security issues), please assume that it is setuid root.

Note

You are required to exploit a buffer overflow vulnerability to start a shell. For example, passing "/bin/bash" as a command line argument to the target program to open a shell (without exploiting the vulnerability) will not give you any credit.

  • In the exploits directory, write an exploit program (e.g., exploit1.c or exploit1.sh) that passes an attack string to the target and performs the attack. Update Makefile to (compile and) run your exploit. make exploit1 should run your exploit successfully.

  • Identify the exact vulnerability in the program that you exploited (i.e. function name and line number). Explain why it is a vulnerability.

  • Explain your attack strategy. That is, explain how you determined the correct input to pass and what commands are executed.

Buffer Overflow to Rewrite a Return (20 pts)

The attack

target2 is a program that takes a customer's name as the input, and prints a coupon. Assume that each customer can only execute the program once, so he/she can only get one coupon. Your goal is to pass some argument to the program so it will repeatedly print coupons. In other words, the argument will make the program execute the function coupon repeatedly.

Note

To get full credit, the function coupon has to execute an infinite number of times. If it only executes twice, then you will get half the points.

  • In the exploits directory, write an exploit program (e.g., exploit2.c or exploit2.sh) that passes the attack string to the target and performs the attack. Update Makefile to (compile and) run your exploit. make exploit2 should run your exploit successfully.

  • Identify the specific bug/vulnerability that made your attack possible (i.e. function name and line number). Explain why it is a vulnerability.

  • Describe your attack strategy. That is, describe the memory addresses involved in your attack, and explain how the attack made the program print an unlimited number of coupons.

The defense

The project server that hosts the attackme VM has an updated operating system with some stack defenses activated.

  • Repeat the attack on target2 outside the VM. Did the attack work? Comment on your results (i.e. explain why).

  • Propose two different operating system and/or compiler/programming language defenses that can be used to prevent this attack from working. Discuss the advantages, disadvantages, and feasibility of the proposed defenses.

Return to LibC (20 pts)

The attack

target3 is a program that scans several network packets and checks if the traffic (concatenation of the packets) matches any virus signatures. Suppose target3 is setuid root. You will login as a normal user, and the goal is to pass argument(s) to the program to start a root shell by exploiting a return-to-libc vulnerability.

You need to assume that the stack is not executable. Therefore, you cannot change the return address to the shellcode in the stack.

  • Draw the layout of the stack frame corresponding to the function is_virus directly after the local variables are initialized. For each element on the stack, provide its size.

  • In the exploits directory, write an exploit program (e.g., exploit3.c or exploit3.sh) that performs the attack. Update Makefile to (compile and) run your exploit. make exploit3 should run your exploit successfully.

  • Identify the specific bug in the program and vulnerability that made your attack possible (i.e. function name and line number). Explain why it is a vulnerability.

  • Describe your attack strategy. That is, explain what memory addresses you used and how you figured out those addresses.

The defense

Try repeating the above attack on the project server outside the VM. The attack should become more difficult now.

  • Are you able to get the attack to work? If so, explain your method. Otherwise, explain what prevented you from completing the attack.

  • What specific mechanism(s) make the attack more difficult?

Format String Attacks (40 pts)

target4 has a format-string vulnerability; your task is to develop a scheme to exploit this vulnerability.

The target program asks the user to provide an input, which will be saved in a buffer called user_input. The program then prints out the buffer using printf. Unfortunately, there is a format-string vulnerability in the way the printf is called on the user inputs. We want to exploit this vulnerability and see how much damage we can achieve.

The program has two secret values stored in its memory, and you are interested in these values. However, the secret values are unknown to you, nor can you find them from reading the binary code.

Note

For the sake of simplicity, we hardcode the secrets using constants 0x44 and 0x55, but you can pretend that you don't have the source code or the secrets.

Although you do not know the secret values, in practice, it is not so difficult to find out their memory addresses (the range or the exact value). The values are at consecutive memory addresses because in many operating systems, memory addresses remain unchanged any time you run the program.

  • Draw the layout of the stack frame corresponding to the main function directly after the local variables are initialized. For each element on the stack, provide its size.

  • Provide the specific inputs (i.e. both the integer and the string) that you need in order to crash the program. Write the inputs in exploit4.txt at line 1 in the form of int string That is, the integer number, followed by a space, followed by the string. Explain why the program crashes with your inputs.

  • Provide the specific inputs (i.e. both the integer and the string) that you need in order to print the address of the variable secret[0]. Write the inputs in exploit4.txt at line 2 in the form of int string. Explain why you think this is the correct address.

Note

Tip You can use GDB to verify that your answer is correct.

  • Provide the specific inputs (i.e. both the integer and the string) that you need in order to print the value of secret[0]. Write the inputs in exploit4.txt at line 3 in the form of int string. Explain your strategy.

  • Based on your knowledge of how arrays are stored on the heap, calculate the address of secret[1]. Write the inputs in exploit4.txt at line 4 in the form of int string. Explain your strategy.

  • Provide the specific inputs (i.e. both the integer and the string) that you need in order to print the value of secret[1]. Write the inputs in exploit4.txt at line 5 in the form of int string. Explain your strategy.

  • Provide the specific inputs (i.e. both the integer and the string) that you need in order to modify the values of both secret[0] and secret[1]. Write the inputs in exploit4.txt at line 6 in the form of int string. Explain your strategy.

  • Does Address Space Layout Randomization (ASLR) make this attack more difficult? Explain.

  • What other operating system defenses can be used to prevent this attack? Explain.

Failure to follow the format rules on exploit4.txt will not give you any credit.

Makefile in the exploits directory has commands to print out the content of exploit4.txt when make exploit4 is executed. Please do not change that part.

Using GDB

GDB is the best way to understand how a target program executes internally when certain inputs given at run-time in this project. You can also identify the memory addresses of variables and buffers to exploit, and print out the content of the stack memory, including the return address of a function call. See the GDB manual for a full guide to GDB commands. Here are some particularly useful commands for this project.

Ctrl-c

Halt the machine and break in to GDB at the current instruction.

c (or continue)

Continue execution until the next breakpoint or Ctrl-c.

si (or stepi)

Execute one machine instruction.

b function or b file:line (or breakpoint)

Set a breakpoint at the given function or line.

b *addr (or breakpoint)

Set a breakpoint at the EIP addr.

set print pretty

Enable pretty-printing of arrays and structs.

info registers

Print the general purpose registers, eip, eflags, and the segment selectors.

x/Nx addr

Display a hex dump of N words starting at virtual address addr. If N is omitted, it defaults to 1. addr can be any expression.

x/Ni addr

Display the N assembly instructions starting at addr. Using $eip as addr will display the instructions at the current instruction pointer.