La semana pasada os planteaba un pequeño reto en forma de típico crackme para obtener la clave secreta a través de la ingeniería inversa de un ejecutable. Ese ejecutable realmente era un pequeño script en Perl convertido mediante Perl2exe, una vieja utilidad de IndigoStar Software muy útil pero que no debe utilizarse para proteger código dado que es muy fácil decompilarse o, mejor dicho, permite extraer el código perl fácilmente.
Antes de pasar a la solución al reto comentar que el script en Perl utiliza Perl/TK. Si no lo conocíais, deciros que Tk es un GUI desarrollado por John Ousterhout inicialmente como una extensión de Tcl (Tool Command Language) y que fue portado a Perl por Nick Ing-Simmons, un gran contribudor de Perl, famoso sobretodo por los módulos Tk y Encode y que murió en septiembre de 2006 de un ataque al corazón. La clave secreta del crackme es 'NickIngSimmons' y este reto es un pequeño homenaje a él y a todos aquellos que contribuyen en este tipo de proyectos de forma desinteresada y colaborativa.
Dicho esto, hablemos de los participantes del reto. Esta vez nuestro ganador es de Colombia y se llama Fernando Quintero (nonr00t'S stuff (uid != 0)) que arrebató el primer puesto al también colombiano Daniel Correa (sinfocol) por tan sólo ¡¡¡18 minutos!!!. Mención también a Adrián Ruíz Bermudo (funsec@pwned:~$) que fue capaz de resolver el reto y se acordó de Nick. A todos ellos y a todos los que habéis intentado resolver el reto muchas gracias, sois los mejores.
A continuación os mostramos el solucionario de Fernando Quintero. Veréis que explica la forma en qué fue pensando para llegar a solucionarlo, muy completo y pedagógico. También os recomendamos ingresar en su grupo http://groups.google.com/group/ctf-colombian-team donde se comparte información sobre los wargames en general.
Sin más, os dejamos con el write-up:
nonroot: Lo primero que hacemos es descargar el archivo haciendo click sobre la imagen que aparece del reto en http://www.hackplayers.com/2012/09/reto-16-otro-crackme-cualquiera.html.
Usando el comando file nos dice que se trata de un archivo gz:
hpys_crackme_16.exe.exe.gz: gzip compressed data, was "recursive", from FAT filesystem (MS-DOS, OS/2, NT)
Al aplicar varias veces el comando gunzip sobre el archivo, sigo obteniendo el mismo mensaje así que mejor lo abro con un editor hexa y observo que el archivo ha perdido sus datos.
Abro nuevamente el archivo original con el editor hexadecimal y veo el archivo completo, así que en algún momento el usar el gunzip sobre este sistema operativo y con ese archivo hace que solo se quede la cabecera del gzip y se pierdan los datos.
Lo que hago entonces es editar el archivo y buscar el header para el archivo ejecutable de windows (algo que empiece con 4D 5A 90 …) y corto el archivo desde ahí, lo renombro a hpcrackme16.exe y verifico su hash para validar que haya quedado correcto:
nonroot@localhost$ file hpcrackme16.exe
hpcrackme16.exe: PE32 executable for MS Windows (GUI) Intel 80386 32-bit
nonroot@localhost$ md5sum hpcrackme16.exe
0cd62abe712b8f7ce7196fddaaa78255 hpcrackme16.exe
nonroot@localhost$
Para trabajar mejor uso el sistema operativo windows, donde tengo herramientas que ayudan a este trabajo.
Como confiamos en Vicente y sabemos que es un crackme y no malware, lo ejecutamos y aprendemos como funciona. Aparece una caja de texto para ingresar una palabra clave.
y si en 5 intentos no la adivinamos, entonces se deshabilita el botón de prueba y se acaba el juego.
¿Qué hacemos entonces?, adivinamos la clave ejecutando el programa una y otra vez? o buscamos algún otro recurso que nos ayude a hacerle seguimiento al programa de forma que podamos entender cual es la clave que espera?
Abrimos el programa con el immunity debugger (https://www.immunityinc.com/products-immdbg.shtml) y unas líneas hacia abajo del código podemos ver unos llamados a "mkdir" y "rmdir"
El debugger nos permitirá una ejecución paso a paso para entender mejor donde aparece nuestra clave.
Ponemos un BreakPoint (tecla F2) en la instrucción donde se llama a mkdir y veremos que la dirección de memoria donde se pone el BP se pinta de color azul claro, ahora podemos ejecutar el programa (tecla F9). El programa inicia y debería detenerse cuando llegue a la instrucción 00401f06. En este punto nuestro registro EAX debería contener un string que apunta al directorio que se va a crear.
Nuestro crackme esta creando un directorio en la ruta temporal del usuario local con el que estamos ejecutando. veamos que se va a crear en este punto. Para lograr que se ejecute la rutina del mkdir debemos avanzar un poco mas hasta alcanzar la dirección 0040114e
Cuando entramos al directorio, nos damos cuenta que no hay nada aún, esto es porque solo hemos ejecutado unas pocas instrucciones del programa y solo hemos logrado que cree el directorio, si queremos ver que sucede en este directorio ejecutamos el programa de forma continua.
El programa se ejecuta normalmente pero en el directorio temporal aparecen archivos .dll y un directorio TK con algunos archivos .xmp. Esta librería TK le da soporte a gráficos para lenguajes de scripting, mas información: https://es.wikipedia.org/wiki/Tcl
Lo que deducimos de aquí es que por alguna razón el crackme esta usando dichas librerías para crear la caja de texto.
En este punto quise revisar los strings del programa ejecutable para hacerme una idea de que funciones o palabras claves podía encontrar. Cuando ejecuto una línea así:
nonroot@localhost$ strings -a -12 hpcrackme16.exe
Encuentro muchas referencias a funciones de windows y muchas otras de perl, por ejemplo: ...
Perl_uvchr_to_utf8_flags
Perl_uvuni_to_utf8
Perl_uvuni_to_utf8_flags
Perl_vivify_defelem
Perl_vload_module
Perl_vnewSVpvf
Perl_vwarner
Perl_warn_nocontext
Perl_warner_nocontext
Perl_whichsig
Perl_win32_init
Perl_win32_term
boot_DynaLoader
boot_Win32CORE
init_Win32CORE
perl_alloc_override
perl_alloc_using
perl_clone_host
perl_clone_using
perl_construct
…
Así que el programa ejecutable tiene algo que ver con perl también.
Decido entonces usar IDA (http://www.hex-rays.com/products/ida/index.shtml) para analizar un poco mejor el programa de forma estática.
El programa cargado en IDA me muestra algo muy simple, se carga (LoadLibraryA) el nombre de una librería dll (p2x588.dll) y luego se invoca la función RunPerl, luego termina la ejecución y eso es todo. En ese corto camino obtiene la dirección del nuevo proceso cargado, por lo que pensamos que lo que hace el programa es continuar la ejecución en un nuevo proceso en el momento de ejecutar RunPerl.
Volvemos al debugger y reincidamos el programa, observamos que el nombre del directorio en la función mkdir ya cambió, este nombre de directorio cambia cada vez que ejecutamos el programa. tratamos de llegar a la parte que vimos en IDA, ejecutando la tecla F8 y alcanzamos el lugar donde se carga la librería.
Si seguimos el programa despacio podemos encontrar el llamado a la función RunPerl con una instrucción CALL EAX en la dirección 00401234. Podemos poner un breakpoint en este sitio para llegar mas rápido la próxima vez.
Si en este punto verificamos el directorio que se crea solo encontraremos la DLL que acaba de ser cargada, por lo que entendemos que es allí donde están las rutinas principales del nuevo programa que se ejecutará. Con las palabras claves p2x588.dll y RunPerl hacemos una búsqueda en Internet que nos lleva a concluir que el crackme es un ejecutable construido a partir de un código perl usando el programa Perl2Exe, el manual de funcionamiento esta en:
http://www.indigostar.com/pxman.html
Un vector de "ataque" para el crackme es descubrir que hace la librería que encontramos y tratar de identificar cuando las funciones de perl son llamadas.
Cargamos la librería p2x588.dll que se genera en el directorio en IDA y lo primero que nos llama la atención son unas referencias a UPX0 y UPX1, así que la librería puede estar empaquetada, para comprobarlo usamos la herramienta que se descarga de: http://upx.sourceforge.net/
Al comprobar que esta empaquetada simplemente la desempaquetamos con la opción -d y nuevamente la cargamos en el IDA, con lo que aparecen muchas mas funciones (verificar las pestañas de Functions, Exports e Imports).
Ubicamos la función RunPerl que empieza en la dirección 2809603D que es el punto de entrada para el nuevo proceso
Paso a paso vamos entendiendo el programa desde este nuevo punto de entrada y observamos que hay una rutina antes de cargar las rutinas de ambiente (environment), esta rutina (sub_28091681) esta en la dirección 28096051
Si leemos un poco sobre esta rutina verificará los switchs con los que va a ser llamado el script de perl y llamará a las rutinas de desempaquetado de los archivos que requiera para su ejecución. Si recordamos el comportamiento que habíamos visto anteriormente, nos dimos cuenta que solo los archivos .dll fueron desempaquetados y creados en el directorio, entonces la idea en este punto es tratar de desempaquetar los archivos fuentes de perl (.pl) para tratar de entender el crackme desde el código fuente.
Lo que hice en este punto fue entender que habían una serie de comprobaciones con las extensiones de los archivos, en la lista se encuentran archivos .ini, .xpm, .so, .lib, .png, .exe, etc.
Todas las extensiones posibles se definen a partir de la dirección: 280A4B00
Sigo paso a paso en el debugger y lo que aparece es que se carga una lista de nombres de archivos con diferentes extensiones acompañados de sus tamaños (variable SIZE) y de una variable ENC que indica si esta cifrado o no, esto es porque el perl2exe cifra los nombres y contenidos de los archivos para protegerlos y los descifra en tiempo de ejecución.
Si queremos obtener un archivo en particular podemos hacerle el seguimiento y luego hacer que ingrese a la rutina de descifrado para que pueda ser volcado al disco en el directorio temporal que se crea.
El nombre del archivo se obtiene con la rutina : 280925FB llamada desde 28091c8c
Asi que una instrucción mas abajo podemos poner un breakpoint (28091c91) para verificar los nombres que aparecen en el registro EAX. Vamos dandole F9 para ir leyendo cual archivo esta leyendo.
La lista de archivo inicia con: p2x_pre_exec_message
Luego continua: p2x_pre_exec_message, p2x_exec_command, p2x_info.pm, y en quinto lugar aparece un archivo interesante "_main.pl" por lo que podría ser el script principal en perl, así que seguimos lentamente esta ejecución (Tecla F7).
Observamos que hay una instrucción JNZ 28091BDE después de cada comparación con la extensión, así que lo que el programa esta haciendo es comparando si esa extensión será volcada o no, en nuestro caso vamos con la extensión .pl por lo que los saltos no se tomarán.
Que sucede si le decimos al programa que SI tome el salto?, para eso editamos la FLAG Z que es la que condiciona el salto, esta flag se encuentra en la ventana de registros y la cambiamos haciendo doble click sobre el valor 1 que tiene actualmente y lo ponemos en 0.
Lo que vemos que hace, es que entra a una rutina donde crea el archivo y hace el volcado del mismo.
Verificamos en el directorio temporal y observamos que tenemos el archivo _main.pl
Podemos repetir el procedimiento para cada archivo que queramos obtener, podemos entender el proceso de descifrado de los archivos, podemos configurar las variables de entorno para lograr que nos muestre lo que queremos, podemos extraer la rutina de descifrado y construir un dumpeador, todo esto queda como ejercicio, si lo hacen, lo comparten por favor ;)
Una rutina interesante esta en: 280925FB
Una vez tenemos el archivo lo abrimos con un editor de texto plano y observamos que efectivamente contiene el código fuente de la aplicación.
nos concentramos entonces en las comparaciones que realiza la aplicación:
y entendemos que la comparación que nos da la respuesta correcta es:
if ($secret eq 'x[RYzZTg\_^[[A5345') {
$texto = "Well done. Congratz!";
Con esto solo nos queda validar la entrada para que sea igual a lo que queremos:
$secret = $secret ^ '621234345234525345';
Como se puede observar es una operación XOR, así que realizamos un XOR entre los dos valores conocidos para determinar cual debe ser la entrada de la aplicación:
nonroot@localhost$ cat respuesta.pl
#!/usr/local/bin/perl
use strict; use warnings;
my $secret="x[RYzZTg\\_^[[A5345";
my $final = '621234345234525345' ^ $secret;
print $final."\n";
nonroot@localhost$ perl respuesta.pl
NickIngSimmons
nonroot@localhost$
La respuesta es: NickIngSimmons
Al ingresar esta respuesta en la caja de texto de la aplicación obtenemos:
Eso es todo, espero que se haya entendido el procedimiento, lo realice tal como lo hice la primera vez, luego ya se puede entrar a profundizar en el funcionamiento del empaquetador/desempaquetador y las rutinas de cifrado.
Un saludo para todos los seguidores de hackplayers y muchas gracias a Vicente por el reto!
Hasta la próxima.
Excelente solución, me imagino que los otros dos competidores dieron soluciones distintas, hay forma de que publiquen estas otras Hackplayers? XD
ResponderEliminarSi, seria bueno que tambien los otros dos ganadores publicaran su solucion :P
ResponderEliminarLas otras soluciones son similares: obtener el código perl colocando un punto de interrupción en un par de "_main.pl" (Adrián) o cambiando una de las extensiones de la instrucción que hace una comparación por .pl (Daniel).
ResponderEliminarAtentos a sus blogs por si deciden publicar también el solucionario.
Saludos!
me podrian ayuda r a descargar el archivo
ResponderEliminar