CSci 230: Computing Systems Organization
Home Syllabus Readings Assignments Tests

Assignment 11: The worm

Due: 5:00pm, Friday, November 14. Value: 30 pts.

Overview

One of the most common security flaws in programs is the buffer overflow error, in which a program carelessly allows a user to write beyond the end of an array, into space used by the program for other purposes. A user can exploit such a bug by altering memory in ways unanticipated by the programmer; if the user exploits it right, the user can trick the software into doing things it was never written to do.

One of the earliest and most famous exploits of a buffer overflow error is the Great Internet Worm, which in 1988 managed basically to shut down the Internet for 20 hours. This program spread from computer to computer using a buffer overflow error from a service on Unix computers called finger, which allows other computers to ask who is logged on that computer. (For example, “finger @ozark” lists everybody logged in on ozark.) To support this, Unix computers continuously run a program called the finger daemon that waits for requests to handle. Unfortunately, early versions of the daemon had a buffer overflow bug in reading requests, which could be exploited to send foreign code to a computer for execution. Somebody exploited this bug on November 2, 1988, and the exploit tricked computers into executing code that would send the same request to finger daemons on other computers (which would send that same request to others,…). The number of computers executing this code quickly ballooned and overwhelmed all other Internet traffic.

But buffer overflow errors are not ancient history. A more recent example comes from mid-August 2003, when the Blaster Worm exploited a buffer overflow error in Windows 2000 and Windows XP.

Note: Deploying worms is both illegal and immoral. This assignment's purpose is not to teach you how to write worms; it is for you to understand how computers work with subroutines, and it emphasizes the pitfalls of allowing a user to write beyond the end of an array.

Understanding the code you'll attack

The program we'll study is pass.s [Link]. You should not modify this program! But you will need to understand the first 21 lines of it, listed below.

 1main        MOV SP, #0xf00
 2            BL ttyStart
 3            BL getPassword          ; retrieve password
 4            ADD R1, PC, #fail       ; regardless of password, display "You fail.\n"
 5            BL printStr
 6halt        B halt
 7
 8part2       ADD R1, PC, #succeed    ; this code is never reached
 9            BL printStr
10            B halt
11
12            ; Retrieves password entered by user
13getPassword STMDB SP!, { LR }
14            SUB SP, SP, #16         ; allocate 16 bytes on stack
15            MOV R1, SP
16            BL readLine             ; read line into 16 bytes
17            ADD SP, SP, #16         ; deallocate 16 bytes
18            LDMIA SP!, { PC }
19
20fail        DCB "You fail.\n", 0
21succeed     DCB "You passed Part 1!\n", 0

You can see that after initializing the stack and terminal, the main routine calls the getPassword subroutine (line 3). The getPassword routine reads a line from the user (line 16) and then returns (line 18). Then main continues by displaying the phrase “You fail” regardless of the entered “password.”

Confirm that the program works by executing the program and then typing, say, “friend” as your password. Verify that its behavior matches what you would expect from the code as listed above.

Part 1: A simple buffer overflow exploit

The pass.s program has a crucial vulnerability, however: The getPassword routine allocates a 16-character array on the stack and then uses readLine to fill the array. But if the user happens to enter a password with more than 16 characters, then readLine will actually write past the end of the array, overwriting other information on the stack.

Your assignment is to find an input that exploits this vulnerability and causes the program to display “You passed Part 1!” In particular, you want to construct an input that ends up overwriting the LR value stored on the stack by line 13: This would normally be the address of line 4, the line following the call to getPassword. But instead, you want it to be the address of line 8.

(The readLine subroutine will continue reading bytes until it reads a newline (ASCII 10(10)). You'll want to make sure this newline character is included on your input.)

There are three useful aas commands worth learning if you haven't already.

break NUM

Adds a “breakpoint” for the code corresponding to the line numbered NUM in the source code. After adding a breakpoint, you can enter “step 1m”, and aas will stop whenever it reaches a breakpoint (or when it has actually executed 1,000,000 steps).

I recommend using a breakpoint of line 17. This way, you'll be able to see the state of the program just after readLine has returned.

list ADDR0 ADDR1

List the memory contents between addresses ADDR0 and ADDR1, both of which should be expressed in hexadecimal. This can be used both for listing the assembled program (as in list 0 80) or for listing the stack contents (as in list e80 f00).

input FILENAME

Causes the terminal to extract its input from the indicated file. You'll need to use this because the input you'll want to use is likely to be something you couldn't easily type using the keyboard.

To create the file containing your input, you'll want to create a file using a hex editor. A hex editor allows you to view and edit the exact contents of a binary file, where each byte in the file is displayed as two hexadecimal digits. Many hex editors are available; for Windows computers, you might try HxD. On the laboratory computers, I recommend bless, which you can find by clicking the Ubuntu circle icon in the screen's upper left corner and then typing bless; alternatively, you can also start it from the command line using “bless &”.

Part 2: A more complex exploit

Now get the program to display “You've passed Part 2!” To do this, you want to fool the getPassword routine so that it actually “returns” into code that readLine has read into the array on the stack: This will be code taken from the input file, not from the original pass.s program. This code will alter the '1' byte in succeed to be '2' instead, and then it would set the PC to be the address of the part2 instruction. My simple solution to this involves four instructions.

After you figure out the sequence of instructions you want the computer to execute, you're faced with trying to construct the corresponding machine code, so that you can then build your input file. In fact, you can use aas to do this: Create a file with the relevant assembly instructions, load it into aas, and then tell aas to list the contents of memory from 0x00 to 0x20. These contents form the bulk of what you want to place into your file.

What to submit

You should submit a typed report in ASCII format: Use a text editor such as gedit or jEdit — no OpenOffice or Microsoft Word documents, please! Your report should include the two attack “passwords,” written in hexadecimal, as well as at least one English paragraph for each explaining in your own words how it works. The quality of your English description matters.