martes, 21 de abril de 2009

La gorda FOCA

Todo empezó cuando me liaron con una aplicación destinada a extraer y eliminar los metadatos de documentos OpenOffice, OOMetaExtractor.

“La cosa” se fue descontrolando, al poco tiempo ya trataba documentos de Office desde la versión 97 a la 2007 y mas tarde PDFs. Pero la cosa nostra, no quedó ahí. Pensamos que sería cómodo que buscara y descargara documentos usado los buscadores Google y LiveSearch. Y así se hizo.

Una vez que la bola de nieve empieza a rodar…no hay quien la pare. El programa pintaba bien, así que requería una buena interfaz, iconos para todo, menús con todo tipo de opciones, colores y sabores.

pdfMirad cuantos colorines !!!

¿Y qué es de un programa sin un buen nombre? Como todo buen nombre primero se eligen las siglas, que sean impactantes, por ejemplo FOCA, un nombre fácil de recordar aunque no demasiado descriptivo... El significado de las siglas vino, creo recordar, acompañado de un buen vino , Fingerprinting Organizations with Collected Archives.

Aun quedaban mejoras por hacer. Pensamos que sería cómodo bajarse todos los documentos de una web, por ejemplo la casa blanca, sacar todos los usuarios de los metadatos, ponerlos todos juntos para guardarlos en un fichero y usarlos por ejemplo para hacer fuerza bruta o guardar los emails para “anunciar” nuestra potenciada viagra > Mas código, mas menús, mas ventanas.

Durante todo el proceso, Chema Alonso y Enrique Rando probaron insaciablemente todas las opciones y examinaron los metadatos extraídos proponiendo muchas mejoras y metadatos extra. Así que se fueron incorporando las versiones antiguas, el historial de edición y la información EXIF de las imágenes incrustadas > Mas código, mas menús , mas ventanas.

doc2La cosa apunto de explotar

Ya por último decidimos que sería interesante coger todos los documentos editados por un mismo usuario y juntar los metadatos considerándolos del mismo equipo, así poder interpretar mas fácilmente toda la maraña de metadatos obtenidos. Así surgió la opción Analize Metadata > Mas y mas código.

La cosa llego muy lejos, hace unos días Chema Alonso y Enrique Rando hablaron en la BlackHat Europe sobre la herramienta y los metadatos. Y con motivo de tan especial ocasión la FOCA fue liberada: Descarga, necesitáis .NET Framework 3.5, preferiblemente en Windows Vista o Windows Server 2008 .

Manual de usuario (en construcción) (las imágenes fueron robadas de él): FOCA, manual de usuario I de IV FOCA, manual de usuario II de IV FOCA, manual de usuario III de IV FOCA, manual de usuario IV de IV

Mas información relacionada

Tened cuidado, suele morder a los desconocidos.

jueves, 16 de abril de 2009

¿Fallo de programación en mod_evasive?

mod_evasive es un módulo de apache que sirve para “esquivar” ataques de denegación de servicio. Detecta cuando un cliente está realizando excesivas peticiones y bloquea temporalmente su acceso o realiza otra acción configurable.

Hay muchos “manuales” para instalar y configurar mod_evasive. Lo mas importante son los valores configurables en httpd.conf para definir su comportamiento, por defecto viene configurado así:

DOSHashTableSize 3097 DOSPageCount 2 DOSSiteCount 50 DOSPageInterval 1 DOSSiteInterval 1 DOSBlockingPeriod 10

Significando de DOSPageCount y DOSPageInterval, sacado de emezeta.com:

  • DOSPageCount / DOSPageInterval: Máximo (umbral) que se debe alcanzar para ser incluído en la lista de bloqueados. En este caso el objetivo será una página concreta. (Entiendase como «Máximo de DOSPageCount páginas en DOSPageInterval segundos»).

    Por defecto el máximo está establecido a 2 páginas por segundo.

Genial, es muy útil, aunque no es raro que un cliente legítimo realice en un momento puntual 2 peticiones a una misma página en menos de un segundo, abriendo pestañas, algún script de recarga, o un php de acceso a imágenes…

