Level 4: Cyrenaicism (10 pts Extra Credit)
If you have completed the first four levels, you have earned 55 points (full credit). 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 the
exact saved value of %ebp
. 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.
For this level, we have gone the opposite direction, making the stack positions even less stable than they normally are.
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 %ebp
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, once again, to supply an exploit string that will cause
getbufn
to return your cookie back to test, rather than the value
1. You can see in the code for test
that this will cause the 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:
-
You can use the program
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 bovik
You must use the same string for all 5 executions ofgetbufn
. Otherwise it will fail the testing code used by our grading server. -
The trick is to make use of the
nop
instruction. It is encoded with a single byte (code0x90
). 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. -
You will need to restore the saved value of
%ebp
in a way that is insensitive to variations in stack positions.