Due: 5:00pm, Friday, November 18. Value: 25 pts. Submit to Sauron.
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.
The program we'll study is pass.s. 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.
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 is also included on your input.)
There are three useful aas commands worth learning.
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 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).
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 in the Applications
menu in the upper left corner of the screen, under the
Programming submenu; you can also start it from the command line using
bless &
.
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.
Via Sauron, you
should submit a typed report including 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.
This report should be a simple text file created using a text
editor such as gedit or jEdit — no OpenOffice or Microsoft Word
documents, please!