Continuamos con el siguiente crackme, el cual presenta una variación del reto anterior que resolvimos, en el cual se realizaban varias comparativas diferentes, así como cast entre tipos de datos.

Footprint

Comenzamos como hemos resuelto otros crackmes, por realizar un file, un ldd y un readelf:

Initial info about crackme0x05

Podemos observar que la mayoría de la información que se ha presentado en otros crackmes se mantiene, así que por este momento el preCutter se detiene aquí.

Cargamos el binario en Cuttter:

Load on Cutter

Y como en otros ejercicios, veamos si el dashboard nos ayuda con alguna otra información adicional:

Dashboard

Como podemos observar y si recordamos otros ejercicios, parece que has sido construido de la misma manera que los demás crackmes, así que hasta aquí llegamos en esta sección.

Entrypoints

Ahora toca el turno de analizar el entry point que vimos con readelf, el cual era 0x080483d0, veamos que dice Cutter al respecto:

Entry Points

La información coincide y es mas, podemos ver también la llamada de entry0 que tenemos para este entry point dentro de las funciones.

Strings

Veamos si en los strings podemos encontrar algún indicio diferente o la variación entre este crackme y los anteriores:

Strings

Con esta captura, concluimos que los strings no son de mucha ayuda en este crackme.

Imports

Pasemos ahora a las funciones que se importan de este crackme:

Imports

Functions

En la sección de funciones encontraremos las que ya analizamos están importadas via libc, y adicionalmente observaremos que aparte de main, tenemos una función llamada check.

Functions

Continuemos con el disassembly de este binario:

Disassembly

Función main

Al ser el main el glue de todo el programa, iniciamos por esta función, la cual tiene el siguiente código:

pdf Main

Siguiendo la misma mecánica que con los crackme anteriores, vamos parte por parte destripando el programa.

Main - 1

0x08048540 … 0x08048549

Tenemos el inicio del prologo de la función, donde se comienza a definir el stack frame para la función.

0x0804854c … 0x08048554

EAX es igual a 0, luego le sumamos 15, y luego le volvemos a sumar 15. EAX=30

0x08048557 … 0x0804855d

EAX / 2^4 y después EAX * 2^4. Resultado: EAX =16. Restamos 16 al ESP

Main - 2

0x0804855f … 0x08048572

Subimos la ubicación del string de LVL al ESP, y llamamos a la función printf. Este proceso se repite otra vez con el string de “Password: “.

Main - 3

0x08048577 Subimos la dirección de local_78h a EAX

0x0804857a Movemos esa dirección almacenada en EAX a local_4h (ESP+4h)

0x0804857e Movemos la ubicación del string ‘%s’, la cual es 0x080486b2, a ESP

0x08048585 Llamamos a scanf, pero ojo, recibe el argumento1 via ESP y el argumento2 via ESP+4h como en los ejercicios anteriores.

Main - 4

0x0804858a Bueno, parece que ahora esa dirección ya tiene un valor via scanf, ahora volvemos a llevar la dirección a EAX 0x0804858d Cargamos EAX en el ESP 0x08048590 Y llamamos a otra función pasándole como parámetro la dirección del valor que tomamos de scanf.

Main - 5

Cuando ya retornemos de la función check, únicamente retornamos 0 vía EAX y acaba la función con leave+ret.

Función check

Ahora si, la función buena a resolver:

pdf Check

No se ve tan monstruosa como la anterior, pero tiene su truco.

Check - 1

La primera parte es equivalente al prologo de la función, donde se define un stack de 40 bytes para la función.

Check - 2

Inicializamos local_8h y local_ch a 0. Si vemos la descripción que nos entrega cutter, veremos que estas son variables locales.

Check - 3

0x080484dc Asignamos el valor de s a EAX, ¿y quien es s? Pues no es mas que el parámetro que la pasamos a la función, la dirección donde se encuentra el valor que ingresamos via scanf

0x080484df Ahora subimos esa dirección a ESP (whoot)

0x0804842e Llamamos a strlen (que recibe la dirección del string). strlen básicamente toma un string y ejecuta un loop while incrementando un contador buscando la primera incidencia de un carácter null \x00. Al encontrarlo devuelve el valor del contador o por cuantos caracteres tuvo que evaluar para encontrar el carácter null, es decir, la longitud del string. El valor devuelto es un numero natural n>0 que se almacena en EAX.

0x080484e7 Tomamos el valor de EAX y lo comparamos con el valor de local_ch (la variable que habíamos inicializado a 0), el resultado levanta las flags de ZF y CF según corresponda. En la primera ejecución local_ch es menor que EAX, por lo que la flag de CF se levanta.

0x080484ea Tenemos un salto condicional, si el flag de CF no se levanto, entonces ejecuta el jump. Hasta que local_ch no sea igual o mayor a la longitud del string ingresado, no se ejecutara el jump.

Check - 4

Si el jump no se ejecuta;

0x080484ec Movemos el valor del contador a EAX

0x080484ef Sumamos el counter y la dirección de s (el valor ingresado via scanf)