Me parece mucho mas adecuado que bloquee una IP cuando ese índice de peticiones por segundo se perpetua en el tiempo. Por ejemplo 20 peticiones en 10 segundos, 2 peticiones por segundo durante 10 segundos (si, aprendí a multiplicar recientemente).

Así que establecí los siguientes valores en la configuración de mod_evasive:

DOSPageCount 20 DOSPageInterval 10

apache reload y a gozar.

Al rato vi como se estaban bloqueando IPs que en principio no deberían ser bloqueadas. Verifiqué el access_log de apache y efectivamente, estaban realizando como mucho 5 peticiones cada 10 segundos. Un valor 4 veces menor(incluso sé dividir), muy lejano al máximo de 20 peticiones cada 10 segundos que hemos configurado. Lo verifiqué manualmente y en efecto, realizando una petición cada 5 segundos a las 20-21ava petición mod_evasive me bloqueaba.

Probé con diferentes valores, rango mas amplios, reiniciar apache, reinstalar y nada, esa cosa no funcionaba como decía. Maldita sea !

Estaba claro, yo no estaba entendiendo bien que significaban esos valores, porque sería muymuy raro que un módulo tan usado fallase en algo tan básico, ¿no? Así que me dispuse a leer la documentación oficial, el mítico README, que decía mas o menos lo mismo que lo que pegue arriba. Así que se me ocurrió la locura de echar un ojo al código fuente: “quizás ahí este explicado o vea una formula que explique su funcionamiento”.

El código, localizado en mod_evasive20.c, se puede entender, al menos la parte donde hace la verificación de si una IP está atacando o no.

image

Resumen de los valores de las variables al realizar la comprobación:

  • t: Tiempo actual en segundos. Inicializado como: time_t t = time(NULL);
  • n->timestamp: n es una tabla que se usa para relacionar URL-Numero de peticiones-Timestamp. timestamp es una “marca de tiempo” que se establece en la primera petición y es actualizada cada DOSPageInterval segundos, en nuestro caso cada 10 segundos.
  • page_interval: Tiene el valor establecido en httpd.conf, DOSPageInterval. Intervalo en segundos durante el cual se medirá si se ha alcanzado el límite de peticiones. Como acabamos de decir, 10 segundos en nuestro caso.
  • n->count: Número de peticiones realizadas a una misma página web, el valor se resetea cada DOSPageInterval segundos, en nuestro caso cada 10 segundos.
  • page_count: Tiene el valor configurado en httpd.conf, DOSPageCount. Máximo número de peticiones que se puede realizar a una misma página web. En nuestro caso 20.

Así podemos ver como la primera parte de la condición mira si desde el timestamp hasta el momento actual han transcurrido menos de 10 segundos, si esto ocurre pasa a la segunda parte de la condición que verifica si el número de peticiones realizadas es mayor que el máximo numero de peticiones permitidas.

Todo perfecto en teoría, pero a mi me funcionaba mal, así que hice un debug chapucero usando el macro LOG que añade un mensaje al log del sistema /var/log/syslog.

image

Recompilamos ./apxs -cia mod_evasive20.c Recargamos la configuración de apache /etc/init.d/apache reload

Y empezamos a hacer una petición cada 5 segundos mientras miramos el syslog tailf –f /var/log/syslog | grep mod_evasive

image *Paint art

Como esperábamos cada 5 segundos se realiza una petición, hasta alcanzar las 6 peticiones de un máximo de 20. Pero esto sigue hasta que…

image

Pero como es posible, está ****** nos banea con 20 peticiones en 200 segundos !!! El error parece estar en que nunca resetea el n->count y en teoría cada 20 segundos debería resetearlo. Veamos en el código en que parte lo resetea…

image

Cuando el tiempo actual en segundos, t, menos la marca de tiempo inicial, n->timestamp, es mayor que el intervalo de tiempo, 10, se resetea n->count. Es decir, una vez pasados los 10 primeros segundos debería resetear el contador n->count, pero en los “logs” vemos que hay un problema, n->timestamp se actualiza constantemente con el valor de la anterior petición (Señalado en azul).

