Hay dos cosas que he aprendido de los equipos que frecuentemente ganan en los CTF:
Podemos descargar la herramienta pin desde aquí y proceder a compilar el pintool "inscount0.so":
user@debian:~/reversing$ wget --quiet http://software.intel.com/sites/landingpage/pintool/downloads/pin-2.14-71313-gcc.4.4.7-linux.tar.gz
user@debian:~/reversing$ tar xf pin-2.14-71313-gcc.4.4.7-linux.tar.gz
user@debian:~/reversing/pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples$ cd pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/
user@debian:~/reversing/pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples$ make inscount0.test
g++ -DBIGARRAY_MULTIPLIER=1 -Wall -Werror -Wno-unknown-pragmas -fno-stack-protector -DTARGET_IA32 -DHOST_IA32 -DTARGET_LINUX -I../../../source/include/pin -I../../../source/include/pin/gen -I../../../extras/components/include -I../../../extras/xed-ia32/include -I../../../source/tools/InstLib -O3 -fomit-frame-pointer -fno-strict-aliasing -c -o obj-ia32/inscount0.o inscount0.cpp
g++ -shared -Wl,--hash-style=sysv -Wl,-Bsymbolic -Wl,--version-script=../../../source/include/pin/pintool.ver -o obj-ia32/inscount0.so obj-ia32/inscount0.o -L../../../ia32/lib -L../../../ia32/lib-ext -L../../../ia32/runtime/glibc -L../../../extras/xed-ia32/lib -lpin -lxed -lpindwarf -ldl
make -C ../../../source/tools/Utils dir obj-ia32/cp-pin
make[1]: se ingresa al directorio `/home/user/reversing/pin-2.14-71313-gcc.4.4.7-linux/source/tools/Utils'
mkdir -p obj-ia32
g++ -DTARGET_IA32 -DHOST_IA32 -DFUND_TC_TARGETCPU=FUND_CPU_IA32 -DFUND_TC_HOSTCPU=FUND_CPU_IA32 -DTARGET_LINUX -DFUND_TC_TARGETOS=FUND_OS_LINUX -DFUND_TC_HOSTOS=FUND_OS_LINUX -O3 -o obj-ia32/cp-pin cp-pin.cpp
make[1]: se sale del directorio `/home/user/reversing/pin-2.14-71313-gcc.4.4.7-linux/source/tools/Utils'
../../../pin -t obj-ia32/inscount0.so -- ../../../source/tools/Utils/obj-ia32/cp-pin makefile obj-ia32/inscount0.makefile.copy \
> obj-ia32/inscount0.out 2>&1
cmp makefile obj-ia32/inscount0.makefile.copy
rm obj-ia32/inscount0.makefile.copy
rm obj-ia32/inscount0.out
user@debian:~/reversing/pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples$ cd ../../../..
user@debian:~/reversing$
¡Estamos listos para probar algunos crackmes desde crackmes.de!
El primer paso es hallar el valor "User ID" correcto. Como solo son 255 posibles valores podemos probarlos todos. Aquí esta la salida (truncada):
Confirmamos que al ingresar "123" nos solicita un numero de licencia:
En este punto podemos verificar el comportamiento usando el pintool:
Si continuamos probando con caracteres distintos, eventualmente encontramos un caracter para el que el conteo de instrucciones cambia:
Concluimos que el serial comienza por "7" :)
En este punto podemos automatizar el proceso. Aquí esta mi script:
Aquí esta la salida del script:
Aquí esta la salida con los tiempos (mucho mas lento que los anteriores):
Y verificamos el resultado:
Confirmado :)
Vamos directamente a explorar la longitud del password:
Con cada caracter que agregamos el conteo se incrementa en uno. Si seguimos incrementando eventualmente veremos lo siguiente:
Cuando el password alcanza 15 caracteres se ejecutan muchas mas instrucciones que en los casos anteriores. Ademas, cuando el password tiene 16 caracteres, nuevamente se ejecutan pocas instrucciones. Concluimos que la longitud es 15 :)
Preparamos el siguiente script (usando subprocess en lugar de pexpect):
Y esta es la salida:
Aunque se generan algunos errores, hemos encontrado un password. Vamos a confirmarlo:
Uhm… Algo malo ha pasado aquí. Veamos de nuevo la salida:
Cuando comienzan los caracteres especiales empieza el error reportando unas comillas sin cerrar (esto se debe a que la comilla es uno de los caracteres que probamos). Pero hay un caso en el que no se presenta el error, posiblemente porque aparece una comilla adicional que completa el par. Lo que imagino es que este serial incluye una comilla :S
Probaremos con comillas simples y dobles (escapandolas con la contraria):
Confirmado. El serial terminaba con una comilla doble :) Este crackme tenía una trampa adicional…
Aquí la salida del script que preparé:
Toma más tiempo que los anteriores, pero igual funciona :)
- Es una ventaja tener jugadores distribuidos en diversas zonas horarias. De esta forma, mientras unos duermen los otros juegan, y el equipo no tiene tiempos muertos en el scoreboard.
- Es una necesidad automatizar todo lo que sea automatizable. De esta forma, el equipo puede dedicar su tiempo a los niveles que no pueden explotarse de forma automática.
La idea de este post es mostrar una de las técnicas que existen para automatizar la solución de algunos crackmes. Específicamente, algunos de los crackmes en los que la validación del serial es un conjunto de pequeñas validaciones. Por ejemplo, un algoritmo como el siguiente:
O alguna otra cosa por el estilo, donde se mantiene ese patrón de saltar rápidamente al final (y ya no seguir validando) si alguna validación intermedia falla.
Un ejemplo bastante familiar sería el caso de strcmp (donde primero se verifica si el primer caracter de las cadenas a comparar es igual y si no lo es terminamos, pero si es igual verificamos otro caracter, y luego otro, y otro hasta encontrar alguno diferente o hasta terminar de recorrer las cadenas). Desde luego, algo tan simple como un strcmp seguramente se resuelve mas fácil con ltrace, pero esta técnica tambien aplica.
La técnica que veremos está descrita en "A binary analysis, count me if you can" (Salwan, 2013) y se basa en usar la herramienta "pin" (el framework de instrumentación binaria dinámica desarrollado por Intel) para calcular el conteo de instrucciones ejecutadas por el binario que estamos analizando y desde allí inferir si algún cambio en nuestro serial nos acerca o no a la solución. Convenientemente, la herramienta "pin" incluye un ejemplo para calcular los conteos de instrucciones :)
- Si la longitud del serial es menor a seis caracteres, falla la validación y no se verifica nada mas
- Si el primer caracter no es una 'R', falla la validación y no se verifica nada mas
- Si el cuarto caracter no es un número menor que 4, falla la validación y no se verifica nada mas.
- Si el sexto caracter no es igual al segundo caracter, falla la validación y no se verifica nada mas.
O alguna otra cosa por el estilo, donde se mantiene ese patrón de saltar rápidamente al final (y ya no seguir validando) si alguna validación intermedia falla.
Un ejemplo bastante familiar sería el caso de strcmp (donde primero se verifica si el primer caracter de las cadenas a comparar es igual y si no lo es terminamos, pero si es igual verificamos otro caracter, y luego otro, y otro hasta encontrar alguno diferente o hasta terminar de recorrer las cadenas). Desde luego, algo tan simple como un strcmp seguramente se resuelve mas fácil con ltrace, pero esta técnica tambien aplica.
La técnica que veremos está descrita en "A binary analysis, count me if you can" (Salwan, 2013) y se basa en usar la herramienta "pin" (el framework de instrumentación binaria dinámica desarrollado por Intel) para calcular el conteo de instrucciones ejecutadas por el binario que estamos analizando y desde allí inferir si algún cambio en nuestro serial nos acerca o no a la solución. Convenientemente, la herramienta "pin" incluye un ejemplo para calcular los conteos de instrucciones :)
Podemos descargar la herramienta pin desde aquí y proceder a compilar el pintool "inscount0.so":
user@debian:~/reversing$ wget --quiet http://software.intel.com/sites/landingpage/pintool/downloads/pin-2.14-71313-gcc.4.4.7-linux.tar.gz
user@debian:~/reversing$ tar xf pin-2.14-71313-gcc.4.4.7-linux.tar.gz
user@debian:~/reversing/pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples$ cd pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/
g++ -DBIGARRAY_MULTIPLIER=1 -Wall -Werror -Wno-unknown-pragmas -fno-stack-protector -DTARGET_IA32 -DHOST_IA32 -DTARGET_LINUX -I../../../source/include/pin -I../../../source/include/pin/gen -I../../../extras/components/include -I../../../extras/xed-ia32/include -I../../../source/tools/InstLib -O3 -fomit-frame-pointer -fno-strict-aliasing -c -o obj-ia32/inscount0.o inscount0.cpp
g++ -shared -Wl,--hash-style=sysv -Wl,-Bsymbolic -Wl,--version-script=../../../source/include/pin/pintool.ver -o obj-ia32/inscount0.so obj-ia32/inscount0.o -L../../../ia32/lib -L../../../ia32/lib-ext -L../../../ia32/runtime/glibc -L../../../extras/xed-ia32/lib -lpin -lxed -lpindwarf -ldl
make -C ../../../source/tools/Utils dir obj-ia32/cp-pin
make[1]: se ingresa al directorio `/home/user/reversing/pin-2.14-71313-gcc.4.4.7-linux/source/tools/Utils'
mkdir -p obj-ia32
g++ -DTARGET_IA32 -DHOST_IA32 -DFUND_TC_TARGETCPU=FUND_CPU_IA32 -DFUND_TC_HOSTCPU=FUND_CPU_IA32 -DTARGET_LINUX -DFUND_TC_TARGETOS=FUND_OS_LINUX -DFUND_TC_HOSTOS=FUND_OS_LINUX -O3 -o obj-ia32/cp-pin cp-pin.cpp
make[1]: se sale del directorio `/home/user/reversing/pin-2.14-71313-gcc.4.4.7-linux/source/tools/Utils'
../../../pin -t obj-ia32/inscount0.so -- ../../../source/tools/Utils/obj-ia32/cp-pin makefile obj-ia32/inscount0.makefile.copy \
> obj-ia32/inscount0.out 2>&1
cmp makefile obj-ia32/inscount0.makefile.copy
rm obj-ia32/inscount0.makefile.copy
rm obj-ia32/inscount0.out
user@debian:~/reversing/pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples$ cd ../../../..
user@debian:~/reversing$
¡Estamos listos para probar algunos crackmes desde crackmes.de!
Caso 1: DrSpliff's drscm3
Empecemos analizando el crackme "DrSpliff's drscm3":
user@debian:~/reversing$ ./crack
User ID: rmolina
Error: Please enter a number between 0x01 and 0xFF
user@debian:~/reversing$
User ID: rmolina
Error: Please enter a number between 0x01 and 0xFF
user@debian:~/reversing$
El primer paso es hallar el valor "User ID" correcto. Como solo son 255 posibles valores podemos probarlos todos. Aquí esta la salida (truncada):
user@debian:~/reversing$ for i in `seq 255`; do echo -n "$i: "; ./crack <<< $i; done
1: Violación de segmento
2: Violación de segmento
3: Violación de segmento
4: Violación de segmento
5: Violación de segmento
6: Violación de segmento
7: Violación de segmento
8: Violación de segmento
9: Violación de segmento
10: Violación de segmento
11: Violación de segmento
12: Violación de segmento
13: Violación de segmento
14: Violación de segmento
15: Violación de segmento
16: Violación de segmento
17: Violación de segmento
18: Violación de segmento
19: Violación de segmento
20: Violación de segmento
21: Violación de segmento
22: Violación de segmento
23: Violación de segmento
24: Violación de segmento
25: Violación de segmento
26: Violación de segmento
27: Violación de segmento
28: Violación de segmento
29: Violación de segmento
30: Violación de segmento
31: Violación de segmento
32: Violación de segmento
33: Violación de segmento
34: Violación de segmento
35: Violación de segmento
36: Violación de segmento
37: Violación de segmento
38: Violación de segmento
39: Violación de segmento
40: Violación de segmento
41: Violación de segmento
42: Violación de segmento
43: Violación de segmento
44: Violación de segmento
45: Violación de segmento
46: Violación de segmento
47: Violación de segmento
48: Violación de segmento
49: Violación de segmento
50: Instrucción ilegal
51: Violación de segmento
52: Violación de segmento
53: Violación de segmento
54: Violación de segmento
55: Violación de segmento
56: Violación de segmento
57: Violación de segmento
58: `trap' para punto de parada/seguimiento
59: Violación de segmento
60: Violación de segmento
61: Violación de segmento
62: Violación de segmento
63: Violación de segmento
64: Violación de segmento
65: Violación de segmento
66: Violación de segmento
67: Violación de segmento
68: Violación de segmento
69: Instrucción ilegal
70: Violación de segmento
71: Violación de segmento
72: Violación de segmento
73: Violación de segmento
74: Violación de segmento
75: Violación de segmento
76: Violación de segmento
77: Violación de segmento
78: Violación de segmento
79: Violación de segmento
80: Violación de segmento
81: Violación de segmento
82: Violación de segmento
83: Violación de segmento
84: Violación de segmento
85: Violación de segmento
86: Violación de segmento
87: Violación de segmento
88: Violación de segmento
89: Violación de segmento
90: Violación de segmento
91: Violación de segmento
92: Violación de segmento
93: Violación de segmento
94: Violación de segmento
95: Instrucción ilegal
96: Violación de segmento
97: Violación de segmento
98: Violación de segmento
99: Violación de segmento
100: Violación de segmento
101: Violación de segmento
102: Violación de segmento
103: Violación de segmento
104: Violación de segmento
105: Violación de segmento
106: Violación de segmento
107: Violación de segmento
108: Instrucción ilegal
109: Violación de segmento
110: Violación de segmento
111: Violación de segmento
112: Violación de segmento
113: Violación de segmento
114: Violación de segmento
115: Violación de segmento
116: Violación de segmento
117: Violación de segmento
118: Violación de segmento
119: Violación de segmento
120: Violación de segmento
121: Violación de segmento
122: Violación de segmento
123: Lice%
Oops!
…
1: Violación de segmento
2: Violación de segmento
3: Violación de segmento
4: Violación de segmento
5: Violación de segmento
6: Violación de segmento
7: Violación de segmento
8: Violación de segmento
9: Violación de segmento
10: Violación de segmento
11: Violación de segmento
12: Violación de segmento
13: Violación de segmento
14: Violación de segmento
15: Violación de segmento
16: Violación de segmento
17: Violación de segmento
18: Violación de segmento
19: Violación de segmento
20: Violación de segmento
21: Violación de segmento
22: Violación de segmento
23: Violación de segmento
24: Violación de segmento
25: Violación de segmento
26: Violación de segmento
27: Violación de segmento
28: Violación de segmento
29: Violación de segmento
30: Violación de segmento
31: Violación de segmento
32: Violación de segmento
33: Violación de segmento
34: Violación de segmento
35: Violación de segmento
36: Violación de segmento
37: Violación de segmento
38: Violación de segmento
39: Violación de segmento
40: Violación de segmento
41: Violación de segmento
42: Violación de segmento
43: Violación de segmento
44: Violación de segmento
45: Violación de segmento
46: Violación de segmento
47: Violación de segmento
48: Violación de segmento
49: Violación de segmento
50: Instrucción ilegal
51: Violación de segmento
52: Violación de segmento
53: Violación de segmento
54: Violación de segmento
55: Violación de segmento
56: Violación de segmento
57: Violación de segmento
58: `trap' para punto de parada/seguimiento
59: Violación de segmento
60: Violación de segmento
61: Violación de segmento
62: Violación de segmento
63: Violación de segmento
64: Violación de segmento
65: Violación de segmento
66: Violación de segmento
67: Violación de segmento
68: Violación de segmento
69: Instrucción ilegal
70: Violación de segmento
71: Violación de segmento
72: Violación de segmento
73: Violación de segmento
74: Violación de segmento
75: Violación de segmento
76: Violación de segmento
77: Violación de segmento
78: Violación de segmento
79: Violación de segmento
80: Violación de segmento
81: Violación de segmento
82: Violación de segmento
83: Violación de segmento
84: Violación de segmento
85: Violación de segmento
86: Violación de segmento
87: Violación de segmento
88: Violación de segmento
89: Violación de segmento
90: Violación de segmento
91: Violación de segmento
92: Violación de segmento
93: Violación de segmento
94: Violación de segmento
95: Instrucción ilegal
96: Violación de segmento
97: Violación de segmento
98: Violación de segmento
99: Violación de segmento
100: Violación de segmento
101: Violación de segmento
102: Violación de segmento
103: Violación de segmento
104: Violación de segmento
105: Violación de segmento
106: Violación de segmento
107: Violación de segmento
108: Instrucción ilegal
109: Violación de segmento
110: Violación de segmento
111: Violación de segmento
112: Violación de segmento
113: Violación de segmento
114: Violación de segmento
115: Violación de segmento
116: Violación de segmento
117: Violación de segmento
118: Violación de segmento
119: Violación de segmento
120: Violación de segmento
121: Violación de segmento
122: Violación de segmento
123: Lice%
Oops!
…
Confirmamos que al ingresar "123" nos solicita un numero de licencia:
user@debian:~/reversing$ ./crack
User ID: 123
Lice% rmolina
Oops!
user@debian:~/reversing$
User ID: 123
Lice% rmolina
Oops!
user@debian:~/reversing$
En este punto podemos verificar el comportamiento usando el pintool:
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./crack; cat inscount.out
User ID: 123
Lice% a
Oops!
Count 80753
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./crack; cat inscount.out
User ID: 123
Lice% b
Oops!
Count 80753
user@debian:~/reversing$
User ID: 123
Lice% a
Oops!
Count 80753
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./crack; cat inscount.out
User ID: 123
Lice% b
Oops!
Count 80753
user@debian:~/reversing$
Si continuamos probando con caracteres distintos, eventualmente encontramos un caracter para el que el conteo de instrucciones cambia:
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./crack; cat inscount.out
User ID: 123
Lice% 5
Oops!
Count 80753
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./crack; cat inscount.out
User ID: 123
Lice% 6
Oops!
Count 80753
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./crack; cat inscount.out
User ID: 123
Lice% 7
Oops!
Count 80760
user@debian:~/reversing$
User ID: 123
Lice% 5
Oops!
Count 80753
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./crack; cat inscount.out
User ID: 123
Lice% 6
Oops!
Count 80753
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./crack; cat inscount.out
User ID: 123
Lice% 7
Oops!
Count 80760
user@debian:~/reversing$
Concluimos que el serial comienza por "7" :)
En este punto podemos automatizar el proceso. Aquí esta mi script:
import pexpect import time import re import string cmd = "./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./crack" def send_password(password): child = pexpect.spawn(cmd) child.expect('User ID:') child.sendline('123') child.expect('Lice%') child.sendline(password) while True: with open('inscount.out', 'r') as f: count = re.findall('Count (.*)', f.read()) if len(count): count = count.pop() break return count def search_one(known = ''): old_char = None old_count = None for new_char in string.printable: new_count = send_password(known + new_char) if old_count is not None and new_count != old_count: if new_count > old_count: return known + new_char else:return known + old_char #return -1 breakold_count = new_count old_char = new_char return -1 known = '' while True: known = search_one(known) if known == -1: break else: print "Found:", known
Aquí esta la salida del script:
user@debian:~/reversing$ time python crack.py
Found: 7
Found: 7s
Found: 7sd
Found: 7sd9
Found: 7sd9g
Found: 7sd9gw
Found: 7sd9gwi
Found: 7sd9gwig
Found: 7sd9gwigE
real 4m29.162s
user 0m8.733s
sys 3m39.602s
user@debian:~/reversing$
Confirmamos el serial que hemos encontrado:
Confirmado :)
Si modificamos el script anterior para usar "USERNAME" y "PASSWORD" en lugar de "User ID" y "Lice%", veremos que no logra resolver el problema, y se queda atrapado en un loop por mucho tiempo (espere cerca de 15 minutos antes de matar el proceso al no ver ningun progreso).
Podemos entonces verificar manualmente si el binario requiere una longitud mínima del usuario:
Necesitamos un mínimo de tres caracteres en el usuario para que el crackme nos pida un password. Verificamos entonces la longitud mínima del password:
Ya que el conteo de instrucciones cambia de 1438 a 1449, concluimos que se necesita un mínimo de cuatro caracteres en el password :)
Podemos entonces actualizar nuestro script inicial para completar los passwords a un mínimo de 3 caracteres (Sólo es necesario cambiar unas pocas líneas: las del expect, el nombre del crackme, y el segundo return):
Y esta es la salida:
Confirmado :)
Exploramos con el pintool la longitud mínima del password:
user@debian:~/reversing$ ./crack
User ID: 123
Lice% 7sd9gwigE
Thanks!
user@debian:~/reversing$
User ID: 123
Lice% 7sd9gwigE
Thanks!
user@debian:~/reversing$
Confirmado :)
Caso 2: rezk2ll's BeatME
Probaremos ahora con un keygenme: "rezk2ll's BeatME". Desde luego, no podemos obtener el algoritmo de generación de claves usando solo esta técnica, así pues, el objetivo será obtener el password válido para el usuario "rmolina".
user@debian:~/reversing$ ./BeatMe
____ _ __ __
| __ ) ___ __ _| |_| \/ | ___
| _ \ / _ \/ _` | __| |\/| |/ _ \
| |_) | __/ (_| | |_| | | | __/
|____/ \___|\__,_|\__|_| |_|\___| | ReZK2LL
USERNAME : rmolina
PASSWORD : rmolina
NOPE , YOU LOSE
user@debian:~/reversing$
____ _ __ __
| __ ) ___ __ _| |_| \/ | ___
| _ \ / _ \/ _` | __| |\/| |/ _ \
| |_) | __/ (_| | |_| | | | __/
|____/ \___|\__,_|\__|_| |_|\___| | ReZK2LL
USERNAME : rmolina
PASSWORD : rmolina
NOPE , YOU LOSE
user@debian:~/reversing$
Si modificamos el script anterior para usar "USERNAME" y "PASSWORD" en lugar de "User ID" y "Lice%", veremos que no logra resolver el problema, y se queda atrapado en un loop por mucho tiempo (espere cerca de 15 minutos antes de matar el proceso al no ver ningun progreso).
Podemos entonces verificar manualmente si el binario requiere una longitud mínima del usuario:
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./BeatMe; cat inscount.out
____ _ __ __
| __ ) ___ __ _| |_| \/ | ___
| _ \ / _ \/ _` | __| |\/| |/ _ \
| |_) | __/ (_| | |_| | | | __/
|____/ \___|\__,_|\__|_| |_|\___| | ReZK2LL
USERNAME : a
NOPE , YOU LOSE
Count 1348
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./BeatMe; cat inscount.out
____ _ __ __
| __ ) ___ __ _| |_| \/ | ___
| _ \ / _ \/ _` | __| |\/| |/ _ \
| |_) | __/ (_| | |_| | | | __/
|____/ \___|\__,_|\__|_| |_|\___| | ReZK2LL
USERNAME : aa
NOPE , YOU LOSE
Count 1348
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./BeatMe; cat inscount.out
____ _ __ __
| __ ) ___ __ _| |_| \/ | ___
| _ \ / _ \/ _` | __| |\/| |/ _ \
| |_) | __/ (_| | |_| | | | __/
|____/ \___|\__,_|\__|_| |_|\___| | ReZK2LL
USERNAME : aaa
PASSWORD : a
NOPE , YOU LOSE
Count 1438
user@debian:~/reversing$
____ _ __ __
| __ ) ___ __ _| |_| \/ | ___
| _ \ / _ \/ _` | __| |\/| |/ _ \
| |_) | __/ (_| | |_| | | | __/
|____/ \___|\__,_|\__|_| |_|\___| | ReZK2LL
USERNAME : a
NOPE , YOU LOSE
Count 1348
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./BeatMe; cat inscount.out
____ _ __ __
| __ ) ___ __ _| |_| \/ | ___
| _ \ / _ \/ _` | __| |\/| |/ _ \
| |_) | __/ (_| | |_| | | | __/
|____/ \___|\__,_|\__|_| |_|\___| | ReZK2LL
USERNAME : aa
NOPE , YOU LOSE
Count 1348
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./BeatMe; cat inscount.out
____ _ __ __
| __ ) ___ __ _| |_| \/ | ___
| _ \ / _ \/ _` | __| |\/| |/ _ \
| |_) | __/ (_| | |_| | | | __/
|____/ \___|\__,_|\__|_| |_|\___| | ReZK2LL
USERNAME : aaa
PASSWORD : a
NOPE , YOU LOSE
Count 1438
user@debian:~/reversing$
Necesitamos un mínimo de tres caracteres en el usuario para que el crackme nos pida un password. Verificamos entonces la longitud mínima del password:
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./BeatMe; cat inscount.out
____ _ __ __
| __ ) ___ __ _| |_| \/ | ___
| _ \ / _ \/ _` | __| |\/| |/ _ \
| |_) | __/ (_| | |_| | | | __/
|____/ \___|\__,_|\__|_| |_|\___| | ReZK2LL
USERNAME : aaa
PASSWORD : a
NOPE , YOU LOSE
Count 1438
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./BeatMe; cat inscount.out
____ _ __ __
| __ ) ___ __ _| |_| \/ | ___
| _ \ / _ \/ _` | __| |\/| |/ _ \
| |_) | __/ (_| | |_| | | | __/
|____/ \___|\__,_|\__|_| |_|\___| | ReZK2LL
USERNAME : aaa
PASSWORD : aa
NOPE , YOU LOSE
Count 1438
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./BeatMe; cat inscount.out
____ _ __ __
| __ ) ___ __ _| |_| \/ | ___
| _ \ / _ \/ _` | __| |\/| |/ _ \
| |_) | __/ (_| | |_| | | | __/
|____/ \___|\__,_|\__|_| |_|\___| | ReZK2LL
USERNAME : aaa
PASSWORD : aaa
NOPE , YOU LOSE
Count 1438
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./BeatMe; cat inscount.out
____ _ __ __
| __ ) ___ __ _| |_| \/ | ___
| _ \ / _ \/ _` | __| |\/| |/ _ \
| |_) | __/ (_| | |_| | | | __/
|____/ \___|\__,_|\__|_| |_|\___| | ReZK2LL
USERNAME : aaa
PASSWORD : aaaa
NOPE , YOU LOSE
Count 1449
user@debian:~/reversing$
____ _ __ __
| __ ) ___ __ _| |_| \/ | ___
| _ \ / _ \/ _` | __| |\/| |/ _ \
| |_) | __/ (_| | |_| | | | __/
|____/ \___|\__,_|\__|_| |_|\___| | ReZK2LL
USERNAME : aaa
PASSWORD : a
NOPE , YOU LOSE
Count 1438
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./BeatMe; cat inscount.out
____ _ __ __
| __ ) ___ __ _| |_| \/ | ___
| _ \ / _ \/ _` | __| |\/| |/ _ \
| |_) | __/ (_| | |_| | | | __/
|____/ \___|\__,_|\__|_| |_|\___| | ReZK2LL
USERNAME : aaa
PASSWORD : aa
NOPE , YOU LOSE
Count 1438
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./BeatMe; cat inscount.out
____ _ __ __
| __ ) ___ __ _| |_| \/ | ___
| _ \ / _ \/ _` | __| |\/| |/ _ \
| |_) | __/ (_| | |_| | | | __/
|____/ \___|\__,_|\__|_| |_|\___| | ReZK2LL
USERNAME : aaa
PASSWORD : aaa
NOPE , YOU LOSE
Count 1438
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./BeatMe; cat inscount.out
____ _ __ __
| __ ) ___ __ _| |_| \/ | ___
| _ \ / _ \/ _` | __| |\/| |/ _ \
| |_) | __/ (_| | |_| | | | __/
|____/ \___|\__,_|\__|_| |_|\___| | ReZK2LL
USERNAME : aaa
PASSWORD : aaaa
NOPE , YOU LOSE
Count 1449
user@debian:~/reversing$
Ya que el conteo de instrucciones cambia de 1438 a 1449, concluimos que se necesita un mínimo de cuatro caracteres en el password :)
Podemos entonces actualizar nuestro script inicial para completar los passwords a un mínimo de 3 caracteres (Sólo es necesario cambiar unas pocas líneas: las del expect, el nombre del crackme, y el segundo return):
import pexpect import time import re import string cmd = "./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./BeatMe" def send_password(password): child = pexpect.spawn(cmd) child.expect('USERNAME') child.sendline('rmolina') child.expect('PASSWORD') child.sendline(password + 'A' * (4 - len(password)) ) while True: with open('inscount.out', 'r') as f: count = re.findall('Count (.*)', f.read()) if len(count): count = count.pop() break return count def search_one(known = ''): old_char = None old_count = None for new_char in string.printable: new_count = send_password(known + new_char) if old_count is not None and new_count != old_count: if new_count > old_count: return known + new_char else: return known + old_char #return -1 break old_count = new_count old_char = new_char return -1 known = '' while True: known = search_one(known) if known == -1: break else: print "Found:", known
Y esta es la salida:
user@debian:~/reversing$ time python BeatMe.py
Found: 7
Found: 7o
Found: 7ov
Found: 7ovq
Found: 7ovqs
Found: 7ovqsp
Found: 7ovqspm
Found: 7ovqspmr
Found: 7ovqspmre
real 3m52.945s
user 0m4.728s
sys 2m58.279s
user@debian:~/reversing$
Found: 7
Found: 7o
Found: 7ov
Found: 7ovq
Found: 7ovqs
Found: 7ovqsp
Found: 7ovqspm
Found: 7ovqspmr
Found: 7ovqspmre
real 3m52.945s
user 0m4.728s
sys 2m58.279s
user@debian:~/reversing$
Confirmamos el serial que hemos encontrado:
user@debian:~/reversing$ ./BeatMe
____ _ __ __
| __ ) ___ __ _| |_| \/ | ___
| _ \ / _ \/ _` | __| |\/| |/ _ \
| |_) | __/ (_| | |_| | | | __/
|____/ \___|\__,_|\__|_| |_|\___| | ReZK2LL
USERNAME : rmolina
PASSWORD : 7ovqspmre
CORRECT , YOU WIN
user@debian:~/reversing$
____ _ __ __
| __ ) ___ __ _| |_| \/ | ___
| _ \ / _ \/ _` | __| |\/| |/ _ \
| |_) | __/ (_| | |_| | | | __/
|____/ \___|\__,_|\__|_| |_|\___| | ReZK2LL
USERNAME : rmolina
PASSWORD : 7ovqspmre
CORRECT , YOU WIN
user@debian:~/reversing$
Confirmado :)
Caso 3: jockcranley's T0AD K3YG3N
Vamos ahora a probar otro keygenme: "jockcranley's T0AD K3YG3N". Nuevamente, buscaremos el serial del usuario "rmolina":
user@debian:~/reversing$ ./toadkey32
< T0AD K3YG3N >
Username: rmolina
Password: rmolina
Access Denied.
user@debian:~/reversing$
< T0AD K3YG3N >
Username: rmolina
Password: rmolina
Access Denied.
user@debian:~/reversing$
Exploramos con el pintool la longitud mínima del password:
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./toadkey32 ; cat inscount.out
< T0AD K3YG3N >
Username: rmolina
Password: a
Access Denied.
Count 102306
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./toadkey32 ; cat inscount.out
< T0AD K3YG3N >
Username: rmolina
Password: aa
Access Denied.
Count 102315
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./toadkey32 ; cat inscount.out
< T0AD K3YG3N >
Username: rmolina
Password: aaa
Access Denied.
Count 102324
user@debian:~/reversing$
< T0AD K3YG3N >
Username: rmolina
Password: a
Access Denied.
Count 102306
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./toadkey32 ; cat inscount.out
< T0AD K3YG3N >
Username: rmolina
Password: aa
Access Denied.
Count 102315
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./toadkey32 ; cat inscount.out
< T0AD K3YG3N >
Username: rmolina
Password: aaa
Access Denied.
Count 102324
user@debian:~/reversing$
Vemos que el conteo va incrementando conforme agregamos otro caracter. Si continuamos un poco mas veremos:
A partir de 8 caracteres, el conteo no cambia. Concluimos que la longitud del password es 8 caracteres :)
Si modificamos el script anterior para usar un mínimo de 8 caracteres en lugar de 4 (y claro, actualizando también el nombre del crackme), veremos que solo se recuperan 4 caracteres antes de terminar:
Cambiamos nuevamente el segundo return (para dejarlo como en el caso 1) y de pasada agregamos una condicion de finalizacion en las ultimas lineas:
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./toadkey32 ; cat inscount.out
< T0AD K3YG3N >
Username: rmolina
Password: aaaaaaaa
Access Denied.
Count 102362
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./toadkey32 ; cat inscount.out
< T0AD K3YG3N >
Username: rmolina
Password: aaaaaaaaa
Access Denied.
Count 102362
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./toadkey32 ; cat inscount.out
< T0AD K3YG3N >
Username: rmolina
Password: aaaaaaaaaa
Access Denied.
Count 102362
user@debian:~/reversing$
< T0AD K3YG3N >
Username: rmolina
Password: aaaaaaaa
Access Denied.
Count 102362
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./toadkey32 ; cat inscount.out
< T0AD K3YG3N >
Username: rmolina
Password: aaaaaaaaa
Access Denied.
Count 102362
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./toadkey32 ; cat inscount.out
< T0AD K3YG3N >
Username: rmolina
Password: aaaaaaaaaa
Access Denied.
Count 102362
user@debian:~/reversing$
A partir de 8 caracteres, el conteo no cambia. Concluimos que la longitud del password es 8 caracteres :)
Si modificamos el script anterior para usar un mínimo de 8 caracteres en lugar de 4 (y claro, actualizando también el nombre del crackme), veremos que solo se recuperan 4 caracteres antes de terminar:
user@debian:~/reversing$ time python toadkey32.py
Found: D
Found: D@
Found: D@L
Found: D@Lh
real 7m58.693s
user 1m16.029s
sys 6m38.833s
user@debian:~/reversing$
Found: D
Found: D@
Found: D@L
Found: D@Lh
real 7m58.693s
user 1m16.029s
sys 6m38.833s
user@debian:~/reversing$
Cambiamos nuevamente el segundo return (para dejarlo como en el caso 1) y de pasada agregamos una condicion de finalizacion en las ultimas lineas:
import pexpect import time import re import string cmd = "./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/so urce/tools/ManualExamples/obj-ia32/inscount0.so -- ./toadkey32" def send_password(password): child = pexpect.spawn(cmd) child.expect('Username') child.sendline('rmolina') child.expect('Password') child.sendline(password + 'A' * (8 - len(password)) ) while True: with open('inscount.out', 'r') as f: count = re.findall('Count (.*)', f.read()) if len(count): count = count.pop() break return count def search_one(known = ''): old_char = None old_count = None for new_char in string.printable: new_count = send_password(known + new_char) if old_count is not None and new_count != old_count: if new_count > old_count: return known + new_char else: return known + old_char #return -1 break old_count = new_count old_char = new_char return -1 known = '' while True: known = search_one(known) if known == -1: break else: print "Found:", known if len(known) == 8: break
Aquí esta la salida con los tiempos (mucho mas lento que los anteriores):
user@debian:~/reversing$ time python toadkey32.py
Found: D
Found: D@
Found: D@L
Found: D@Lh
Found: D@Lh0
Found: D@Lh0\
Found: D@Lh0\T
Found: D@Lh0\TT
real 14m16.726s
user 2m56.303s
sys 10m44.432s
user@debian:~/reversing$
Found: D
Found: D@
Found: D@L
Found: D@Lh
Found: D@Lh0
Found: D@Lh0\
Found: D@Lh0\T
Found: D@Lh0\TT
real 14m16.726s
user 2m56.303s
sys 10m44.432s
user@debian:~/reversing$
Y verificamos el resultado:
user@debian:~/reversing$ ./toadkey32
< T0AD K3YG3N >
Username: rmolina
Password: D@Lh0\TT
Access Granted.
user@debian:~/reversing$
< T0AD K3YG3N >
Username: rmolina
Password: D@Lh0\TT
Access Granted.
user@debian:~/reversing$
Confirmado :)
Caso 4: NobZ's CrackmeLinux
Finalmente, veamos el crackme "NobZ's CrackmeLinux". Este no lee el serial desde la entrada estandar sino que lo recibe como argumento (no necesitaremos entonces usar pexpect).
user@debian:~/reversing$ ./CrackmeLinux
-E- Exactly one argument needed
user@debian:~/reversing$
-E- Exactly one argument needed
user@debian:~/reversing$
Vamos directamente a explorar la longitud del password:
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./CrackmeLinux a ; cat inscount.out
Try again, bro...
Count 78
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./CrackmeLinux aa ; cat inscount.out
Try again, bro...
Count 79
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./CrackmeLinux aaa ; cat inscount.out
Try again, bro...
Count 80
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./CrackmeLinux aaaa ; cat inscount.out
Try again, bro...
Count 81
user@debian:~/reversing$
Try again, bro...
Count 78
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./CrackmeLinux aa ; cat inscount.out
Try again, bro...
Count 79
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./CrackmeLinux aaa ; cat inscount.out
Try again, bro...
Count 80
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./CrackmeLinux aaaa ; cat inscount.out
Try again, bro...
Count 81
user@debian:~/reversing$
Con cada caracter que agregamos el conteo se incrementa en uno. Si seguimos incrementando eventualmente veremos lo siguiente:
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./CrackmeLinux aaaaaaaaaaaaa ; cat inscount.out
Try again, bro...
Count 90
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./CrackmeLinux aaaaaaaaaaaaaa ; cat inscount.out
Try again, bro...
Count 91
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./CrackmeLinux aaaaaaaaaaaaaaa ; cat inscount.out
Try again, bro...
Count 184
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./CrackmeLinux aaaaaaaaaaaaaaaa ; cat inscount.out
Try again, bro...
Count 93
user@debian:~/reversing$
Try again, bro...
Count 90
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./CrackmeLinux aaaaaaaaaaaaaa ; cat inscount.out
Try again, bro...
Count 91
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./CrackmeLinux aaaaaaaaaaaaaaa ; cat inscount.out
Try again, bro...
Count 184
user@debian:~/reversing$ ./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./CrackmeLinux aaaaaaaaaaaaaaaa ; cat inscount.out
Try again, bro...
Count 93
user@debian:~/reversing$
Cuando el password alcanza 15 caracteres se ejecutan muchas mas instrucciones que en los casos anteriores. Ademas, cuando el password tiene 16 caracteres, nuevamente se ejecutan pocas instrucciones. Concluimos que la longitud es 15 :)
Preparamos el siguiente script (usando subprocess en lugar de pexpect):
import subprocess import time import re import string cmd = "./pin-2.14-71313-gcc.4.4.7-linux/pin -t pin-2.14-71313-gcc.4.4.7-linux/source/tools/ManualExamples/obj-ia32/inscount0.so -- ./CrackmeLinux"
def send_password(password): password = password.replace("'", "\'") password = password.replace('"', '\"') child = subprocess.call(cmd + " '" + password + "A" * (15 - len(password)) + "'", shell=True, stdout=subprocess.PIPE) while True: with open('inscount.out', 'r') as f: count = re.findall('Count (.*)', f.read()) if len(count): count = count.pop() break return count def search_one(known = ''): old_char = None old_count = None for new_char in string.printable: new_count = send_password(known + new_char) if old_count is not None and new_count != old_count: if new_count > old_count: return known + new_char else: return known + old_char #return -1 break old_count = new_count old_char = new_char return -1 known = '' while True: known = search_one(known) if known == -1: break else: print "Found:", known
Y esta es la salida:
user@debian:~/reversing$ time python CrackmeLinux.py
Found: 0
Found: 0b
Found: 0bf
Found: 0bfu
Found: 0bfu5
Found: 0bfu5c
Found: 0bfu5c4
Found: 0bfu5c4t
Found: 0bfu5c4t3
Found: 0bfu5c4t3D
/bin/sh: 1: Syntax error: Unterminated quoted string
Found: 0bfu5c4t3D=
/bin/sh: 1: Syntax error: Unterminated quoted string
Found: 0bfu5c4t3D=-
/bin/sh: 1: Syntax error: Unterminated quoted string
Found: 0bfu5c4t3D=-_
/bin/sh: 1: Syntax error: Unterminated quoted string
Found: 0bfu5c4t3D=-_-
Found: 0bfu5c4t3D=-_-!
/bin/sh: 1: Syntax error: Unterminated quoted string
real 4m24.479s
user 0m4.756s
sys 5m29.233s
user@debian:~/reversing$
Found: 0
Found: 0b
Found: 0bf
Found: 0bfu
Found: 0bfu5
Found: 0bfu5c
Found: 0bfu5c4
Found: 0bfu5c4t
Found: 0bfu5c4t3
Found: 0bfu5c4t3D
/bin/sh: 1: Syntax error: Unterminated quoted string
Found: 0bfu5c4t3D=
/bin/sh: 1: Syntax error: Unterminated quoted string
Found: 0bfu5c4t3D=-
/bin/sh: 1: Syntax error: Unterminated quoted string
Found: 0bfu5c4t3D=-_
/bin/sh: 1: Syntax error: Unterminated quoted string
Found: 0bfu5c4t3D=-_-
Found: 0bfu5c4t3D=-_-!
/bin/sh: 1: Syntax error: Unterminated quoted string
real 4m24.479s
user 0m4.756s
sys 5m29.233s
user@debian:~/reversing$
Aunque se generan algunos errores, hemos encontrado un password. Vamos a confirmarlo:
user@debian:~/reversing$ ./CrackmeLinux 0bfu5c4t3D=-_-!
Try again, bro...
user@debian:~/reversing$
Try again, bro...
user@debian:~/reversing$
Uhm… Algo malo ha pasado aquí. Veamos de nuevo la salida:
user@debian:~/reversing$ time python CrackmeLinux.py
Found: 0
Found: 0b
Found: 0bf
Found: 0bfu
Found: 0bfu5
Found: 0bfu5c
Found: 0bfu5c4
Found: 0bfu5c4t
Found: 0bfu5c4t3
Found: 0bfu5c4t3D
/bin/sh: 1: Syntax error: Unterminated quoted string
Found: 0bfu5c4t3D=
/bin/sh: 1: Syntax error: Unterminated quoted string
Found: 0bfu5c4t3D=-
/bin/sh: 1: Syntax error: Unterminated quoted string
Found: 0bfu5c4t3D=-_
/bin/sh: 1: Syntax error: Unterminated quoted string
Found: 0bfu5c4t3D=-_-
Found: 0bfu5c4t3D=-_-!
/bin/sh: 1: Syntax error: Unterminated quoted string
real 4m24.479s
user 0m4.756s
sys 5m29.233s
user@debian:~/reversing$
Found: 0
Found: 0b
Found: 0bf
Found: 0bfu
Found: 0bfu5
Found: 0bfu5c
Found: 0bfu5c4
Found: 0bfu5c4t
Found: 0bfu5c4t3
Found: 0bfu5c4t3D
/bin/sh: 1: Syntax error: Unterminated quoted string
Found: 0bfu5c4t3D=
/bin/sh: 1: Syntax error: Unterminated quoted string
Found: 0bfu5c4t3D=-
/bin/sh: 1: Syntax error: Unterminated quoted string
Found: 0bfu5c4t3D=-_
/bin/sh: 1: Syntax error: Unterminated quoted string
Found: 0bfu5c4t3D=-_-
Found: 0bfu5c4t3D=-_-!
/bin/sh: 1: Syntax error: Unterminated quoted string
real 4m24.479s
user 0m4.756s
sys 5m29.233s
user@debian:~/reversing$
Cuando comienzan los caracteres especiales empieza el error reportando unas comillas sin cerrar (esto se debe a que la comilla es uno de los caracteres que probamos). Pero hay un caso en el que no se presenta el error, posiblemente porque aparece una comilla adicional que completa el par. Lo que imagino es que este serial incluye una comilla :S
Probaremos con comillas simples y dobles (escapandolas con la contraria):
user@debian:~/reversing$ ./CrackmeLinux "0bfu5c4t3D=-_-'"
Try again, bro...
user@debian:~/reversing$ ./CrackmeLinux '0bfu5c4t3D=-_-"'
Yeah ! You did it !
Try again, bro...
user@debian:~/reversing$ ./CrackmeLinux '0bfu5c4t3D=-_-"'
Yeah ! You did it !
Confirmado. El serial terminaba con una comilla doble :) Este crackme tenía una trampa adicional…
Conclusión
Es cosa de minutos modificar el script en python para hacer la prueba. Y la ejecución puede tomar algun tiempo tambien, pero es tiempo que podemos aprovechar haciendo otra cosa. Por lo tanto, aunque este truco sólo funcionará en algunos casos (en los que la validación el serial se va verificando paso a paso y no en bloque), bien vale la pena probar esta técnica durante un CTF :)Bonus Track :)
Cómo lo mencioné antes, la técnica también funcionaría con un strcmp. Por ejemplo, este pequeño crackme (que sería trivial resolver con strings o ltrace) también se puede resolver via conteo de instrucciones. pero les dejo de tarea implementarlo :)#include <stdio.h> #include <string.h> #include <readline/readline.h> int main() { static char *serial = (char *)NULL; serial = readline("serial: "); if (!strcmp(serial, "Secret0")) { printf("yes\n"); } else { printf("nop\n"); } return 0; }
Aquí la salida del script que preparé:
user@debian:~/reversing$ python rmolina.py
Found: S
Found: Se
Found: Sec
Found: Secr
Found: Secre
Found: Secret
Found: Secret0
Found: S
Found: Se
Found: Sec
Found: Secr
Found: Secre
Found: Secret
Found: Secret0
Toma más tiempo que los anteriores, pero igual funciona :)
Actualización
Escribí un pequeño gist que soluciona los casos basados en pexpect, incluyendo la identificación de la longitud mínima del password. Tal vez más adelante haga lo mismo para generalizar un poco más los casos basados en subprocess.
Comentarios
Publicar un comentario