0x080484f2 Movemos el byte de AL a EAX (ojo, movimos el valor) y dejamos EAX lleno de ceros a la izq.

0x080484f5 Subimos ese valor (el byte) en local_dh.

Ahora local_dh vale el valor de la letra en curso según el ciclo for.

Check - 5

0x080484f8 Subimos la dirección de local_4h a EAX

0x080484fb Cargamos EAX en local_8h_2 (el que esta mas cerca del ESP)

0x080484ff Movemos el valor que esta en 0x08048668 (%d)

0x08048507 Cargamos en EAX la dirección de local_dh (la letra en curso del texto ingresado)

0x0804850a Subimos esta letra a ESP desde EAX

0x0804850d Llamamos a la función sscanf, que recibe como parámetros el char *s[i], el string ‘%d’, y el entero local_4h

La función sscanf() se encarga de procesar un string que recibe de parámetro inicial y al igual que su pariente, scanf, recibe también el formato y opcionalmente donde almacenar el resultado. Esto nos da una clara idea de que lo que ingresemos de input, sera convertido a int y almacenado en local_4h.

Check - 6

0x08048512 Movemos el valor de local_4h a EDX

0x08048515 Subimos la dirección de local_8h a EAX

0x08048518 Sumamos el valor de EDX (el entero de la letra en curso) y EAX (que era 0 al inicio del for, fue la primera en inicializarse), y lo guardamos en EAX

0x0804851a Comparamos el valor de EAX contra 0x10 (16 en decimal)

Esto básicamente quiere decir que hasta este punto, local_8h es una variable fuera del ciclo donde se va sumando los valores individuales de cada carácter ingresado en el primer scanf. También significa que debemos ingresar números, puesto que la comparación se realizo contra 16, cantidad que ninguna letra puede hacer frente.

Check - 7

Entramos en un if, que dice así;

Si el resultado de la comparación fue falso (no iguales), entonces brinca a 0x0804852b.

0x0804852b Cargamos la dirección de contador en EAX

0x0804852e Incrementamos el valor de EAX

0x08048530 Brincamos hacia arriba hasta 0x080484dc, donde el loop comenzó

Si el resultado de la comparación fue verdadero (iguales), entonces continua.

0x0804852b Movemos la dirección de s a EAX

0x0804852b Cargamos esa dirección en ESP

0x0804852b Llamamos a la función parell que recibe de parámetro s.

Las ultimas tres instrucciones son básicamente cuando el loop finalizo, en donde se carga el mensaje de “Password Incorrect” para posteriormente imprimirlo en pantalla via printf. Tenemos un leave/ret que finaliza la función y nos regresa a main.

Función parell

Hemos llegado hasta esta función después de recibir en main un string, enviarlo de parámetro a check, en donde fue validado caracter por caracter, tras una conversón a entero, que la sumatoria total de todos los valores es de 16, por lo que check llama a parell enviando el string que recibió de main.

Parell

Como podemos ver, esta función es mas pequeña que check y main, pero veremos que secretos nos aguarda.

Parell - 1

Esta primera parte del prologo de la función define un stack frame de 0x18 para la función.

Parell - 2

0x0804848a Cargamos la dirección de local_4h en EAX

0x0804848d Cargamos el valor de EAX en local_8h (Cerca de ESP)

0x08048491 Continuamos subiendo el valor ‘%d’ a format aka ESP+0x4

0x08048499 Movemos la dirección del string s hacia EAX

0x0804849c Cargamos la dirección del string s en ESP

0x0804849f Llamamos a sscanf.

Ok, sscanf (*s, ‘%d’, local_4h), como en la función anterior.

Parell - 3

0x0804849f Movemos el valor de local_4h a EAX (osea el numero ingresado)

0x0804849f Realizamos un and entre el numero y 1. Esto quiere decir que básicamente evalúa que la salida sea o no par, aplicando una operación el bit menos significativo.

0x0804849f Se realiza un test entre EAX y EAX para validar que el and haya resultado verdadero

Parell - 4

En caso de que el numero sea par, se imprime el mensaje “Pasword OK!” y se finaliza el programa con estatus 0.

En caso de que el numero no sea par, parell retorna a check y check a su vez finalizar el for, concluyendo con el mensaje de “Password Incorrect.”

Gráfico

Los grafos de las tres funciones principales son:

Función main

graph main

Función check

graph check

Función parell

graph parell

Validación

Como la información que analizamos de este binario, concluimos que no hay una única respuesta, sino varias. La condición es que los números en serie que ingresemos puedan sumar 16 exacto y que el numero sea par, es decir, números como 04444 y 22222222 son validos. Números como 55421 no porque a pesar de que sus primeros números en serie suman 16, el numero es impar, mientras que números como 5560 o 1234512 si porque los primeros números suman 16 y son números pares.

Validemos:

Validación

Excelente! Hemos resuelto el sexto crackme de IOLI.


Bueno eso ha sido todo de momento, espero que les haya gustado. Saludos,