La idea es que este timestamp se actualice solo cuando el contador se pone a 0

image *Paint genius

Recompilamos, reiniciamos y vemos el log

image

Esto ya tiene mejor pinta, el contador n->count se resetea cada 10 segundos y el valor n->timestamp también. De modo que ahora si, solo bloqueará a aquellas IPs que hagan mas de 20 peticiones en 10 segundos.

Este mismo cambio hay que realizarle otra vez en la comprobación de páginas del sitio visitadas, situado unas pocas lineas mas abajo.

He intentado contactar con el autor de este modulo, para que me verificase que era un fallo y no una mala interpretación mía. Pero el modulo es de hace 4 años así que posiblemente el autor haya cambiado de correo.

mod_evasive es muy conocido y usado, es increíble que nadie haya notado que no funciona como debe o que no se hayan mirado el código fuente, de ahí mis dudas sobre si no estaré equivocado yo. Aunque por otra parte en la configuración por defecto se usa un valor de DOSPageInterval de 1 y entonces la continua actualización de n->timestamp no entorpece el resto de comprobaciones, ya que solo se actualizará cada segundo (no tiene precisión de milisegundos) y si se realizan X peticiones por cada segundo serán detectadas correctamente.

Editado a 02/02/2011 A continuación pongo el parche que ha dejado Antonio en los comentarios:

