In this simple howto we will see how the execution flow of a program can be redirected, by exploiting a BOF (Buffer Overflow) vulnerability. If you are not familiar with this term or its use, please refer to the wiki page.
In this howto I will assume that all of you have (at least) a basic knowledge of:
If you don't meet one or more of the above ponts, I suggest you to gather more information
about them.
Few further notes before to begin:
Take a look at the following main:
As you can see, the purpose of the above code, merely is to take characters from
the keyboard, as long as an 'x' is entered, then counting the number of characters insterted, and finally
saying if such a number is even or odd.
Two considerations:
The buffer buf is thought to contain only 10 characters, but actually there is not any
control to enforce that. Here it is the BOF vulnerability!
In fact if we write more than 10 characters, the program will try to place all
of them in the
buffer buf, one by one. Not only 10. If the buffer is not enough large, the
overflowing characters will be written in the near memory locations.
Hence, let's try to pass to the program 50 chars.
Little note:
the expression: perl -e 'print "A"x50 . "x"'
literally says "print A 50 times
and append an x",
try in a shell to believe.
Furthermore we can leadly place the characters to write in a separate file, then
passing them to the program with: $./progr < file
This is going to work thanks to the operator of standard input
redirection (i.e. <).
So the steps to perform are:
$gcc -g -fno-stack-protector bof.c -o bof
$echo "`perl -e 'print "A"x50 . "x"'`" > expl
$./bof < expl
which gives us:
Numer of insterted character is even
[1] 17375 segmentation fault (core dumped) ./bof < expl
What we get is a message error saying to us that a segmentation
fault happened. This is happened because we have
written so many A letters that we have overwritten the
memory portions beyond the memory reserved for the buf variable.
Particularly the memory location reserved to contain the memory
address to jump when the return statement is executed, has been
soiled. And this fact has caused a fault. In fact the fault
happened when the return statement is executed
Let's take a closer look by using the gdb debugger (GNU debugger):
Let's see the program body, and let's fix a breakpoint just before the return statement.
Ok, now let's run the program giving to it the 50 'A's as showed before.
The program has stopped at the breakpoint.
Up to now the program it is not crashed yet. In fact,
as said before, it will crash during the execution of the
return statement. Not before.
So, let's see the assembly code:
As we can see the return statement is performed by the three
latest instructions. Particularly the purpose of the very last
one (retq) is to pop
up an 8 byte value from the the top of the stack and jumping into it.
Usually, at this point of a program execution, the top of the stack would
contain a valid address. So, let's skip two machine instructions
and see what actually the top of stack contains:
As we can see, the top of the stack contains a list of
apparently strange characters. If we are familiar with
the ASCII table, we will note immediately that what the
stack contains actually is a list of A!
So, what happened?
It merely happened, that we wrote a numer of A long
enough to
overwrite the stack starting from the location of the variable
buf, up to the place where the returning
address should had been placed.
Therefore when the retq statement is executed, the popped
address will be not valid and thus a fault will be thrown.
But, What would happen if we would write a valid address,
instead of a list of A?
Did you already guess, didn't you? The execution flow
will be redirect where the address says!!
So, let's do it!
First of all: we just need to write a number of chars
long enough to write the first location of stack, when
the retq will be executed. Therefore, as the above stack snippet
shows, 50 chars are too many.
If we look at the above snippet, we will see that we have
written 18 byte more. Hence the right length of our string
will be: 50 - 18 = 32 bytes.
Now, let's take the address of the function bof():
What we have to do now is to write 24 'A's and
a 8 byte long address.
In fact, even though the above address
is showed using only 3 bytes, we have to represent it
completely, hence on 8 bytes; hence: 0x0000000000400634.
Furthermore, my machine uses a little endian representation
(and probably also yours), hence the bytes of the address have
to be written using this correct representation.
Hence the address in the little endian representation, and written in bytes, became: \x34\x06\x40\x00\x00\x00\x00\x00.
From another terminal, let's modify the expl file writing in it the new string and let's launch again the program:
$echo "`perl -e 'print "A"x24 . "\x34\x06\x40\x00\x00\x00\x00\x00" . "x"'`" > expl
$./bof < expl
which magically gives us:
$./bof < expl
Numer of insterted character is even
WTF!
[1] 18534 segmentation fault (core dumped) ./bof < expl
In a nuthsell, what happened is:
So, this was the way to redirect an execution flow by exploiting a buffer overflow. Obviously this was a really useless example, I left you to think what we could do with this kind of vulnerabilities. Bye.