Lab: Stack-Based Buffer Overflows
In this lab, you will be learning about buffer overflows in the stack, a common vulnerability in programs that allows you to run arbitrary code. This lab will require some programming in C, some knowledge of assembly, and a touch of debugging tools.
Late submissions will result in a 10% penalty per day (e.g., 2.5 days late result in 25% penalty)
Stack-based buffer overflows have been around for a long time and all indications are that these and similar attacks will continue to be a problem in the near future. Aleph One wrote the definitive paper on using this technique to exploit programs, Smashing The Stack For Fun And Profit, and Paul Makowsky expanded on the protections that are now standard to allow Smashing the stack in 2011. You should read these papers carefully. They will walk you through every aspect of overflowing the buffer, creating a shellcode, current protections implemented in modern operating systems, and ultimately exploiting a vulnerable program. It is very long and full of technical detail -- make sure to set aside plenty of time to work through it.
Several strategies have been created to counter these types of bugs in programs. The version of Linux installed on your machine employs two different kernel-based stack protection mechanisms, and one hardware-flag. One kernel protection randomizes stack addresses to make it difficult to predict locations of shellcode. The other places random canary values on the stack to protect stored addresses. The hardware flag used (NX bit) sets memory regions as non-executable, triggering a signal and the termination of the program. You need to disable all of these mechanisms or else it will be difficult to exploit overflows on your system.
To disable stack protection, you will need to write a value in a special system file (as root or running a script with sudo). We have provided scripts that do this called
enable-stack-protection. located inside the zip file referenced below. You should read these scripts to see how to enable and disable stack randomization. Note that if your system is ever rebooted, the
stack protections will be re-enabled by default.
To disable the placing of canary values in your executables, you need to use a gcc version that does not place canary values, or disable the functionality with the
-fno-stack-protector option. Again, we have automated this task during the compilation of the code you will be exploiting. Read the
Makefile shipped with the zip file below to see how to disable canary values.
Log on to your linux machine, and obtain the files needed for this lab:
wget http://strawman.nslab/lab1/lab1.zipand unzip them with
You will find a directory
lab1/which contains three files. The Makefile contains instructions to compile the C programs
lowercase.c. To compile the programs, simply type
lab1/directory. Make sure you don't compile the programs directly with gcc, as that will use gcc version 4 which inserts canary values by default. Examine both programs and discuss within your team what possible security issues exist with them.
Now, focus on
uppercase.c. This program takes a string in on
argvand prints out the same string with all ASCII lowercase characters changed to uppercase. Think about what would happen if a user provided a string longer than 512 bytes. Now, try it out. Run the program with a first argument that is much longer than 512 bytes.
gdbto run the program again with the same, very long, first argument (hint:
set args AAAAAAA...). When you run the program and it finishes, record the
%eipaddress reported when it segfaults. Compare this value to your string's ASCII hexadecimal representation (hint: man ascii). Now, repeatedly shorten/lengthen your string and re-run
gdbto determine the length of the shortest string needed to control
Set a breakpoint at
uppercaseagain with your shortest (but
argv. Once the breakpoint is reached, find the address of
Write a new program in C which exploits this stack-based buffer overflow to execute a shell. Your exploit should embed a shellcode in the
uppercaseand then overwrite the stack frame return address so that execution continues within that buffer when
main()returns. Note: since
bufwill get modified before
main()returns, you won't be able to jump into that buffer.
The shellcode you need to use is:
char scode = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89" "\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c" "\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff" "\xff\xff/bin/sh";
Once you have a working exploit which executes a shell, make
uppercasea setuid root program by running
chmod u+s uppercaseas root. Now, run your exploit again as a non-root user. Once your shellcode runs, use the
id(1)command to determine what user the shell is executed as. If everything works correctly, you'll have elevated privileges to root through this overflow.
Now take a look at
lowercase.c. Notice what happens to
argvbefore it is copied into
buf. If you try to exploit this overflow by placing your shellcode in
argv, why won't it work? Think about other locations your shellcode could be stored instead of
Write a new exploit for
lowercasewhich does not embed the shellcode in
argv. Instead, just repeatedly fill
argvwith the address where your shellcode resides. There's at least two places in
lowercase's address space where you can stuff your shellcode.
For this lab, your team must submit a report with the following information:
Submit all exploits with every line commented to demonstrate you understand how the exploits work.
Submit a diagram of the stack for
uppercasewhich includes the stack frames for
strcpy()and all local variables for
main(). Also, include the location of
main()'s return address and how it gets overwritten by an overflown
buf. Finally, include
argvin the diagram as well, relative to the stack frames. Exact offsets aren't that important here, but relative locations are.
How would you modify
lowercase.cto make them safe from buffer overflow attacks? Submit an answer to this question in the form a code diff of each program in the unified format. See the man page for
diff(1). Verify that your changes fixed the problem by running your exploits on the patched programs.
bufwere instead allocated via
malloc(3)in these vulnerable programs, would the same techniques for exploitation work? Why or why not?
What is different about executing a setuid program versus a non-setuid program? What are the dangers of setuid programs? Why are setuid programs sometimes necessary?
Find an advisory for a remote overflow vulnerability published within the last year on the web which has an associated, published exploit. The exploit must be in the form of source code, and should be designed to execute code on the remote system (you do not need to test this). In what scenarios can the vulnerability you found be exploited? Include links and references to an advisory on the vulnerability, the exploit itself, and any related information you find.
Your grade for this lab will be composed of:
25% - The source code of the exploits with appropriate comments.
25% - The stack diagram.
20% - The fixes for the vulnerabilities in both programs.
30% - The rest of the questions.