University of Minnesota
Development of Secure Software Systems
index.php

CSci 4271 Lab 7

This lab will cover two kinds of vulnerabilities in OS interaction we've discussed in lecture, and their corresponding attack techniques. First will be controlling one program's use of another program, while second is a time-of-check to time-of-use race condition. These are the kinds of vulnerabilities that one might attack in a program that was privileged such as by running setuid, though for simplicity in what can be done in the CSE Labs context, you'll just attack unprivileged programs running as yourself. (If you're doing the lab with a partner you trust, you could try making the vulnerable programs setuid; though some of the attacks will be blocked in that setting.)

  • (Controlling program usage)

    The program uses-system doesn't do anything interesting besides calling the system program uname, which when called without other arguments just prints Linux. As a sample attacker goal, suppose you want to make running uses-system trigger a different program, say the calculator program xcalc. The two modes of the uses-system program demonstrate two ways of calling an external program that could be hijacked.

    cp /web/classes/Spring-2024/csci4271/labs/07/uses-system.c .
    gcc -Wall -g uses-system.c -o uses-system
    
    1. In mode 1, the program uses an unqualified program name as an argument to the system library routine, which in turn uses the shell to execute the program. What environment variable should you modify to change what program gets executed? Try out your attack.
    2. Mode 2 uses an absolute path to the uname program, which avoids the attack described in mode 1. But even if the normal /bin/uname program runs, you can subvert it by replacing one of the library functions it calls. Do this via the LD_PRELOAD environment variable. The function we recommend you replace is named fputs_unlocked; it is the one that uname would normally use to print the string Linux. In a new C source file, write a new implementation of this function. Instead of the normal behavior, your function should remove the LD_PRELOAD from the environment with unsetenv (to avoid an infinite loop) and then run xcalc. You should compile this into a shared library with the -shared option to GCC, give it a file name ending in .so, and then pass the absolute path to it in the LD_PRELOAD environment variable.
  • (TOCTTOU races)

    The program read-five-chars attempts to read a file that should contain 5 characters followed by a newline, and then prints the contents of the file. However, there is a race between the checks on the file and the process of opening and printing it that can be used to trick the program into printing more than a 5-character string. Can you get the program to instead print:

    My favorite five-character string is Calculator!
    

    Here again the first argument to the program, named mode, switches between a few different versions of the vulnerability that can be attacked in different ways.

    cp /web/classes/Spring-2024/csci4271/labs/07/read-five-chars.c .
    gcc -Wall -g read-five-chars.c -o read-five-chars
    
    1. In mode 1, the program waits for 10 seconds between the check and the use. This is enough time that you can carry out the race condition attack manually. You can overwrite the file with cp, or make it be a link using ln; if using ln, you may find it useful to supply the options -nsf to ensure that a new symbolic link always overwrites an old one.
    2. In mode 2, the program waits only 500 milliseconds, so you'll want to automate your attack to make sure it runs fast enough. It's useful to know that on modern versions of Linux, the sleep command can take fractional seconds for its argument. You may find it useful to know that the shell syntax:
      (a; b) & (c; d)
      
      can be used to run two sequences of commands at once. For instance can you predict what the following command will print?
      (echo a; sleep 0.25; echo b) & (echo c; sleep 0.5; echo d)
      
    3. In mode 3, the program tries to read the contents of another file in between the check and the use. You can reliably attack this version by making the other file the program tries to read a named pipe (created with mkfifo). A named pipe has the property that if one program opens it to read, that program will wait until another program has opened it to write (or vice-versa).
    4. In mode 0, there are no additional operations between the check and the use. This is what would most classically be called a "race" because it really just does depend on whether the attacker can make the change at just the right moment. But if you set up the attack in a basic way you should be able to still see it work at least some of the time.