Hace unos años cuando empecé a trastear con Android y animado por mi afición a la Ingeniería Inversa, decidí realizar una serie de crackmes. Los dos primeros pasaron algo desapercibidos, pero el Crackme nº3 tuvo una gran repercusión en el mundillo y, aunque no fue el primer crackme para Android ni mucho menos, si que fue uno de los más estudiados. Todos ellos fueron publicados a través de crackmes.de y el nº3 en cuestión el 6 de Noviembre de 2010. Os dejo una lista de unas cuantas webs donde aparece analizado para que veáis la repercusión que a mi parecer tuvo.
Este BTM va otra vez sobre IPs. Si amigos del séptimo arte, viendo un capítulo de mi querida «The Sinner» me han vuelto a chirriar los dientes. La verdad que viendo el capítulo no te da tiempo a apreciarlo, únicamente me quedo con que aparece una URL y lo reviso a posteriori (esto lo digo para los curiosos que me preguntáis).
En esta ocasión me tiene un poco inquieto ya que es una serie que cuida enormemente los detalles y el fallo que os voy a mostrar parece intencionado. La imagen en cuestión es esta:
Fotograma del capítulo 2×06
Aparece un buscador con una URL más o menos creíble si no fuera porque la IP que aparece es IMPOSIBLE. La máxima IPv4 es 255.255.255.255, es decir, no han dado ni una, y eso es lo que me tiene mosca. Si hubieran utilizado 82.47.25.29 hubiera quedado bien y estaríamos hablando de un problema de geolocalización de IPs, ya que el rango 82.47.xx.xx le pertenece a UK y deberíamos discernir si el servidor está en EEUU o no…
En definitiva, puede ser un fallo a propósito, un guiño o tener un significado. No se que deciros, bueno si, ¡lo investigaré!
Aquí tenemos un Crackme clásico creado por Scarebyte hallá por el año 2000 y que cuenta con varias fases siendo un crackme muy interesante para iniciarse o simplemente para divertirse. Al estar realizado en Delphi, los apartados de las checkboxes y de las trackbars se simplifican y mucho, pero aún así hay que currarselo un poco para dejar todo bien atado. Si os fijáis en las soluciones que aparecen en crackmes.de, en aquellos años se usaba DEDE y aunque yo usaré otra herramienta, DEDE sigue siendo igual de útil.
Desempacado
PEiD nos dice que nos enfrentamos a ASPack 1.08.03 -> Alexey Solodovnikov, así que vamos al lío.
Eliminar la NAG
Tan sencillo como poner un Breakpoint a User32.MessageBoxA. La llamada a NOPear está en la dirección 441CF2.
Password
Desde las string references localizamos los mensajes de chico bueno y chico malo que nos llevan al código a analizar.
0044C3CD |. E8 5294FDFF CALL CrackMe_.00425824
0044C3D2 |. 8B45 FC MOV EAX,[LOCAL.1]
0044C3D5 |. E8 9A76FBFF CALL CrackMe_.00403A74
0044C3DA |. 83F8 0C CMP EAX,0C ; Lengh C = 12
0044C3DD |. 0F85 53010000 JNZ CrackMe_.0044C536 ; Salto a chico malo
0044C3E3 |. 8D55 FC LEA EDX,[LOCAL.1]
0044C3E6 |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8]
0044C3EC |. E8 3394FDFF CALL CrackMe_.00425824
0044C3F1 |. 8B45 FC MOV EAX,[LOCAL.1]
0044C3F4 |. 8038 43 CMP BYTE PTR DS:[EAX],43 ; 1º dígito serial = C
0044C3F7 |. 0F85 27010000 JNZ CrackMe_.0044C524 ; Salto a chico malo
0044C3FD |. 8D55 F8 LEA EDX,[LOCAL.2]
0044C400 |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8]
0044C406 |. E8 1994FDFF CALL CrackMe_.00425824
0044C40B |. 8B45 F8 MOV EAX,[LOCAL.2]
0044C40E |. 8078 03 6F CMP BYTE PTR DS:[EAX+3],6F ; 4º dígito serial = o
0044C412 |. 0F85 0C010000 JNZ CrackMe_.0044C524 ; Salto a chico malo
0044C418 |. 8D55 F4 LEA EDX,[LOCAL.3]
0044C41B |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8]
0044C421 |. E8 FE93FDFF CALL CrackMe_.00425824
0044C426 |. 8B45 F4 MOV EAX,[LOCAL.3]
0044C429 |. 8078 08 6F CMP BYTE PTR DS:[EAX+8],6F ; 9º dígito serial = o
0044C42D |. 0F85 F1000000 JNZ CrackMe_.0044C524 ; Salto a chico malo
0044C433 |. 8D55 F0 LEA EDX,[LOCAL.4]
0044C436 |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8]
0044C43C |. E8 E393FDFF CALL CrackMe_.00425824
0044C441 |. 8B45 F0 MOV EAX,[LOCAL.4]
0044C444 |. 8078 01 6C CMP BYTE PTR DS:[EAX+1],6C ; 2º dígito serial = l
0044C448 |. 0F85 D6000000 JNZ CrackMe_.0044C524 ; Salto a chico malo
0044C44E |. 8D55 EC LEA EDX,[LOCAL.5]
0044C451 |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8]
0044C457 |. E8 C893FDFF CALL CrackMe_.00425824
0044C45C |. 8B45 EC MOV EAX,[LOCAL.5]
0044C45F |. 8078 04 20 CMP BYTE PTR DS:[EAX+4],20 ; 5º dígito serial = espacio
0044C463 |. 0F85 BB000000 JNZ CrackMe_.0044C524 ; Salto a chico malo
0044C469 |. 8D55 E8 LEA EDX,[LOCAL.6]
0044C46C |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8]
0044C472 |. E8 AD93FDFF CALL CrackMe_.00425824
0044C477 |. 8B45 E8 MOV EAX,[LOCAL.6]
0044C47A |. 8078 0A 52 CMP BYTE PTR DS:[EAX+A],52 ; 11º dígito serial = R
0044C47E |. 0F85 A0000000 JNZ CrackMe_.0044C524 ; Salto a chico malo
0044C484 |. 8D55 E4 LEA EDX,[LOCAL.7]
0044C487 |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8]
0044C48D |. E8 9293FDFF CALL CrackMe_.00425824
0044C492 |. 8B45 E4 MOV EAX,[LOCAL.7]
0044C495 |. 8078 07 75 CMP BYTE PTR DS:[EAX+7],75 ; 8º dígito serial = u
0044C499 |. 0F85 85000000 JNZ CrackMe_.0044C524 ; Salto a chico malo
0044C49F |. 8D55 E0 LEA EDX,[LOCAL.8]
0044C4A2 |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8]
0044C4A8 |. E8 7793FDFF CALL CrackMe_.00425824
0044C4AD |. 8B45 E0 MOV EAX,[LOCAL.8]
0044C4B0 |. 8078 09 6E CMP BYTE PTR DS:[EAX+9],6E ; 10º dígito serial = n
0044C4B4 |. 75 6E JNZ SHORT CrackMe_.0044C524 ; Salto a chico malo
0044C4B6 |. 8D55 DC LEA EDX,[LOCAL.9]
0044C4B9 |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8]
0044C4BF |. E8 6093FDFF CALL CrackMe_.00425824
0044C4C4 |. 8B45 DC MOV EAX,[LOCAL.9]
0044C4C7 |. 8078 02 6E CMP BYTE PTR DS:[EAX+2],6E ; 3º dígito serial = n
0044C4CB |. 75 57 JNZ SHORT CrackMe_.0044C524 ; Salto a chico malo
0044C4CD |. 8D55 D8 LEA EDX,[LOCAL.10]
0044C4D0 |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8]
0044C4D6 |. E8 4993FDFF CALL CrackMe_.00425824
0044C4DB |. 8B45 D8 MOV EAX,[LOCAL.10]
0044C4DE |. 8078 05 69 CMP BYTE PTR DS:[EAX+5],69 ; 6º dígito serial = i
0044C4E2 |. 75 40 JNZ SHORT CrackMe_.0044C524 ; Salto a chico malo
0044C4E4 |. 8D55 D4 LEA EDX,[LOCAL.11]
0044C4E7 |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8]
0044C4ED |. E8 3293FDFF CALL CrackMe_.00425824
0044C4F2 |. 8B45 D4 MOV EAX,[LOCAL.11]
0044C4F5 |. 8078 0B 6E CMP BYTE PTR DS:[EAX+B],6E ; 12º dígito serial = n
0044C4F9 |. 75 29 JNZ SHORT CrackMe_.0044C524 ; Salto a chico malo
0044C4FB |. 8D55 D0 LEA EDX,[LOCAL.12]
0044C4FE |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8]
0044C504 |. E8 1B93FDFF CALL CrackMe_.00425824
0044C509 |. 8B45 D0 MOV EAX,[LOCAL.12]
0044C50C |. 8078 06 67 CMP BYTE PTR DS:[EAX+6],67 ; 7º dígito serial = g
0044C510 |. 75 12 JNZ SHORT CrackMe_.0044C524 ; Salto a chico malo
0044C512 |. BA 78C54400 MOV EDX,CrackMe_.0044C578 ; ASCII "Right Password"
0044C517 |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8]
0044C51D |. E8 3293FDFF CALL CrackMe_.00425854
0044C522 |. EB 22 JMP SHORT CrackMe_.0044C546
0044C524 |> BA 90C54400 MOV EDX,CrackMe_.0044C590 ; ASCII "Wrong Password"
0044C529 |. 8B83 E8020000 MOV EAX,DWORD PTR DS:[EBX+2E8]
0044C52F |. E8 2093FDFF CALL CrackMe_.00425854
0044C534 |. EB 10 JMP SHORT CrackMe_.0044C546
0044C536 |> BA 90C54400 MOV EDX,CrackMe_.0044C590 ; ASCII "Wrong Password"
Chequeo rápido
ABCD EFGHIJK
Clno iguonRn
; 1º dígito serial = C
; 4º dígito serial = o
; 9º dígito serial = o
; 2º dígito serial = l
; 5º dígito serial = espacio
; 11º dígito serial = R
; 8º dígito serial = u
; 10º dígito serial = n
; 3º dígito serial = n
; 6º dígito serial = i
; 12º dígito serial = n
; 7º dígito serial = g
Básicamente chequea la frase «Cool Running» de forma desordenada como se ve justo encima, siendo el password correcto «Clno iguonRn«. Os dejo el código para que lo analicéis.
Nº serie asociado a un nombre
De nuevo con las string references localizamos el código.
0044C648 /. 55 PUSH EBP
0044C649 |. 8BEC MOV EBP,ESP
0044C64B |. 83C4 F8 ADD ESP,-8
0044C64E |. 53 PUSH EBX
0044C64F |. 56 PUSH ESI
0044C650 |. 33C9 XOR ECX,ECX
0044C652 |. 894D F8 MOV [LOCAL.2],ECX
0044C655 |. 8BF0 MOV ESI,EAX
0044C657 |. 33C0 XOR EAX,EAX
0044C659 |. 55 PUSH EBP
0044C65A |. 68 83C74400 PUSH CrackMe_.0044C783
0044C65F |. 64:FF30 PUSH DWORD PTR FS:[EAX]
0044C662 |. 64:8920 MOV DWORD PTR FS:[EAX],ESP
0044C665 |. 33C0 XOR EAX,EAX
0044C667 |. 8945 FC MOV [LOCAL.1],EAX
0044C66A |. A1 80F84400 MOV EAX,DWORD PTR DS:[44F880] ; Eax = Nombre
0044C66F |. E8 0074FBFF CALL CrackMe_.00403A74
0044C674 |. 83F8 06 CMP EAX,6 ; Cmp lengh nombre con 6
0044C677 |. 0F8E F0000000 JLE CrackMe_.0044C76D ; Salta si <= 6
0044C67D |. A1 80F84400 MOV EAX,DWORD PTR DS:[44F880] ; Eax = Nombre
0044C682 |. E8 ED73FBFF CALL CrackMe_.00403A74
0044C687 |. 83F8 14 CMP EAX,14 ; Cmp lengh nombre con 20 (14h)
0044C68A |. 0F8D DD000000 JGE CrackMe_.0044C76D ; salta si >= 20
0044C690 |. A1 80F84400 MOV EAX,DWORD PTR DS:[44F880]
0044C695 |. E8 DA73FBFF CALL CrackMe_.00403A74
0044C69A |. 85C0 TEST EAX,EAX
0044C69C |. 7E 17 JLE SHORT CrackMe_.0044C6B5
0044C69E |. BA 01000000 MOV EDX,1
0044C6A3 |> 8B0D 80F84400 /MOV ECX,DWORD PTR DS:[44F880] ; Bucle in
0044C6A9 |. 0FB64C11 FF |MOVZX ECX,BYTE PTR DS:[ECX+EDX-1]
0044C6AE |. 014D FC |ADD [LOCAL.1],ECX ; Suma dig nombre y guarda en 12FBC4
0044C6B1 |. 42 |INC EDX
0044C6B2 |. 48 |DEC EAX
0044C6B3 |.^ 75 EE \JNZ SHORT CrackMe_.0044C6A3 ; Bucle out
0044C6B5 |> A1 84F84400 MOV EAX,DWORD PTR DS:[44F884] ; Eax = Compañia
0044C6BA |. E8 B573FBFF CALL CrackMe_.00403A74
0044C6BF |. 83F8 02 CMP EAX,2 ; Cmp lengh compañia con 2
0044C6C2 |. 7E 18 JLE SHORT CrackMe_.0044C6DC ; Salta si <= 2
0044C6C4 |. A1 84F84400 MOV EAX,DWORD PTR DS:[44F884] ; Eax = Compañia
0044C6C9 |. E8 A673FBFF CALL CrackMe_.00403A74
0044C6CE |. 83F8 08 CMP EAX,8 ; Cmp lengh compañia con 8
0044C6D1 |. 7D 09 JGE SHORT CrackMe_.0044C6DC ; Salta si >= 8
0044C6D3 |. 8B45 FC MOV EAX,[LOCAL.1] ; Eax = sum nombre
0044C6D6 |. 6BC0 02 IMUL EAX,EAX,2 ; Sum nombre * 2
0044C6D9 |. 8945 FC MOV [LOCAL.1],EAX
0044C6DC |> 68 98C74400 PUSH CrackMe_.0044C798 ; ASCII "I Love Cracking and "
0044C6E1 |. 8D55 F8 LEA EDX,[LOCAL.2]
0044C6E4 |. 8B45 FC MOV EAX,[LOCAL.1]
0044C6E7 |. E8 68B0FBFF CALL CrackMe_.00407754
0044C6EC |. FF75 F8 PUSH [LOCAL.2] ; sum del nombre
0044C6EF |. 68 B8C74400 PUSH CrackMe_.0044C7B8 ; ASCII " Girls ;)"
0044C6F4 |. B8 8CF84400 MOV EAX,CrackMe_.0044F88C
0044C6F9 |. BA 03000000 MOV EDX,3
0044C6FE |. E8 3174FBFF CALL CrackMe_.00403B34 ; Concatena 1º frase + sum nombre + 2ºfrase
0044C703 |. 33C0 XOR EAX,EAX
0044C705 |. 8945 FC MOV [LOCAL.1],EAX
0044C708 |. A1 88F84400 MOV EAX,DWORD PTR DS:[44F888] ; Eax = Serial
0044C70D |. E8 6273FBFF CALL CrackMe_.00403A74
0044C712 |. 8BD8 MOV EBX,EAX
0044C714 |. A1 8CF84400 MOV EAX,DWORD PTR DS:[44F88C]
0044C719 |. E8 5673FBFF CALL CrackMe_.00403A74
0044C71E |. 3BD8 CMP EBX,EAX ; Compara tamaño frase con tamaño serial
0044C720 |. 75 4B JNZ SHORT CrackMe_.0044C76D
0044C722 |. A1 88F84400 MOV EAX,DWORD PTR DS:[44F888]
0044C727 |. E8 4873FBFF CALL CrackMe_.00403A74
0044C72C |. 85C0 TEST EAX,EAX
0044C72E |. 7E 27 JLE SHORT CrackMe_.0044C757
0044C730 |. BA 01000000 MOV EDX,1
0044C735 |> 8B0D 88F84400 /MOV ECX,DWORD PTR DS:[44F888] ; Bucle in -->
0044C73B |. 0FB64C11 FF |MOVZX ECX,BYTE PTR DS:[ECX+EDX-1]
0044C740 |. 034D FC |ADD ECX,[LOCAL.1]
0044C743 |. 8B1D 8CF84400 |MOV EBX,DWORD PTR DS:[44F88C]
0044C749 |. 0FB65C13 FF |MOVZX EBX,BYTE PTR DS:[EBX+EDX-1] ; Compara dígito a dígito nuestro serial
0044C74E |. 2BCB |SUB ECX,EBX ; con la concatenación anterior
0044C750 |. 894D FC |MOV [LOCAL.1],ECX
0044C753 |. 42 |INC EDX
0044C754 |. 48 |DEC EAX
0044C755 |.^ 75 DE \JNZ SHORT CrackMe_.0044C735 ; <-- Bucle out
0044C757 |> 837D FC 00 CMP [LOCAL.1],0
0044C75B |. 75 10 JNZ SHORT CrackMe_.0044C76D ; Salta si algo ha ido mal
0044C75D |. 8B86 14030000 MOV EAX,DWORD PTR DS:[ESI+314]
0044C763 |. BA CCC74400 MOV EDX,CrackMe_.0044C7CC ; "You have found the correct Serial :)"
En resumen
Tamaño del nombre entre 7 y 19.
Tamaño de la compañía entre 3 y 7 aunque no interviene en el serial.
Suma los valores ascii de los dígitos del nombre y lo multiplica por 2.
Concatena «I Love Cracking and » + «sum del nombre» + » Girls ;)».
Checkbox
Para afrontar esta parte del reto vamos a usar una herramienta llamada Interactive Delphi Reconstructoro IDR. En su día la mejor herramienta era DEDE, pero IDR a mi parecer es algo más potente.
Básicamente IDR nos permite sin quebraderos de cabeza localizar el código del botón que comprueba la secuencia de checkboxes correcta. Cargamos el crackme en IDR y dentro de la pestaña «Units (F2)«, abajo del todo hacemos doble click sobre «F Crack» y vemos que nos muestra todos los controles del formulario. El botón que nos interesa se llama «SpeedButton3«.
Si hacemos doble click sobre el nos muestra el código que se muestra a continuación.
Como podéis apreciar, las checkboxes involucradas son la 3, 5, 6, 9, 11, 12, 13, 15, 19 y 20. Solo nos falta saber cuales se corresponden con esa numeración y aquí ya depende de cada uno, yo en su día saqué los números a mano mediante el orden de tabulación, pero ya que tenemos IDR, el nos va a dar la solución de una forma sencilla y rápida.
Vamos a la pestaña «Forms (F5)«, seleccionamos la opción Form y hacemos doble click sobre el formulario.
Veréis que aparece el formulario con todos los recursos, incluso los puedes modificar. Localizar los checkboxes ahora es un juego de niños.
Os dejo un vídeo.
Trackbar
De nuevo, con la ayuda de IDR, localizamos la parte del código y analizamos su funcionamiento. Esta parte es la más divertida ya que requiere de un keygen pero en vez de coger el número de serie de una caja de texto lo obtiene de 5 trackbars como muestra la siguiente imagen.
1) Siendo nuestro serial : 1 2 3 4 5
a b c d e
2) Realiza las operaciones matemáticas:
Round(((Cos(sqrt(b^3+5)) + (-sqrt(a+1)) + Ln(c*3+1) + (-sqrt(d+2)) + ((e*3)/2))+0.37)*1000))
3) Obtenemos un hash resultante de 5415
4) XORea los dígitos de la siguiente manera:
(5)35 xor 86 = B6
(4)34 xor 83 = BD
(1)31 xor 86 = B7
(5)35 xor 8D = B8
De modo que tenemos B6BDB7B8
5) Compara B6BDB7B8 con B5BAB2BA
6) Revertimos el XOR para obtener el hash bueno
B5 xor 86 = 36(6)
BA xor 83 = 33(3)
B2 xor 86 = 34(4)
BA xor 8D = 37(7)
Luego el hash bueno es 6347
7) Debemos hacer fuerza bruta buscando:
Round(((Cos(sqrt(b^3+5)) + (-sqrt(a+1)) + Ln(c*3+1) + (-sqrt(d+2)) + ((e*3)/2))+0.37)*1000)) = 6347
Para obtener los seriales válidos podemos hacer bucles recursivos hasta recorrer las 10^5 opciones posibles. Una forma de hacerlo en VBNet es la siguiente.
Dim tmp As Double
Dim an, bn, cn, dn, en As Integer
For an = 0 To 9
For bn = 0 To 9
For cn = 0 To 9
For dn = 0 To 9
For en = 0 To 9
tmp = Round(((Cos(Sqrt((Pow(bn, 3)) + 5)) + (-Sqrt(an + 1)) + Log(cn * 3 + 1) + (-Sqrt(dn + 2)) + ((en * 3) / 2) + 0.37) * 1000))
txtdebug.Text = "a-b-c-d-e = Hash || " & an & "-" & bn & "-" & cn & "-" & dn & "-" & en & " = " & tmp
If tmp = 6347 Then
ListBox1.Items.Add("Serial: " & an & bn & cn & dn & en)
End If
Application.DoEvents()
Next
Next
Next
Next
Next
Os dejo como siempre el crackme y el keygen en los enlaces.
Este BTM va otra vez sobre IPs. Si amigos del séptimo arte, viendo un capítulo de mi querida «The Sinner» me han vuelto a chirriar los dientes. La verdad que viendo el capítulo no te da tiempo a apreciarlo, únicamente me quedo con que aparece una URL y lo reviso a posteriori (esto lo digo para los curiosos que me preguntáis).
En esta ocasión me tiene un poco inquieto ya que es una serie que cuida enormemente los detalles y el fallo que os voy a mostrar parece intencionado. La imagen en cuestión es esta:
Fotograma del capítulo 2×06
Aparece un buscador con una URL más o menos creíble si no fuera porque la IP que aparece es IMPOSIBLE. La máxima IPv4 es 255.255.255.255, es decir, no han dado ni una, y eso es lo que me tiene mosca. Si hubieran utilizado 82.47.25.29 hubiera quedado bien y estaríamos hablando de un problema de geolocalización de IPs, ya que el rango 82.47.xx.xx le pertenece a UK y deberíamos discernir si el servidor está en EEUU o no…
En definitiva, puede ser un fallo a propósito, un guiño o tener un significado. No se que deciros, bueno si, ¡lo investigaré!
Acabo de montar AperiSolve en una Raspi que tenía por casa pensando que sería coser y cantar, pero me he encontrado con que el repositorio no estaba preparado para todas las distros Linux de forma estándar. El resultado lo he colgado en Github, de modo que para montarlo en vuestra propia Raspi solo tenéis que seguir estos pasos:
1. Clonar el repositorio
git clone https://github.com/deurus/AperiSolve-Raspi3.git
cd AperiSolve-Raspi3/AperiSolve
2. Construir los contenedores
docker compose build
docker compose up -d
3. Abrir la web
http://<IP_RASPI>:5000
Si tenéis curiosidad de la adaptación que he tenido que hacer aquí están los pasos que he seguido:
1. Preparar el sistema
sudo apt update
sudo apt install -y git docker.io docker-compose
sudo usermod -aG docker $USER
newgrp docker
2. Clonar AperiSolve
git clone https://github.com/Zeecka/AperiSolve.git
cd AperiSolve
3. Crear la estructura de build para la imagen ARM/x86
nano docker-compose.yml
y pega este contenido:
FROM python:3.11-slim
RUN apt-get update && apt-get install -y \
zip \
p7zip-full \
binwalk \
foremost \
exiftool \
steghide \
ruby \
binutils \
pngcheck \
&& rm -rf /var/lib/apt/lists/*
COPY aperisolve/ /aperisolve/
RUN pip install --no-cache-dir -r /aperisolve/requirements.txt
WORKDIR /aperisolve
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "wsgi:app"]
4. Arreglar docker-compose.yml para ser válido y compatible
services:
web:
image: aperisolve-local
build: .
container_name: aperisolve-web
ports:
- "5000:5000"
depends_on:
- redis
- postgres
environment:
DB_URI: "postgresql://aperiuser:aperipass@postgres:5432/aperisolve"
worker:
image: aperisolve-local
container_name: aperisolve-worker
depends_on:
- redis
- postgres
environment:
DB_URI: "postgresql://aperiuser:aperipass@postgres:5432/aperisolve"
redis:
image: redis:7
container_name: aperisolve-redis
postgres:
image: postgres:16
container_name: aperisolve-postgres
environment:
POSTGRES_USER: aperiuser
POSTGRES_PASSWORD: aperipass
POSTGRES_DB: aperisolve
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
5. Modificar aperisolve/config.py
nano config.py
y pega este contenido:
from pathlib import Path
IMAGE_EXTENSIONS = [".png", ".jpg", ".jpeg", ".gif", ".bmp", ".webp", ".tiff"]
WORKER_FILES = ["binwalk", "foremost", "steghide", "zsteg"]
RESULT_FOLDER = Path(__file__).parent.resolve() / "results"
RESULT_FOLDER.mkdir(parents=True, exist_ok=True)
6. Modificación de aperisolve/app.py
Sustituir la línea: app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get("DB_URI")
por:
default_db = "postgresql://aperiuser:aperipass@postgres:5432/aperisolve"
app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get("DB_URI", default_db)
7. Construir la imagen
docker build -t aperisolve-local .
8. Levantar los contenedores
docker compose down
docker compose up -d
9. Comprobar logs
docker logs aperisolve-web --tail=50
docker logs aperisolve-worker --tail=50
10. Acceder a la web
- Desde cualquier máquina de la red local: http://IP-DE-LA-MAQUINA:5000
- Desde la Raspi: http://localhost:5000
11. Limpieza (cuando necesites)
- Reiniciar contenedores:
docker compose restart
- Borrar resultados antiguos:
sudo rm -r aperisolve/results/*
Hoy tenemos aquí un crackme del año 2000 empacado y con un algoritmo aunque no muy complicado largo de tracear. Está empacado varias veces, algo poco habitual pero recordemos que es un crackme antiguo. Tras el empacado se encuentra Delphi.
Si lo pasamos por PEiD nos dice que Aspack 2.1, Exeinfo no está muy seguro y RDG packer detector en el escaneo avanzado nos encuentra Aspack, UPX y PE-Pack.
En principio nos enfrentamos a Aspack 2.1, abrimos el crackme con OllyDbg y vemos el típico PUSHAD.
Pulsamos F8 (Step Over) y a continuación click derecho sobre el registro ESP y Follow in DUMP.
Seleccionamos los primeros cuatro bytes útiles del dump y les ponemos un Breakpoint de Hardware, Access y Dword.
Pulsamos F9 y nos para aquí:
Ya tenemos a Aspack contra las cuerdas, pulsamos F8 hasta después del RETN para llegar al OEP (Original Entry Point).
Pero en el supuesto OEP vemos otro PUSHAD por lo que esto no ha terminado. Investigando un poco más vemos que la segunda capa se corresponde con PE-PACK 1.0. La estrategia a seguir es la misma, como ya tenemos el breakpoint puesto pulsamos F9 y nos para aquí:
Pulsamos F8 y nos llega a otro PUSHAD. Esta vez es UPX.
Pulsamos de nuevo F9 y paramos aquí:
Pulsamos F8 y esta vez si llegamos al OEP (4576EC).
A continuación vamos a dumpear el archivo en memoria. Vamos a plugins > OllyDumpEX, pulsamos sobre «Get EIP as OEP» y finalmente sobre «Dump«.
Minimizamos Olly (no cerrar), abrimos el programa ImportREC y seleccionamos el ejecutable «Sweeet1.exe».
Pegamos el OEP original (576EC), le damos a AutoSearch y a continuación a Get Imports.
Finalmente pulsamos Fix Dump y elegimos el ejecutable dumpeado anteriormente. Esto nos genera un ejecutable dumpeado que es el ejecutable válido.
Ahora PEiD nos dice que estamos tratando con un crackme hecho en Delphi.
Hemos pasado por tres capas de compresión casi idénticas, vamos a analizarlas.
El algoritmo
Cuando abrimos el crackme nos fijamos en que genera una key. Esta key se genera en función del disco duro desde el que se ejecuta.
Como la secuencia de generación del serial válido es larga os pongo lo más importante muy resumido y con ejemplos como siempre.
El serial es del siguiente tipo:
Serial = 1ªParte-2ªParte-3ªParte
Serial = 0000XXXXX-SerialCalculado-xxxx000Z8
Comprobación del tamaño del nombre
----------------------------------
........
00456EAA E8 01CCFAFF CALL sweeet1_Fix_dump_rebuilded.00403AB0
00456EAF 83F8 04 CMP EAX,4 ------------------------------------------------; Nombre >=4
00456EB2 7D 13 JGE SHORT sweeet1_Fix_dump_rebuilded.00456EC7
00456EB4 A1 08954500 MOV EAX,DWORD PTR DS:[sweeet1_Fix_dump_rebuilded.459508]
00456EB9 8B00 MOV EAX,DWORD PTR DS:[EAX]
00456EBB E8 0869FEFF CALL sweeet1_Fix_dump_rebuilded.0043D7C8
00456EC0 BB 01000000 MOV EBX,1
00456EC5 EB 15 JMP SHORT sweeet1_Fix_dump_rebuilded.00456EDC
00456EC7 83FB 25 CMP EBX,25
00456ECA 7D 0E JGE SHORT sweeet1_Fix_dump_rebuilded.00456EDA
00456ECC 83C3 32 ADD EBX,32
00456ECF 83C3 1E ADD EBX,1E
00456ED2 83EB 4F SUB EBX,4F
00456ED5 83FB 25 CMP EBX,25 -----------------------------------------------; Nombre <=25
00456ED8 ^ 7C F2 JL SHORT sweeet1_Fix_dump_rebuilded.00456ECC
00456EDA 33DB XOR EBX,EBX
00456EDC 33C0 XOR EAX,EAX
........
1ºBucle - Nuestro nombre (A)
----------------------------
........
00456F55 BE 1B000000 MOV ESI,1B -------------------------------; ESI = 1B
00456F5A EB 21 JMP SHORT sweeet1_dump_.00456F7D
00456F5C 8D55 D4 LEA EDX,[EBP-2C]
00456F5F A1 34A84500 MOV EAX,DWORD PTR DS:[sweeet1_dump_.45A8
00456F64 8B80 C4020000 MOV EAX,DWORD PTR DS:[EAX+2C4]
00456F6A E8 B5DAFCFF CALL sweeet1_dump_.00424A24
00456F6F 8B45 D4 MOV EAX,DWORD PTR SS:[EBP-2C]
00456F72 0FB64418 FF MOVZX EAX,BYTE PTR DS:[EBX+EAX-1]---------; Coje digito
00456F77 03F0 ADD ESI,EAX ------------------------------; digito + ESI
00456F79 43 INC EBX
00456F7A 0FAFF3 IMUL ESI,EBX ----------------------------; multiplica por i (bucle)
00456F7D 8D55 D4 LEA EDX,[EBP-2C]
........
2ºBucle - La key (B)
--------------------
........
00456F9C |. BF 1A000000 MOV EDI,1A -------------------------;EDI = 1A
00456FA1 |. BB 01000000 MOV EBX,1
00456FA6 |. EB 1E JMP SHORT sweeet1_.00456FC6
00456FA8 |> 8D55 D4 /LEA EDX,[LOCAL.11]
00456FAB |. A1 34A84500 |MOV EAX,DWORD PTR DS:[45A834]
00456FB0 |. 8B80 D0020000 |MOV EAX,DWORD PTR DS:[EAX+2D0]
00456FB6 |. E8 69DAFCFF |CALL sweeet1_.00424A24
00456FBB |. 8B45 D4 |MOV EAX,[LOCAL.11]
00456FBE |. 0FB64418 FF |MOVZX EAX,BYTE PTR DS:[EAX+EBX-1]--;Coje dígito
00456FC3 |. 03F8 |ADD EDI,EAX -----------------------;Suma dígito a dígito
00456FC5 |. 43 |INC EBX
00456FC6 |> 8D55 D4 LEA EDX,[LOCAL.11]
00456FC9 |. A1 34A84500 |MOV EAX,DWORD PTR DS:[45A834]
00456FCE |. 8B80 D0020000 |MOV EAX,DWORD PTR DS:[EAX+2D0]
00456FD4 |. E8 4BDAFCFF |CALL sweeet1_.00424A24
00456FD9 |. 8B45 D4 |MOV EAX,[LOCAL.11]
00456FDC |. E8 CFCAFAFF |CALL sweeet1_.00403AB0
00456FE1 |. 3BD8 |CMP EBX,EAX
00456FE3 |.^ 7C C3 \JL SHORT sweeet1_.00456FA8
........
Generación del serial central
-----------------------------
........
00456FE5 |. B9 01000000 MOV ECX,1
00456FEA |. BB 01000000 MOV EBX,1
00456FEF |. 8BC7 MOV EAX,EDI
00456FF1 |. F7EE IMUL ESI ----------; C = A * B
00456FF3 |. 99 CDQ
........
00456FFD |. 2345 E8 AND EAX,[LOCAL.6]--; D = A and C
00457000 |. 2355 EC AND EDX,[LOCAL.5]
00457003 |. 8945 E8 MOV [LOCAL.6],EAX
00457006 |. 8955 EC MOV [LOCAL.5],EDX
........
00457032 |. 8BC7 MOV EAX,EDI
00457034 |. 99 CDQ
00457035 |. 0345 E8 ADD EAX,[LOCAL.6]--; E = D + B
00457038 |. 1355 EC ADC EDX,[LOCAL.5]
0045703B |. 8945 E0 MOV [LOCAL.8],EAX
0045703E |. 8955 E4 MOV [LOCAL.7],EDX
........
00405732 8B4424 10 MOV EAX,DWORD PTR SS:[ESP+10]
00405736 F72424 MUL DWORD PTR SS:[ESP]
00405739 8BC8 MOV ECX,EAX
0040573B 8B4424 04 MOV EAX,DWORD PTR SS:[ESP+4]
0040573F F76424 0C MUL DWORD PTR SS:[ESP+C]------; F = B * D
00405743 03C8 ADD ECX,EAX
00405745 8B0424 MOV EAX,DWORD PTR SS:[ESP]
00405748 F76424 0C MUL DWORD PTR SS:[ESP+C]------; G = A * F
........
0045705E |. 0B0424 OR EAX,DWORD PTR SS:[ESP]-----; Serial central = G or A
........
00457077 |. E8 FC07FBFF CALL sweeet1_.00407878
0045707C |. 8B45 F8 MOV EAX,[LOCAL.2]-------------; EAX = Serial central
........
004570D1 |. E8 A207FBFF CALL sweeet1_.00407878
004570D6 |. 8B45 D0 MOV EAX,[LOCAL.12]
004570D9 |. E8 D2C9FAFF CALL sweeet1_.00403AB0--------; Obtiene longitud del serial central en hexa
004570DE |. 8BD8 MOV EBX,EAX
........
004570D1 |. E8 A207FBFF CALL sweeet1_.00407878--------;*Nota
*Nota:
A partir de aquí genera la primera y tercera parte del serial de la siguiente manera:
Serial = 1ªParte-2ªParte-3ªParte
Serial = 0000XXXXX-SerialCalculado-xxxx000Z8
1ºParte = 3ºdigSerial+1ºdigSerial+2ºdigSerial+3ºdigSerial+4ºdigNombreMayu+2ºdigNombreMayu+5ºdigNombreMayu+1ºdigNombreMayu+3ºdigNombreMayu
3ºParte = 3ºdigNombreMin+1ºdigNombreMin+4ºdigNombreMin+2ºdigNombreMin+Tamaño Serial_2ªParte en Hex y de tres dígitos+Z8
Ejemplo:
Nombre: deurus
Key: C0C0A000
Serial: 6906REUDU-906297047918-udre00CZ8
1) A = 23A2A (Con nuestro nombre empezando por 1B se lo suma a ESI y se lo multiplica por i (la que toque cada vez))
2) B = 1A1 (Con nuestra Key empezando por 1A va sumando los digitos)
3) C = B * A = 3A0BE6A
4) D = A and C = 3A2A
5) E = D + B = 3BCB (Offset 457035)
6) F = B * D = 5EBE6A (Offset 48704A)
7) G = A * F = D303834164
8) Serial = G or A (Serial = D303834164 or 23A2A = D303837B6E (906297047918))
A tener en cuenta:
1ªParte del serial siempre mayúsculas.
2ªParte siempre numérico. Usa el registro de 64 bits (Qword) con signo.**Nota
Warning: This challenge is still active and therefore should not be resolved using this information. Aviso: Este reto sigue en activo y por lo tanto no se debería resolver utilizando esta información.
Realistic Challenge 1: Your friend tried to purchase some software off a company. But after he paid they decided to increase it’s price by a large amount. They are now refusing to send it him. Get them back by getting their most expensive software a lot cheaper than they intended you to.
Lo que nos dice el enunciado del reto a groso modo es que debemos cambiar el precio del software antes de comprarlo.
Firebug
Para resolver este reto basta con tener instalado el complemento para Firefox «Firebug«. Abrimos la web y echamos un vistazo con Firebug
Vemos un parámetro oculto que se llama «amount» y que tiene un valor de 100$. Basta con cambiarlo a 00,01$ y ya tenemos resuelto el reto.
Esta vez vamos a analizar los CrackMes de un antiguo colaborador de Karpoff Spanish Tutor, CrkViZ. En estas cinco soluciones vamos a pelearnos con Visual Basic 5/6 nativo y Pcode, con el registro de Windows y tendremos que parchear algúna rutina antidebug. Los CrackMes son del año 2000 y aunque algunos estaban ya solucionados, los analizaremos igualmente para ver la diferencia que existe con los análisis realizados en aquellos años, sí, estamos hablando del Softice.
Herramientas disponibles
Cuando hablamos de Visual Basic 5/6, podemos destacar 3 herramientas que nos facilitan mucho la vida, VB Decompiler, VB Reformer y ExDec. Las dos primeras se defienden bien tanto con código nativo como pcode y ExDec solamente nos sirve para pcode. Aún así, si todo lo demás falla, Ollydbg nos sacará de apuros.
CrkViz-1
Este primer crackme está compilado en Pcode y hoy día, con las herramientas de que disponemos no supone ninguna dificultad. Tan solo debemos abrirlo con VB Decompiler y ya nos encontramos con el serial válido.
Los opcodes obtenidos con ExDec se ven de la siguiente manera.
Este segundo crackme también está compilado en pcode. La rutina del serial es muy sencilla pero al introducir un número aleatorio nos obliga a parchear. Cargamos el crackme en VB Decompiler y nos muestra esto:
Básicamente vemos que genera un número aleatorio entre 1 y 999999999 y luego le suma 1. La forma de afrontar esto es parcheando. Nos fijamos en el offset aproximado (4037F2) y abrimos el crackme en un editor hexadecimal. La forma de convertir el offset que nos muestra VB Decompiler a lo que nos muestra un editor hexadecimal es la siguiente.
Una vez localizados los bytes, los cambiamos por ceros y guardamos.
Una vez parcheado, el serial correcto es 1.
CrkViz-3
En esta tercera entrega, CrkViz aumentó la dificultad. El crackme está compilado en código nativo y nos enfrentamos a un serial asociado a un nombre y a una rutina antidebug que en realidad es una Nag, ya que se muestra siempre.
Afrontar la nag es muy sencillo, basta con localizarla y parchear la llamada.
Reconstruyendo la llamada al registro vemos que lee de esta ruta: HKEY_CURRENT_USER\Software\VB and VBA Program Settings\CrkMeViz3\Register el contenido de User Name y del Serial number.
Quizá uno de los fallos de éste crackme, es que no comprueba la autenticidad de estos parámetros y si los modificas parece que estás registrado. Un ejemplo:
La rutina de comprobación del serial no es para nada complicada pero recordemos que estamos tratando con VB y éste delega el trabajo duro en otras librerias de modo que tenemos que «meternos» a tracear las llamadas para ver los valores que multiplica y divide.
Al estar correctamente registrados desaparece el botón de registrar.
CrkViz-4
El cuarto crackme es prácticamente igual que el tercero salvo que en vez de nag ahora contamos con limitación de ejecuciones. Del mismo modo utiliza el registro de Windows para guardar los datos de registro y las ejecuciones que llevamos.
Ponemos un breakpoint «bp RegOpenKeyW» y llegamos a la conclusión de que la ruta es HKEY_CURRENT_USER\Software\VB and VBA Program Settings\ODBC\Register y los valores se guardan en Counter, User Name y Serial number respectivamente. Este crackme hereda el fallo del anterior y si alteramos los valores el crackme nos muestra como usuarios autorizados, aunque sabemos que no estamos registrados ya que seguimos limitados por ejecuciones. Ni que decir tiene que lo mismo que modificamos el nombre y número de serie, podemos modificar el contador a nuestro favor. Crear un archivo «Reiniciar_contador.reg» con el siguiente contenido sería suficiente.
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\VB and VBA Program Settings\ODBC]
[HKEY_CURRENT_USER\Software\VB and VBA Program Settings\ODBC\Register]
"Counter"="0"
"User Name"="deurus"
"Serial number"="12345"
El keygen es prácticamente igual que en el crackme anterior, solo cambia el divisor.
Este último crackme está compilado en código nativo y simplemente se trata de una comparación lineal. La única diferencia reside en que no hay botón de registro, la comprobación la gestiona un evento «On Change«, de modo que está comprobando el tamaño del serial que introducimos y cuando éste tiene 8 dígitos llegamos aquí.
¿Ha sido indoloro no?, claro que sí, Visual Basic es un coñazo de tracear pero hay que reconocer que con el tiempo las herramientas han mejorado mucho y nuestra vida es mucho más sencilla. Bueno, pués esto ha sido todo, como siempre os dejo todo el material utilizado y un Keygen.
En el primer vistazo con el editor hexadecimal ya vemos la solución al reto:
Pho
Al igual que el caso anterior con el editor hexadecimal tenemos más que suficiente para resolver el reto.
Minions
En el análisis inicial no destaca prácticamente nada excepto la palabra myadmin que podemos ver con un editor hexadecimal.
La palabra myadmin es una buena candidata a ser contraseña ante una decodificación. Probamos con lo estándar y conseguimos resultados con steghide. La decodificación nos devuelve la cadena AEMAVABGAGwAZQBhAHIAbgB7AHQAaABpAHMAXwBpAHMAXwBmAHU***** que rápidamente catalogamos como base64 para resolver el reto.
Unopenable
Se nos entrega una imagen GIF aparentemente corrupta. Estudiando un poco la cabecera de los archivos GIF llegamos rápidamente a la conclusión de que faltan los cuatro primeros bytes del archivo.
Una vez insertados los bytes podemos ver una animación que contiene una cadena de texto fácilmente reconocible como base64. La decodificamos y ya tenemos la solución.
Oreo
Mirando con un editor hexadecimal no encontramos nada excepto la frase This is not the flag you are looking for para intentar disuadirnos.
Cargamos la imagen en Aperi’Solvey enseguida nos llama la atención la sección Binwalk y un suculento Rar.
Descargamos el archivo Rar y al descomprimir nos encontramos con un archivo de texto con la misma frase desalentadora del inicio y una imagen JPG, esta vez con dos oreos. Inspeccionando la imagen damos con la solución.
Warning: This challenge is still active and therefore should not be resolved using this information. Aviso: Este reto sigue en activo y por lo tanto no se debería resolver utilizando esta información.
Realistic Challenge 5: There is a new company out called NullCo. Have a look around the site and see what you can do.
Hay una nueva compañia llamada NullCo. Echa un vistazo a la web haber que puedes hacer.
Analizando a la víctima
Echamos un vistazo a la web y lo único interesante que vemos es un buscador.
Miramos el código fuente y vemos una ruta interesante.
Si exploramos la ruta «http://www.thisislegal.com/nc/adm/» nos aparece un login. Metemos cualquier cosa y el mensaje de error da demasiada información.
Ahora fijémonos en el menú productos. Pinchamos sobre cualquier producto y a continuación en la imagen para ampliarla, veremos el siguiente enlace.
Vamos a ver si podemos explotar «i.php«. Probamos a obtener información sensible del servidor.
Probamos «http://www.thisislegal.com/nc/i.php?img=adm/login.pwd» y nos da error, seguramente por que está anexionando la extensión de la imagen, es decir, el script está interpretando esto: