Buffer Overflow Attacks
Buffer Overflow Attacks
The heap is potentially large and complex, and so there are different
types of vulnerabilities: there are heap overflows, use after free and
double free vulnerabilities. In these series of blogs I’ll cover all the
previously mentioned heap vulnerabilities, starting off with heap
overflows.
Example C Code
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
struct data {
char name[64];
};
struct fp {
int (*fp)();
};
void winner()
printf("level passed\n");
void nowinner()
struct fp *f;
d = malloc(sizeof(struct data));
f = malloc(sizeof(struct fp));
f->fp = nowinner;
strcpy(d->name, argv[1]);
f->fp();
First, let’s start by walking through the code. I find it helps when you
first just read the code and understand it, without looking for
vulnerabilities. Right off the bat, we have two structs: data which
has a character array of 64 bytes, and fp which has a pointer to a
function.
Now if you’ve got a keen eye, you might have already spotted the
developer’s mistake. If you didn’t, it’s explained in the next section.
An exploitation example
Now when exploiting a program, it almost goes without saying, but
the first thing to do is run it. We’ve seen the code, but let’s see the
program in action as it will help us in the next steps.
This image provides us a look at the heap with these given inputs.
Since in the first run we don’t provide an input it is empty, so when
strcpy is called it points to nothing and thus a segmentation fault
occurs. In the second scenario, strcpy successfully copies the first
argument given to the program, which is argv[1]. argv[2] is never
used as the second argument is just ignored by the program.
Luckily we’ve read the code and know that the first argument is
being passed to an unchecked buffer of 64 bytes, so we start by
giving it 64 characters and see what happens:
Fuzzing the input of the program
I added some “U”s to see what would happen and it seemed the
segfault address changed. That’s a bit strange. To double check, I
tried entering a single “U” and it still added a 55 to the segfault
address (0x55 in ASCII is “U”). Seeing as this is the point where it is
overwriting the addresses, we’ll mark it down as important.
Something else that we notice is the segfault is only raised after the
program has printed the data about the struct pointers. We also
notice that it stopped printing “level not passed”. Thus we can
conclude that the memory space that the “T”s and “U”s are taking up
are corrupting the address to which fp is pointing.
What the heap looks like now that the buffer is overflowed
The reason the segfault occurs when we get to the “T”‘s is because
we are overwriting the information at the beginning of the block. We
can now mess around with the heap since we’re leaking the memory
and get it to output different results.
Seeing as the character “U” changes the segfault address, I’ll replace
those characters with our winner() address. Now if this was done in
the wild an attacker would place the address of their malicious code
here. Let’s see where the winner() function is located. By
doing disass winner gdb will disassemble the function and print the
address of the first instruction. Let’s go ahead and overwrite the “U”
with the hex address of the first instruction in the winner() function.
Exploiting the program
Now we can see that I ran the function for the address a little bit
differently. In order to pass the hex address we need to insert it
using echo -ne. To pass the command, we need to use backticks so
we open the string, enclose it in backticks, and pass the echo
command our custom string. We separate each hex byte with “” to
identify it as hex. Lastly, the reason we write the address back to
front is because it works in little endian notation. Knowing which
endianness to use is always important when exploiting a function, in
this case I tried little endian on my first time and it worked.