This is the source code for level03.c:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
void memcpy(void dest, const void *src, size_t n);
Copies n bytes from memory area src to memory area dest. The memory areas must not overlap.
The function does not check for any terminating null character in src.
Returns a pointer to dest.
void memset(void buf, int value, size_t count);
buf = Pointer to the block of memory to fill.
value = Value to be set. The value is passed as an int, but the function fills the block of memory using the unsigned char conversion of this value.
count = Number of bytes to be set to the value.
Sets the first count bytes of the block of memory pointed by buf to the specified value
Returns a pointer to the memory area buf
The most common use of memset() is to initialize a region of memory to some known value.
Running the program we see this:
So we know the bad function address is at 0x80484a4 and the good function address is at 0x8048474. A function pointer is set to point to the address of the bad function. The program checks for an argument that is at least 4 in length. Then memset sets all except the last 4 bytes of the buffer to 0. There is a buffer overflow in how the program copies the argument to the buffer, without checking for boundaries. This will be key in exploiting the binary.
I proceeded through feeding a string to the program that I created with
pattern_create.rb. Then I ran the program with GDB:
So EIP points to some junk that I provided with the string. Let’s check the offset:
Excellent! Since we already have the address we need for the good function that will spawn us a shell, the next step is simple:
1 2 3 4 5 6 7
We hijacked the execution flow and made EIP point to the address of the function we needed.
The surest protection against temptation is cowardice.
— Mark Twain