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-2023/csci4271/labs/07/uses-system.c .
gcc -Wall -g uses-system.c -o uses-system
-
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.
-
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
method that instead of the normal behavior, instead
removes the LD_PRELOAD from the environment with
unsetenv and then runs 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-2023/csci4271/labs/07/read-five-chars.c .
gcc -Wall -g read-five-chars.c -o read-five-chars
-
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.
-
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)
-
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).
-
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 10% of the
time.