El parche es este: --- mod_evasive20.c.orig 2005-10-08 21:01:18.000000000 +0200 +++ mod_evasive20.c 2011-02-02 18:00:25.000000000 +0100 @@ -168,9 +168,9 @@ /* Reset our hit count list as necessary */ if (t-n->timestamp>=page_interval) { n->count=0; + n->timestamp = t; } } - n->timestamp = t; n->count++; } else { ntt_insert(hit_list, hash_key, t); @@ -190,9 +190,9 @@ /* Reset our hit count list as necessary */ if (t-n->timestamp>=site_interval) { n->count=0; + n->timestamp = t; } } - n->timestamp = t; n->count++; } else { ntt_insert(hit_list, hash_key, t); lo copiamos y pegamos en un fichero, por ejemplo mod_evasive.patch, dentro del directorio mod_evasive, aplicamos el parche con: patch -p0 < ../mod_evasive.patch

miércoles, 15 de abril de 2009

Usar debug para generar ficheros ejecutables

Supongamos que hemos conseguido explotar satisfactoriamente un fallo remoto en un sistema operativo Windows y tenemos acceso mediante una shell a dicho sistema. O Dicho de otro modo, hemos usado Metasploit para petar el ordenador del vecino y ahora tenemos un cmd de su sistema :|

El siguiente paso será ejecutar una herramienta de administración remota para “asegurarnos” la permanencia en su sistema. Pero bueno solo tenemos una shell, tendremos que descargarnos el RAT, ¿no?

Para ello se podría usar ftp.exe, si pero no siempre es posible, quizás el sistema no nos deje (permisos) o el firewall no nos permita acceder a ningún FTP.

A primera vista se nos puede ocurrir usar el comando echo para escribir valores hexadecimales sobre un fichero de salida, pero el comando no soporta valores hexadecimales como entrada, que raro, ¿no?

Lo que se puede usar es el comando echo para generar un fichero que pueda interpretar correctamente el comando debug y así generar el archivo que queramos. Con estos dos comandos podemos crear cualquier fichero en el sistema. Perfecto para generar un troyano o mejor un mini-downloader que baje el troyano.

Aquí se explica detalladamente como conseguirlo, con imágenes explicativas !

image

Resumiendo, hay un par de programas que una vez seleccionado un fichero te crean un bat con todos los echos y comandos necesarios para generar dicho fichero en un sistema remoto. Después, ya solo sería cuestión de tener paciencia e ir ejecutando cada línea de dicho bat en el sistema remoto.

Programas:

miércoles, 8 de abril de 2009

Solución al reto 1 de Panda (III parte, las dos comprobaciones)

-------------------------------------------------------------------- Solución al reto 1 de Panda (I parte, fracaso absoluto) Solución al reto 1 de Panda (II parte, FPqué!?) Solución al reto 1 de Panda (III parte, las dos comprobaciones) -------------------------------------------------------------------- En anteriores episodios, localizamos dos zonas vitales para que nuestro serial fuera tomado como valido. Primero una comprobación, un salto que nos lleva a la zona de password incorrecto, “bad!” y una variable que tiene que tener un determinado valor, 4, para mostrar el mensaje correcto, “yes!”.

Empecemos por la primera comparación.

image

Empezamos desde el salto condicional JNZ resaltado en amarillo hacia atrás. JNZ, realiza el salto cuando el flag Zero no está activado, vale 0. En nuestro caso esto es así.

image Por esta razón el salto es tomado y llegamos a la zona de printf “bad!” :(. Podríamos cambiar el flag manualmente a 1 o invertir el salto y seguir la ejecución, pero nuestro objetivo es conseguir un password correcto no forzar a que muestre el mensaje correcto.

Así que manos a la obra, hay que analizar las instrucciones anteriores al JNZ.

La instrucción inmediatamente anterior al JNZ es XOR AH, 40 Queremos que el flag zero se active, así que esa instrucción tiene que dar como resultado 0, por ello AH debe valer 40 en ese punto.

Vamos a ir analizando mas rápidamente el resto las instrucciones, tened en cuenta que se hace en orden inverso, primero las que se ejecutarán últimas:

  • AND AH, 45: Quedamos en que AH tenía que valer 40 tras esta operación, si se le va a aplicar un “AND 45”, AH tiene que tener el bit 6 activado (correspondiente al segundo 4) y los bits 2 y 0 desactivados (correspondientes al primer 5). Puede resultar confuso, si, mirad esta representación para aclaraos:image
    En los bits marcados con x da igual el valor que tengan ya que el AND cuando se le aplica un 0 siempre devuelve un 0 independientemente del otro bit.
  • FSTSW AX: Esta instrucción guarda en AX el valor de los bits de condición de la FPU, mas info. ¿Qué diablos son esos bits? Pues los que indican el resultado de una comparación de dos floats. Puede verse en los registros.
    image
    El valor de FST se guarda en AX. Recordad que antes las operaciones se han hecho sobre AH (la parte alta del registro AX), que descartamos la parte baja AL, 8 bits. Así que queremos que el bit 6+8, 14, del FST valga 1 y que los bits 2+8,10 y 0+8,8 valgan 0. Ollydbg puede confundir con su descomposición del registro FST. El contenido desglosado de FST es el siguiente.
    imageComo vemos el bit 14 corresponde a el bit C3(Z) de los Condition Code. El bit 10 al C2(C) y el bit 8 al C0(S). imageCuando estos bits se activan justo como requiere el programa significa que la FPU ha comparado dos número que eran iguales. Seguimos con las instrucciones previas, omitiendo aquellas que no hagan nada interesante:
  • FUCOMPP: Compara los números de la FPU ST(0) y ST(1)
    imageAsí que esos dos números tendrían que ser iguales, ¿no?
  • FXCH ST(1): Intercambia los registros ST(0) y ST(1)
  • FILD WORD PTR SS:[ESP]: Carga en ST(0), el tope de la pila de la FPU, el valor apuntado por la cima de la pila, y si hubiese algo en ST(0) lo desplaza a ST(1).
  • PUSH EAX: El valor que acababa de cargar en la FPU es EAX, que lo mete en la pila previamente.
  • CBW: Convierte un byte, AL, en palabra, AX, rellenando con ceros…pues fale.
  • MOV AL,BYTE PTR SS:[EBP-21]: Mueve a AL el carácter introducido, ¿que cómo sabemos que en EBP-21 estaba el carácter? Acudiendo a nuestro scanf inicial. image
  • FMULP ST(1),ST: Se multiplican ST(0) y ST(1)
  • FLD [LOCAL.8]: Carga en la pila de la FPU el %f2
  • FILD DWORD PTR SS:[ESP]: Carga en la FPU el valor de la cima de la pila
  • PUSH EAX: Pone en la pila EAX
  • MOV EAX,[LOCAL.12]: Pone en EAX la variable LOCAL.12, que aun no sabemos que es…
  • FISTP [LOCAL.12]: Guarda en LOCAL.12 el valor ST(0)
  • FLD [LOCAL.7]: Pone en ST(0) %f1

Recapitulemos:

  • Se carga en la FPU %f1
  • Lo saca y lo vuelve a meter, así me gusta nena, pero sigue estando %f1 en la FPU, ST(0)
  • Se carga en la FPU %f2 en ST(0), ahora %f1 está en ST(1) Para %f1 = 1 y %f2 = 97 image
  • Se multiplican estos dos valores
  • Mete en la FPU el valor ASCII del carácter %c
  • Compara los dos números

De modo que un password como esté: “1-1-97-a” debería superar la primera condición. 1*97 == ASCII(‘a’)

Probémoslo, ponemos un breakpoint antes del maldito JNZ, introducimos el posible password vemos que camino toma el salto.

image

JNZ no salta (la flecha esta de color gris), por lo que llega al JMP que le aleja de el printf que nos dice que el password era incorrecto. Perfecto !!

Uff, esto de analizarlo al revés es una locura. Pero nos ahorra analizar algunas instrucciones aunque ahora no nos ha servidor de mucho después nos ahorraremos un buen cacho de análisis.

Pasamos al segundo requisito para que un password sea correcto. que el desplazamiento visto en el capítulo anterior, almacenado en LOCAL.4, valga 4. Recordamos…

image

Buscamos donde se asigna una valor a LOCAL.4, se encuentra casi al principio, cerca del scanf.

image

Se le asigna el valor de EAX, justo después de de esa función. Esto es típico, cuando una función devuelve un valor, realmente se almacena en EAX. Hay que fijarse que la función solo se le pasan %f1 (argumento 3) y %d (argumento 1) así que en principio no usará el resto de números introducidos.

Entramos en la función, la seleccionamos y pulsamos intro. Una vez dentro de la función vamos al final de está. Tracearemos desde el final al principio buscando que hace falta para que EAX valga 4.

image

EAX vale lo que contenga LOCAL.18. A esta instrucción de puede llegar desde tres lugares. Desde la instrucción justamente anterior, MOV [LOCAL.17], EDX. Y desde dos saltos. Para localizarlos seguimos la línea roja de la muerte.

image

Desde cualquiera de los dos saltos LOCAL.18 vale 0 y por lo tanto EAX también. Esto no puede ocurrir ! Ponemos un breakpoint en los dos saltos, con el fin de determinar si alguno de los dos es tomado. Ejecutamos el programa e introducimos “1-1-97-a”. Vemos que en el primer salto ni se para, una comparación anterior hace que no se llegue hasta ahí. En el segundo alto si se para y…salta, por lo que LOCAL.18 valdrá 0, EAX 0 y … “bad!”.

image

Bueno está claro que la culpa es del JE(Jump if Equal) anterior si hubiese sido “Equal” hubiese ignorado este JMP maldito.

Analizamos las instrucciones anteriores al JE.

image

A destacar que EAX y LOCAL.16 tienen que valer lo mismo. EAX resulta de la suma de EAX y EBX. Ponemos un breakpoint en ADD EAX, EBX para observar que valores toma con diferentes passwords.

image

Sea cual sea el password introducido, EAX siempre vale 1 y EBX 3. Así que EAX terminará conteniendo el valor 4 (EAX += EBX).

Recordamos que antes hemos quedado en que EAX y LOCAL.16 tenían que valer lo mismo, y EAX sabemos que siempre vale 4. Así que buscamos cual es la última asignación realizada a LOCAL.16.

image

La encontramos ahí, tras analizar las instrucciones vemos como LOCAL.16 es cargada a partir de LOCAL.7 la cual es cargada a partir de ARG.3, que como hemos visto al entrar a esta función es %f1.

Ahora con todas las piezas disponibles, montamos el puzzle. EAX y LOCAL.16 tenían que valer lo mismo, EAX siempre valía 4, así que LOCAL.16 tiene que valer 4 y LOCAL.16 coge el valor de %f1. Así que %f1 tiene que valer 4.

Pero no olvidemos que %f1*%f2 = %c 4*%f2 = %c Si %f2 vale 25 por ejemplo 100 = %c = ‘d’

Pues ahí lo tenemos. El primer número, %d, no se ha usado para nada, así que le damos un valor aleatorio. %f1 tiene que valer 4. %f2 le damos el valor de 25. %c tiene que ser igual al valor ASCII de %f1*%f2, 100, ‘d’.

Y colorín colorado, un password correcto es: 1-4-25-d

image

Eureka ! Tarde unas 6 horas en sacarlo, soy un cracker de pacotilla. Envíe la solución 14 horas después del inicio del reto así que me quedaré sin camiseta, solo los 5 primeros la reciben.

Shaddy, un amigo que lo resolvió antes que yo, ha publicado también una solución que seguramente esté mejor explicada que esto. Solución en Megaupload Solución en GoogleGroups

Si queréis aprender sobre ingeniería inversa, este curso de Ricardo Narvaja es un buen comienzo, y la lista de google de crackslatinos un buen camino.

martes, 7 de abril de 2009

Solución al reto 1 de Panda (II parte, FPqué!?)

-------------------------------------------------------------------- Solución al reto 1 de Panda (I parte, fracaso absoluto) Solución al reto 1 de Panda (II parte, FPqué!?) Solución al reto 1 de Panda (III parte, las dos comprobaciones) -------------------------------------------------------------------- En la anterior entrada hemos llegado a la conclusión de que las operaciones realizadas para verificar un password introducido no son triviales. Al menos sabemos el formato que debe tener: %d-%f-%f-%c. Y parece que se realizan operaciones con la FPU, algo no muy habitual.

Vamos a ojear que operaciones se hacen a continuación del scanf para intentar encontrar donde verifica si nuestro password es correcto o no.

image

Como vemos, se copian a los registros ecx y eax, los valores %d y %f1 introducidos y se llama a una función. Así que sabemos que posiblemente esa función haga algún tipo de comparaciones con esos dos valores.

Entramos a la función con F7 y observamos horrorizados el paisaje, el desensamblado.

image

Es una función muy larga y usa un montón de operaciones de la FPU. Yo, ciego por las prisas, no se me ocurre otra cosa que pensar que lo mejor sería ponerse manos a la obra y empezar a analizar todo lo que hace la función.

image

Ahí se puede observar como comenté gran parte de las instrucciones, bieeeen. Lo bueno es que aprendí algo sobre el funcionamiento de la FPU, lo malo es que tras analizar algunas instrucciones mas llegué a la conclusión de que eso no estaba haciendo nada con sentido. Reescribía valores obtenidos de anteriores operaciones, realizaba operaciones sin sentido o que provocaban algún tipo de error en la FPU, etc. Estaba perdiendo el tiempo.

Lo peor de todo es que no estaba seguro de si el programa hacia un uso “avanzado” de la FPU y no era capaz de comprender qué operaciones se estaban realizando.

Así que momentáneamente me desesperé y lo di por imposible. Y es en estos momentos en los que uno piensa, recapacita, pero no mucho.

Pensé en que lo mejor sería, como se dice en los primeros tutoriales de cracking, encontrar “el chico bueno” y “el chico malo”. El mensaje que nos diga que hemos superado la protección y el mensaje que nos diga que no lo hemos hecho. El segundo ya lo tenemos es el mensaje “bad!”. ¿Pero y el chico bueno? ¿Qué imprime el programa cuando introducimos un serial válido?, y lo mas importante, ¿cuándo y porqué lo hace?.

Umm, vamos a ver que APIs usa, quizás eso nos aclare un poco las ideas. Botón derecho sobre la zona central “Search for > Name (label) in current module”

image

Como ya sabíamos se usan las funciones printf y scanf. printf se usará para mostrar lo de “Password:”, a continuación se utiliza scanf para recoger lo que escribimos y de nuevo un printf para decirnos si es correcto o no.

Entonces busquemos todos los sitios donde se usa printf, en alguno de ellos se tendrá que imprimir un mensaje parecido a “Conseguido”. Clic derecho sobre printf, “Find references to import”.

image

Hay 3, ya que la última corresponde a la IAT. Pulsamos sobre cada referencia y vemos que parámetros se le pasan a printf en las 3 situaciones.

image

El primero estamos seguros que no es. Los otros dos pues depende de lo que se le valgan los parámetros LOCAL.3 y EAX en su debido momento. Si nos fijamos en el código y buscamos operaciones que modifiquen la variable LOCAL.3 vemos que solo se modifica al principio, asignándole el valor “bad!”.

image

Podemos verificar que en 403030 se encuentra la cadena “bad” y que casualidad, unos bytes mas adelante está la cadena “yes”, ¿será lo que buscamos?

image

Así que sabemos que el segundo printf imprime siempre “bad!”. Seguramente se llegue aquí después de alguna comprobación que determine que nuestro serial es incorrecto. De hecho un par de instrucciones antes del segundo printf hay una comparación apetitosa.

image

Si no se activa el flag zero en las operaciones anteriores al JNZ nos lleva al segundo printf que imprime siempre bad :( Mas adelante tendremos que revisar como evitar esto. pero antes vamos a echar un ojo al último printf.

Al tercer printf se le pasa el registro EAX, donde estará la dirección de lo que imprimirá. Como podemos ver en el código, EAX vale la suma de la dirección de la cadena “bad” + un desplazamiento sacado de LOCAL.4

image Sabemos, porque lo vimos en el anterior, que la variable LOCAL.3 se mantiene siempre con la dirección de la cadena “bad”. Y que si la variable LOCAL.4 vale 0, se imprimirá “bad!”, pero si LOCAL.4 vale 4, se imprimiría “yes!”.

Resumiendo. Sabemos que hay una comprobación que si no se cumple nos llevará al segundo printf, mostrando “bad!”. Y que el tercer printf nos mostrará el mensaje “yes!” si conseguimos que LOCAL.4 valga 4. Ya tenemos mas o menos analizadas las condiciones necesarias para que un serial sea correcto, en la siguiente entrada veremos como conseguir que esto ocurra…

lunes, 6 de abril de 2009

Solución al reto 1 de Panda (I parte, fracaso absoluto)

-------------------------------------------------------------------- Solución al reto 1 de Panda (I parte, fracaso absoluto) Solución al reto 1 de Panda (II parte, FPqué!?) Solución al reto 1 de Panda (III parte, las dos comprobaciones) --------------------------------------------------------------------

El día 1 de este mes, abril, Panda organizó unos retos orientados al estudio de las aplicaciones o ingeniería inversa, vamos, al cracking.

En total eran 3 retos separados en el tiempo, el primero de cada uno se llevaba un jugoso premio, una PSP, un IPOD y un miniportátil.

Yo me enteré de los retos el mismo día que salió el primero gracias a Pedro Laguna. El primer reto aún puede encontrarse en su web. Mirror en google groups

Tras abrirlo vemos una ventana de consola pidiéndonos un password:image

Incorrecto, ya decía yo que no podía tener tanta suerte. Lo abrimos con OllyDbg

image

No se ve nada interesante a primera vista. Lo primero que se me ocurre es buscar las cadenas que usa el programa para localizar la cadena “bad!” que imprime cuando introducimos un password incorrecto y encontrar la cadena que se mostrará cuando el password sea correcto. Botón derecho sobre la zona central, “Search for > All referenced text strings”.

image

Encuentro:

  • “bad!”, la cadena mostrada cuando se introduce el password incorrectamente
  • “Password: ”, lo que imprime printf al comienzo
  • “%d-%f-%f-%c”, parece una típica cadena de formato usada en scanf, tendría sentido ya que espera un entero, guion, dos float separados por guión y un carácter. Algo así 1234-678.45-234,43-T
  • “%s!”, a saber puede ser el formato cadena usado en printf o scanf

Lo raro es que no encontramos ninguna cadena que nos muestre que hemos introducido el password correctamente.

Ahora se me ocurren varias posibilidades.

  • Inspeccionar las instrucciones que hagan uso de la cadena “bad!” , antes de llegar ahí tiene que haber una comparación que nos lleve a la zona buena o a esta mala. Lo malo es que puede ser mucho antes e ir de adelante hacia atrás es mas complicado.
  • Ver donde se usa la cadena de formato típica de scanf y tracear hacía delante viendo que se hace con los valores introducidos hasta llegar a una zona que decida si mostrarnos el mensaje bueno o malo.

Sigamos la segunda ya que el programa no parece muy largo. Botón derecho sobre la posible cadena de formato “Follow in Disassembler”

image

Pues si, va a ser una cadena usada por scanf. Sabemos que los 5 parámetros insertados en la pila antes de llamar a scanf serán:

  1. La dirección de la cadena de formato
  2. La dirección donde se guardará el primer entero, %d
  3. La dirección donde se guardará el primer float, %f1
  4. La dirección donde se guardará el segundo float, %f2
  5. La dirección donde se guardará el carácter %c

Ponemos un breakpoint en el CALL scanf, ejecutamos, tras parar en el breakpoint vemos el estado de la pila, encontramos todos los parámetros pasados al CALL:

image

Podemos poner un breakpoint en cada una de las 4 direcciones para que el programa detenga su ejecución cuando se lean o escriban esos parámetros. De este modo tendríamos una idea de las operaciones que se hace con los números que introducimos para verificar si es un password correcto o no.

Haremos eso, seleccionamos cada dirección del stack, clic derecho, “Follow in dump”.

image

Seleccionamos los 4 primeros bytes que es lo que ocupa un entero, botón derecho “Breakpoint > Hardware, on access > Dword”. Seguimos el mismo procedimiento para los otros 3 valores, teniendo en cuenta que un float también ocupa 4 bytes y un carácter ocupa uno.

Perfecto, ahora recordemos que el programa lo tenemos parado en el breakpoint que pusimos a la instrucción Call scanf. Así que pulsamos F8 para ejecutar scanf.

El programa estará a la espera de que introduzcamos el password por la consola, nosotros introduciremos el valor “1-2.0-3.0-A”.

El programa parará varias veces ya que accede a las zonas de memoria donde guarda los valores recogidos desde la consola y donde pusimos los hardware breakpoint. Pulsamos F9 hasta ver que nos encontramos en la siguiente instrucción del CALL scanf.

Podremos comprobar en el DUMP, que en las direcciones que vimos en la pila se sitúan los valores que hemos introducido por consola.

image

Vemos la ‘A’, 40400000, 40000000 y 00000001. Recordad que se usa Little-Endian. Los dos float, se almacenan en coma flotante de ahí que no se distinga a primera vista el 2.0 y el 3.0.

Seguimos pulsando F9 y vemos que el programa se va deteniendo según usa los valores recogidos desde la consola. Vemos que se detiene a veces en instrucciones muy raras.

image

Buscando con el perrito del ciego de lycos, encontramos que son instrucciones del coprocesador matemático, la FPU, “operaciones para trabajar con números en coma flotante”. Mierda !

Seguimos pulsando F9 y deteniéndonos por los malditos hardware breakpoints. Hasta que el programa finaliza sin habernos dado cuenta de que ha pasado.

Doble mierda, plan fallido ! Mi idea era que durante el progreso hubiese encontrado cómo fácilmente se mueven los valores a los registros habituales y se usan instrucciones comunes para compararlos con algún valor esperado. Pero lo mas cercano que he encontrado han sido un montón de instrucciones raras de la FPU.

Primera aproximación fallida. Tendremos que ir poco a poco viendo lo que hacen instrucciones posteriores al scanf, hasta comprender porque nuestro password no es correcto.

Continuará...