Exercise on Exploiting Overflows
The following program is available on a system you have user access to.
$ stat probe
File: 'probe'
Size: 6312 Blocks: 16 IO Block: 4096 regular file
Device: 13h/69d Inode: 42 Links: 1
Access: (4775/-rwsrwxr-x) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2015-04-01 16:20:00.000000000 +0300
Modify: 2015-04-01 16:20:00.000000000 +0300
Change: 2015-04-01 16:20:00.000000000 +0300
Birth: -
The program asks the user for a file path and tells them whether such a file exists. Its reach is not limited to files owned by or shared with the user. As the access flag s
indicates, the program is run with elevated privileges, because it must be able to inspect the whole system.
The program looks harmless, but has a bug that allows an apt user to take over the world system. Your task is to
- find the bug,
- use it for evil and
- fix it in your own version.
If you are incapable of evil, at least
- make the program crash and
- explain how someone could use it for more.
The source code of the program is shown below.
#include <errno.h>
#include <langinfo.h>
#include <limits.h>
#include <locale.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static int report_ascii(char const* const path,
bool const exists) {
return printf("The file '%s' %s!\n", path, exists
? "exists"
: "does not exist");
}
#define LSQM "\342\200\230"
#define RSQM "\342\200\231"
static int report_unicode(char const* const path,
bool const exists) {
return printf("The file " LSQM "%s" RSQM " %s!\n", path, exists
? "exists"
: "does not exist");
}
#undef LSQM
#undef RSQM
int main(void) {
int (* report)(char const*, bool);
char buf[_POSIX_PATH_MAX];
bool exists;
size_t size;
report = setlocale(LC_CTYPE, "") != NULL
&& strcmp(nl_langinfo(CODESET), "UTF-8") == 0
? report_unicode
: report_ascii;
if (system("echo What file would you like to probe?") == -1) {
perror("system");
return EXIT_FAILURE;
}
size = fread(buf, 1, PATH_MAX - 1, stdin);
if (ferror(stdin)) {
perror("fread");
return EXIT_FAILURE;
}
if (size > 0 && buf[size - 1] == '\n')
buf[size - 1] = '\0';
else
buf[size] = '\0';
if (access(buf, F_OK) == -1) {
if (errno != ENOENT) {
perror("access");
return EXIT_FAILURE;
}
exists = false;
} else
exists = true;
if (report(buf, exists) < 0) {
perror("report");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
The program is available as an ELF 64-bit LSB executable for x86-64 architectures.
If you want to compile the program yourself, make sure to turn off all the analysis, optimization and security features your compiler has to offer. They can modify the program in unexpected ways and make the exercise difficult or impossible.
For example GCC 4.8.2 can be instructed as follows.
$ gcc -D_GNU_SOURCE -O0 -fno-stack-protector -o probe -std=c99 -w probe.c
See its manual for unnecessary details.