If you have completed the first four levels, you have mastered the principles of the runtime stack operation, and you have gained firsthand experience with buffer overflow attacks. We consider this a satisfactory mastery of the material. You are welcome to stop right now.
The next level is for those who want to push themselves beyond our baseline expectations for the course, and who want to face a challenge in designing buffer overflow attacks that arises in real life. This part of the assignment only counts 10 points, even though it requires a fair amount of work to do, so don't do it just for the points.
From one run to another, especially by different users, the exact stack
positions used by a given procedure will vary. One reason for this
variation is that the values of all environment variables are placed near
the base of the stack when a program starts executing. Environment
variables are stored as strings, requiring different amounts of storage
depending on their values. Thus, the stack space allocated for a given
user depends on the settings of his or her environment variables. Stack
positions also differ when running a program under gdb, since
gdb uses stack space for some of its own state.
In the code that calls getbuf, we have incorporated features
that stabilize the stack, so that the position of getbuf's
stack frame will be consistent between runs. This made it possible for
you to write an exploit string knowing the exact starting address of
buf and return address of the function. If you tried to use
such an exploit on a normal program, you would find that it
works some times, but it causes segmentation faults at other times. Hence
the name "dynamite"—an explosive developed by Alfred Nobel that
contains stabilizing elements to make it less prone to unexpected
explosions.
For this level, we have gone the opposite direction, making the stack positions even less stable than they normally are. Hence the name "nitroglycerin"—an explosive that is notoriously unstable.
When you run bufbomb with the command line flag "-n," it
will run in "Nitro" mode. Rather than calling the function
getbuf, the program calls a slightly different function
getbufn:
int getbufn()
{
char buf[KABOOM_BUFFER_SIZE];
Gets(buf);
return 1;
}
This function is similar to getbuf, except that it has a buffer
of 512 characters. You will need this additional space to create a reliable
exploit. The code that calls getbufn first allocates a random
amount of storage on the stack (using library function alloca)
that ranges between 0 and 255 bytes. Thus, if you were to sample the value
of %esp during two successive executions of
getbufn, you would find they differ by as much as [-127, 127]
bytes.
In addition, when run in Nitro mode, bufbomb requires you to
supply your string 5 times, and it will execute getbufn 5
times, each with a different stack offset. Your exploit string must make
it return your cookie each of these times.
Your task is identical to the task for the Dynamite level. Once again,
your job for this level is to supply an exploit string that will cause
getbufn to return your cookie back to test (note a new
testn function), rather than the
value 1. This will cause the test program to go "KABOOM!" Your exploit
code should set your cookie as the return value, restore any corrupted
state, push the correct return location on the stack, and execute a
ret instruction to really return to test.
Some Advice:
hex2raw to send multiple copies
of your exploit string. If you have a single copy in the file
exploit.txt, then you can use the following command:
unix> cat exploit.txt | hex2raw -n | bufbomb -n -u bob:jane
You must use the same string for all 5 executions of getbufn.
Otherwise it will fail the testing code used by our grading server.
nop instruction. It is
encoded with a single byte (code 0x90). You can place
a long sequence of these at the beginning of your exploit code so
that your code will work correctly if the initial jump lands anywhere
within the sequence.