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.
Intro
This crackme is for the crack challenge 6 of canyouhack.it.
In this crackme the goal is to turn on all the lights. Note that a light off to the next, so if we interrupt this, we win.
Tools
Exeinfo (For crackme info)
Delphi Decompiler (For decompile)
OllyDbg (For debug)
Decompiling
With Delphi Decompiler we can found easy the buttons and his offsets.
Go to the offset 401A64 in OllyDbg and analyze the code.
We view two jumps, one turn ON the light and the other Turn OFF the next light. Patching the call from offset 401A8B we get the serial.
Se nos proporciona la imagen anterior y se nos invita a resolver la ecuación para el menor entero positivo que representa cada fruta. La solución del reto es concatenar «Flag-» a la suma de los valores de las tres frutas.
Análisis inicial
El enunciado es breve pero ya nos adelanta que existen varias soluciones y que además sólo quieren los números más pequeños que podamos encontrar.
Las frutas están muy bien y son muy sanas pero a nosotros se nos hace más sencillo plantear la ecuación con letras.
Para arrojar algo de luz veamos la representación gráfica en 2D y 3D con la ayuda de Desmos.
Las gráficas más que luz nos traen oscuridad de modo que preguntemos a ChatGPT.
ChatGPT
resuelve: (x/y+z)+(y/x+z)+(z/x+y)=4 para enteros positivos
Dado que las variables x, y, z son enteros positivos, podemos intentar encontrar una solución para la ecuación bajo estas condiciones. Sin embargo, encontrar una solución exacta puede ser complejo debido a la naturaleza no lineal de la ecuación. Aun así, podemos intentar algunos enfoques:
Prueba y error: Podemos probar diferentes combinaciones de valores enteros positivos para x, y, z y ver si satisfacen la ecuación.
Análisis de casos específicos: Podemos comenzar analizando casos más simples, como cuando dos de las variables son iguales, y luego ampliar nuestra búsqueda desde allí.
Análisis gráfico o numérico: Utilizando herramientas gráficas o numéricas, podríamos identificar posibles soluciones y luego verificar si son enteros positivos.
Voy a realizar un análisis numérico con un rango limitado de valores para ver si encontramos alguna solución que cumpla con las condiciones. Vamos a probar con valores de 1 a 10 para cada variable.
El análisis numérico para valores de en el rango de 1 a 10 no ha encontrado ninguna solución que satisfaga la ecuación. Esto sugiere que, dentro de este rango, no existen combinaciones de enteros positivos que cumplan con la ecuación.
Dado que la ecuación es bastante compleja y no lineal, es posible que las soluciones, si existen, se encuentren fuera del rango considerado o que requieran un enfoque diferente para ser identificadas. Si tienes un rango específico de valores en mente o si hay alguna otra restricción o contexto relevante, por favor házmelo saber para poder ajustar el análisis en consecuencia.
Básicamente nos está invitando a utilizar prueba y error para encontrar una solución, y el prueba y error de las matemáticas no es más que la fuerza bruta en programación.
El código
Partiendo del problema anterior dame un código en python que realice una comprobación recursiva de todas las variables con un rango de 1 a 999.
soluciones = []
rango_valores_x = range(1, 999)
rango_valores_y = range(1, 999)
rango_valores_z = range(1, 999)
for x in rango_valores_x:
for y in rango_valores_y:
for z in rango_valores_z:
# Calculamos el valor de la expresión para cada combinación de x, y, z
valor = x / (y + z) + y / (x + z) + z / (x + y)
# Comprobamos si el valor es igual a 4 y agregamos la solución a la lista
if abs(valor - 4) < 1e-6: # Usamos una pequeña tolerancia para evitar errores de precisión
soluciones.append((x, y, z))
print(soluciones)
Dejo resaltada la línea 12 del código ya que en ella reside el problema. Está comprobando con un margen de tolerancia de 10-6 ya que el resultado de las diferentes fracciones no da como resultado exacto 4. Esto es un problema ya que nosotros necesitamos que de exactamente 4 para validar los valores enteros de x, y y z. Pongo un ejemplo para el que se haya perdido con una solución válida para la tolerancia 10-6 siendo x=2, y=264 y z=993.
En otras palabras, ChatGPT nos ha brindado una solución aproximada que no sirve para nuestro propósito. Seguimos probando con el código anterior quitando la tolerancia y con rangos mayores hasta que en 106 paro. Me acaba de quedar claro que con la fuerza bruta no vamos a ninguna parte, o más bien, no tenemos capacidad de computación para resolverlo de ésta manera.
¿Qué está pasando?
Lo que pasa es que estamos ante una ecuación algebraica de 3 incógnitas que deben ser enteros positivos cuya solución se alcanza mediante la teoría de curvas elípticas.
Curvas elípticas
Las curvas elípticas son fundamentales en matemáticas avanzadas, representadas por la ecuación y2=x3+Ax+B, donde A y B son constantes. Estas curvas son un punto de encuentro entre la geometría, la teoría de números y el álgebra, ofreciendo un campo rico para la exploración y el análisis. En este CTF, nos enfocaremos en los puntos racionales de las curvas elípticas. Utilizando el método tangente-secante, un procedimiento geométrico iterativo, buscaremos ampliar un conjunto finito de soluciones conocidas a la ecuación de la curva. Este método nos permite indagar en la estructura de las soluciones racionales, que potencialmente pueden ser infinitas. Además, estableceremos una conexión entre las soluciones enteras de las ecuaciones diofánticas y los puntos racionales en las curvas elípticas partiendo de la ecuación (1) especificada en el análisis inicial. A pesar de su aparente simplicidad, esta ecuación es conocida por presentar soluciones mínimas de gran tamaño.
Adecuación
Antes de nada, necesitamos saber el grado de la ecuación, de modo que planteamos la ecuación en forma polinómica estándar deshaciéndonos de los denominadores.
Ahora necesitamos expandir y simplificar para llegar a la conclusión de que estamos ante una ecuación diofántica de grado 3. Este proceso es engorroso por la cantidad de términos a manejar así que vamos a utilizar Mathematica como software de respaldo para finalmente obtener el polinomio en la forma de Weierstrass según la ecuación 4.
\begin{align}
& y^2=x^3+109x^2+224x\\
\end{align}
donde:
\begin{align}
x = \frac{−28(a+b+2c)}{(6a+6b−c)}\\
y = \frac{364(a−b)}{(6a+6b−c)}
\end{align}
Las relación entre la ecuación 3 y los puntos de la curva elíptica se establecen mediante la ecuación 4. Las transformaciones entre las soluciones (a, b, c) y los puntos (x, y) en la curva elíptica vienen dados por las ecuaciones 5 y 6. Con estas transformaciones, cada solución de la ecuación diofántica se puede representar como un punto en la curva elíptica, y las operaciones de suma de puntos en la curva elíptica pueden usarse para encontrar nuevas soluciones de la ecuación diofántica.
Mathematica
El código que tenéis a continuación pertenece al gran trabajo de Aditi Kulkarni [7], que además nos da el resultado para cualquier valor de n. Ojo porque para n=4 el resultado tiene 81 dígitos, para n=6 tiene 134, para n=10 tiene 190 y para n=12 asciende a 2707 dígitos.
(* Asignar un valor numérico a n *)
n = 4;
(* Definir la ecuación de una curva elíptica en términos de n *)
curve4 = y^2 == x^3 + (4*n^2 + 12*n - 3)*x^2 + 32*(n + 3)*x;
(* Encontrar un punto racional en la curva que no sea (4,0) *)
P4 = {x, y} /. First[FindInstance[curve4 && x != 4 && y != 0, {x, y}, Integers]];
(* Función para calcular la pendiente entre dos puntos en la curva,
o la derivada en el punto si son iguales *)
Slope4[{x1_, y1_}, {x2_, y2_}] :=
If[x1 == x2 && y1 == y2,
ImplicitD[curve4, y, x] /. {x -> x1, y -> y1},
(y2 - y1)/(x2 - x1)];
(* Función para calcular la intersección en y de la línea entre dos puntos
o la tangente en el punto si son iguales *)
Intercept4[{x1_, y1_}, {x2_, y2_}] := y1 - Slope4[{x1, y1}, {x2, y2}]*x1;
(* Función para encontrar el siguiente punto racional en la curva *)
nextRational4[{x1_, y1_}, {x2_, y2_}] :=
{Slope4[{x1, y1}, {x2, y2}]^2 - CoefficientList[curve4[[2]], x][[3]] - x1 - x2,
-Slope4[{x1, y1}, {x2, y2}]^3 + Slope4[{x1, y1}, {x2, y2}]*(CoefficientList[curve4[[2]], x][[3]] + x1 + x2) - Intercept4[{x1, y1}, {x2, y2}]};
(* Función para convertir un punto en la curva elíptica a una solución diofántica *)
ellipticToDiophantine[n_, {x_, y_}] :=
{(8*(n + 3) - x + y)/(2*(4 - x)*(n + 3)),
(8*(n + 3) - x - y)/(2*(4 - x)*(n + 3)),
(-4*(n + 3) - (n + 2)*x)/((4 - x)*(n + 3))};
(* Usar nextRational4 para iterar desde P4 hasta encontrar una solución
válida y positiva para la ecuación diofántica *)
sol4 = ellipticToDiophantine[n,
NestWhile[nextRational4[#, P4] &, P4,
! AllTrue[ellipticToDiophantine[n, #], Function[item, item > 0]] &]];
(* Escalar la solución para obtener enteros mínimos *)
MinSol4 = sol4*(LCM @@ Denominator[sol4])
(* Suma de las tres variables*)
Total[MinSol4]
Solución
Concatenando Flag- con el resultado de Mathematica tenemos la ansiada flag.
ChatGPT ha demostrado ser eficaz en el análisis y la resolución de problemas, siempre que se le proporcione el contexto adecuado. Sin embargo, es importante ser conscientes de que la respuesta proporcionada puede ser aproximada, especialmente si la solución requiere una gran cantidad de recursos computacionales. Por ejemplo, al trabajar con una ecuación diofántica y valores específicos para (x) e (y), ChatGPT puede ayudar a calcular puntos como (P), (2P), (3P), etc., pero hay que tener en cuenta que los resultados para estos puntos pueden ser estimaciones.
Finalmente, os invito a leer la solución de Mingliang Z. [4], en la que se resuelve el problema por completo y de forma muy detallada.
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.
Hoy tenemos aquí un crackme de los que te hacen temblar las conexiones neuronales. Estamos acostumbrados al típico serial asociado a un nombre y a veces nos sorprenden.
El crackme data del año 2000, está realizado por aLoNg3x y lo tenéis colgado en crackmes.de. En crackmes.de también disponéis de una solución muy elegante realizada por cronos, pero que no acaba de saciar nuestro afán de descubrir todas las soluciones posibles.
El algoritmo
Abrimos el crackme con Olly y enseguida encontramos la rutina de comprobación junto con los mensajes de éxito y error. Os dejo la rutina comentada como siempre.
004012D7 |. 83C4 08 ADD ESP,8 ;
004012DA |. 09C0 OR EAX,EAX ;
004012DC |. /74 16 JE SHORT Zebrone.004012F4 ; Salta a Bad boy
004012DE |. |6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
004012E0 |. |68 26324000 PUSH Zebrone.00403226 ; |Title = "Great !!!"
004012E5 |. |68 30324000 PUSH Zebrone.00403230 ; |Text = "Congratulations, you have cracked the Zebra Crackme ver 1.1"
004012EA |. |FF75 08 PUSH [ARG.1] ; |hOwner = 0011067C ('Zebra - aLoNg3x - 1.1 Version',class='#32770')
004012ED |. |E8 C6010000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
004012F2 |. |EB 14 JMP SHORT Zebrone.00401308
004012F4 |> \6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
004012F6 |. 68 F8314000 PUSH Zebrone.004031F8 ; |Title = "Hmmmm :P"
004012FB |. 68 01324000 PUSH Zebrone.00403201 ; |Text = "Sorry... The Serial isn't correct :Þ"
00401300 |. FF75 08 PUSH [ARG.1] ; |hOwner = 0011067C ('Zebra - aLoNg3x - 1.1 Version',class='#32770')
00401303 |. E8 B0010000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
00401308 |> 31C0 XOR EAX,EAX
0040130A |. 40 INC EAX
0040130B |. EB 39 JMP SHORT Zebrone.00401346
0040130D |> 6A 00 PUSH 0 ; /Result = 0
0040130F |. FF75 08 PUSH [ARG.1] ; |hWnd = 0011067C ('Zebra - aLoNg3x - 1.1 Version',class='#32770')
00401312 |. E8 89010000 CALL <JMP.&USER32.EndDialog> ; \EndDialog
00401317 |. 31C0 XOR EAX,EAX
00401319 |. 40 INC EAX
0040131A |. EB 2A JMP SHORT Zebrone.00401346
0040131C |> 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
0040131E |. 68 40304000 PUSH Zebrone.00403040 ; |Title = "Zebra ver. 1.1"
00401323 |. 68 4F304000 PUSH Zebrone.0040304F ; |Text = "This is the 1.1 Zebra Crackme, Thanks to Quequero and Koma, to have said me a bug of the previous version. (It was due to an orrible cpu appoximation). As usually you cannot patch this .EXE, you've to find one of the many correct solut"...
00401328 |. FF75 08 PUSH [ARG.1] ; |hOwner = 0011067C ('Zebra - aLoNg3x - 1.1 Version',class='#32770')
0040132B |. E8 88010000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
00401330 |. 31C0 XOR EAX,EAX
00401332 |. 40 INC EAX
00401333 |. EB 11 JMP SHORT Zebrone.00401346
00401335 |> 6A 00 PUSH 0 ; /Result = 0
00401337 |. FF75 08 PUSH [ARG.1] ; |hWnd = 0011067C ('Zebra - aLoNg3x - 1.1 Version',class='#32770')
0040133A |. E8 61010000 CALL <JMP.&USER32.EndDialog> ; \EndDialog
0040133F |. 31C0 XOR EAX,EAX
00401341 |. 40 INC EAX
00401342 |. EB 02 JMP SHORT Zebrone.00401346
00401344 |> 31C0 XOR EAX,EAX
00401346 |> C9 LEAVE
00401347 \. C2 1000 RETN 10
================================================================
0040134A /$ 55 PUSH EBP
0040134B |. 89E5 MOV EBP,ESP
0040134D |. 83EC 68 SUB ESP,68
00401350 |. FF75 08 PUSH [ARG.1] ; /x1
00401353 |. E8 78010000 CALL <JMP.&CRTDLL.atof> ; \atof
00401358 |. DD55 E8 FST QWORD PTR SS:[EBP-18]
0040135B |. 83EC 08 SUB ESP,8
0040135E |. DD1C24 FSTP QWORD PTR SS:[ESP]
00401361 |. E8 82010000 CALL <JMP.&CRTDLL.floor>
00401366 |. DD5D F8 FSTP QWORD PTR SS:[EBP-8]
00401369 |. FF75 0C PUSH [ARG.2] ; /x2
0040136C |. E8 5F010000 CALL <JMP.&CRTDLL.atof> ; \atof
00401371 |. DD55 D8 FST QWORD PTR SS:[EBP-28]
00401374 |. 83EC 08 SUB ESP,8
00401377 |. DD1C24 FSTP QWORD PTR SS:[ESP]
0040137A |. E8 69010000 CALL <JMP.&CRTDLL.floor>
0040137F |. 83C4 18 ADD ESP,18
00401382 |. DD55 F0 FST QWORD PTR SS:[EBP-10]
00401385 |. DC4D F8 FMUL QWORD PTR SS:[EBP-8]
00401388 |. D9EE FLDZ
0040138A |. DED9 FCOMPP ; floor(x1)*floor(x2)=0 ???
0040138C |. DFE0 FSTSW AX ; <<Store status word
0040138E |. 9E SAHF ; <<Store AH into FLAGS
0040138F |. 75 07 JNZ SHORT Zebrone.00401398 ; Si salta todo OK
00401391 |. 31C0 XOR EAX,EAX
00401393 |. E9 96000000 JMP Zebrone.0040142E ; Bad boy
00401398 |> DD45 F8 FLD QWORD PTR SS:[EBP-8] ; <<Floating point load
0040139B |. DC5D F0 FCOMP QWORD PTR SS:[EBP-10] ; x1 = x2 ???
0040139E |. DFE0 FSTSW AX ; <<Store status word
004013A0 |. 9E SAHF ; <<Store AH into FLAGS
004013A1 |. 75 07 JNZ SHORT Zebrone.004013AA ; Si salta todo OK
004013A3 |. 31C0 XOR EAX,EAX
004013A5 |. E9 84000000 JMP Zebrone.0040142E ; Bad boy
004013AA |> DD45 F8 FLD QWORD PTR SS:[EBP-8] ; <<Floating point load
004013AD |. DD5D C8 FSTP QWORD PTR SS:[EBP-38]
004013B0 |. D9E8 FLD1 ; Carga 1 en el stack
004013B2 |. DD55 C0 FST QWORD PTR SS:[EBP-40] ; <<Floating point store
004013B5 |. DC5D C8 FCOMP QWORD PTR SS:[EBP-38] ; x1 > 1 ???
004013B8 |. DFE0 FSTSW AX ; <<Store status word
004013BA |. 9E SAHF ; <<Store AH into FLAGS
004013BB |. 77 2D JA SHORT Zebrone.004013EA ; Si salta bad boy
004013BD |. DF2D 38304000 FILD QWORD PTR DS:[403038] ; <<Load integer>> 2540BE400 = 10^10
004013C3 |. DD55 B8 FST QWORD PTR SS:[EBP-48] ; <<Floating point store
004013C6 |. DC5D C8 FCOMP QWORD PTR SS:[EBP-38] ; x1 < 10^10 ???
004013C9 |. DFE0 FSTSW AX ; <<Store status word
004013CB |. 9E SAHF ; <<Store AH into FLAGS
004013CC |. 72 1C JB SHORT Zebrone.004013EA ; Si salta bad boy
004013CE |. DD45 F0 FLD QWORD PTR SS:[EBP-10] ; <<Floating point load
004013D1 |. DD5D B0 FSTP QWORD PTR SS:[EBP-50] ; <<Store and pop
004013D4 |. DD45 C0 FLD QWORD PTR SS:[EBP-40] ; <<Floating point load
004013D7 |. DC5D B0 FCOMP QWORD PTR SS:[EBP-50] ; x2 > 1 ???
004013DA |. DFE0 FSTSW AX ; <<Store status word
004013DC |. 9E SAHF ; <<Store AH into FLAGS
004013DD |. 77 0B JA SHORT Zebrone.004013EA ; Si salta bad boy
004013DF |. DD45 B8 FLD QWORD PTR SS:[EBP-48] ; <<Floating point load>> carga 10^10
004013E2 |. DC5D B0 FCOMP QWORD PTR SS:[EBP-50] ; x2 < 10^10 ???
004013E5 |. DFE0 FSTSW AX ; <<Store status word
004013E7 |. 9E SAHF ; <<Store AH into FLAGS
004013E8 |. 73 04 JNB SHORT Zebrone.004013EE ; Salta si menor
004013EA |> 31C0 XOR EAX,EAX
004013EC |. EB 40 JMP SHORT Zebrone.0040142E ; Bad boy
004013EE |> DD45 F8 FLD QWORD PTR SS:[EBP-8] ; <<Floating point load>> carga x1
004013F1 |. D9FE FSIN ; Sin(x1)
004013F3 |. DD5D A8 FSTP QWORD PTR SS:[EBP-58] ; <<Store and pop
004013F6 |. DD45 F0 FLD QWORD PTR SS:[EBP-10] ; <<Floating point load>> carga x2
004013F9 |. D9FE FSIN ; Sin(x2)
004013FB |. DD5D A0 FSTP QWORD PTR SS:[EBP-60] ; <<Store and pop
004013FE |. DD45 A8 FLD QWORD PTR SS:[EBP-58] ; <<Floating point load
00401401 |. DC4D A0 FMUL QWORD PTR SS:[EBP-60] ; Sin(x1) * Sin(x2)
00401404 |. DF2D 30304000 FILD QWORD PTR DS:[403030] ; <<Load integer>> 2386F26FC10000 = 10^16
0040140A |. DEC9 FMULP ST(1),ST ; 10^16 * (Sin(x1) * Sin(x2))
0040140C |. 83EC 08 SUB ESP,8
0040140F |. DD1C24 FSTP QWORD PTR SS:[ESP] ; <<Store and pop
00401412 |. E8 D1000000 CALL <JMP.&CRTDLL.floor>
00401417 |. 83C4 08 ADD ESP,8
0040141A |. DD5D 98 FSTP QWORD PTR SS:[EBP-68]
0040141D |. D9EE FLDZ ; <<Load 0.0 onto stack
0040141F |. DC5D 98 FCOMP QWORD PTR SS:[EBP-68] ; 10^16 * (Sin(x1) * Sin(x2)) = 0 ???
00401422 |. DFE0 FSTSW AX
00401424 |. 9E SAHF ; <<Store AH into FLAGS
00401425 |. 75 05 JNZ SHORT Zebrone.0040142C ; Si NO salta todo OK
00401427 |. 31C0 XOR EAX,EAX
00401429 |. 40 INC EAX
0040142A |. EB 02 JMP SHORT Zebrone.0040142E
0040142C |> 31C0 XOR EAX,EAX
0040142E |> C9 LEAVE
0040142F \. C3 RETN
La primera dificultad que podemos encontrar es que utiliza instrucciones FPU y coma flotante, ya que si no tenemos la vista entrenada nos puede resultar un engorro. Superado esto, la rutina de comprobación se puede resumir así:
x1 * x2 != 0
x1 != x2
x1 > 1 y < 10^10
x2 > 1 y < 10^10
Floor[10^16 * sin(x1) * sin(x2)] = 0
A priori no parece que tenga mucha dificultad, pero vamos a analizarlo más concienzudamente. Necesitamos que la parte entera del resultado de la multiplicación sea 0, algo que parece sencillo, pero fíjate que la constante 10^16 nos obliga a su vez, a que el resultado delseno sea muy pequeño, cosa que como comprobaréis limita mucho los resultados satisfactorios.
Repasando trigonometría
Cuando nos enseñó nuestro profesor la función del seno nos hizo el siguiente dibujo:
Partiendo de la circunferencia unitaria, podemos concluir que el seno de alpha es igual a la altura x. Como lo que nos interesa a nosotros es que el seno sea muy pequeño, en realidad estamos buscando que la x sea lo más pequeña posible. Llegamos entonces a la conclusión de que las soluciones para enteros entre 1 y 10^10 van a ser muy reducidas. Además nos percatamos que el ángulo alpha va a tener que estar muy proximo a 0º – 360 (0 – 2π) y a 180º (π). En el siguiente gráfico queda claro el estrecho margen en el que nos movemos.
Si habéis leído la solución de cronos ahora le encontraréis algo más de sentido a por que él utilizó fracciones continuas de π y cogió como resultado los numeradores más cercanos a 10^10, en su caso 245850922 y 411557987.
Como veis, el exponente negativo (^-17) debe ser mayor que el positivo (^16) para tener éxito.
Fuerza bruta
Lo que vamos a hacer a continuación es buscar todos los senos con exponente negativo ^-8 ó ^-9 de enteros entre 1 y 10^10, y vamos a cruzar los resultados para determinar todos los resultados válidos.
Preparamos el programa y le dejamos trabajar. En principio vamos a filtrar todos los resultados que tengan exponente negativo y luego ya aislaremos los que nos interesan. Esto lo hago por curiosidad.
La fuerza bruta nos arroja 63663resultados con exponente negativo entre ^-5 y ^-9, de los cuales solamente nos quedamos con 65, que son los comprendidos a exponentes de entre ^-8 y ^-9. Los números mágicos son los siguientes:
Los rojos son exponentes ^-9, el resto ^-8.
La mayoría de estos números solo valen con ciertas combinaciones, de hecho, ninguno vale para todos. Esto se debe, a parte del propio exponente, a que hay senos positivos y negativos y para hacer válido a un seno negativo hay que combinarlo con otro negativo. Esto último se debe únicamente a la interpretación que hace el crackme.
Finalmente cruzamos los resultados y obtenemos 44 combinaciones de seriales válidos que si obviamos repeticiones se reducen a la mitad.
Combinaciones válidas:
Conclusiones
Podemos concluir que para cada 10^10 enteros hay 22 soluciones posibles. Finalmente comentar que si aLoNg3x no hubiera puesto el límite en 10^10, habría soluciones infinitas.
Introducción Objetivo del juego y normas Código inicial Primeras modificaciones Terminando la faena Código ganador Curiosidades Enlaces Introducción Hace tiempo
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.
Resumidamente, esta técnica consiste en ocultar información en el bit menos significativo de cada uno de los píxeles de una imagen, consiguiendo así que el cambio realizado sea invisible al ojo humano. El problema de esta técnica, es que la información oculta puede obtenerse fácilmente si esta no se ha codificado previamente o si no se sigue un patrón concreto a la hora de ocultarla.
Desde la web del reto nos avisan de que esto es un simple truco pero espectacular. Nos animan a descargar una imágen y a encontrar la solución oculta.
Aprovecho este reto para presentaros una herramienta vital al enfrentaros a ciertos retos sobre esteganografía, steganabara.
Steganabara tiene dos apartados muy interesantes, uno es «color table» y otro «bit mask«, hoy veremos en acción a «bit mask».
No os preocupéis por la solución ya que cambia para cada usuario y sesión.
Buscando la solución oculta
Abrimos steganabara y empezamos a trastear con bit mask.
Al poco tiempo ya vemos que vamos bien encaminados.
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.
If we go to the link, we view one string like a hash: 68a571bcf7bc9f76d43bf931f413ab2c. Umm, it’s like MD5. Go to decrypt online and we get the pass: «canyouhack.it». But if we test this password in the crackme, surprise!, nothing happens. We need to continue analyzing the code. Later we view the next interesting link:
Los retos de encriptación son muy variados como hemos comentado anteriormente. Aquí tenemos unos buenos ejemplos de ello.
Cripto 1
En este primer nivel nos encontramos con un método de encriptación muy antíguo. Sólo diré como pista, que es de los más antiguos que se conocen.
ozhlofxrlmvhxzorulimrz
Lo primero que suelo hacer en este tipo de retos cuando son solamente letras, es comprobar las dos opciones más típicas, que son el cifrado César y Vigenere. En este caso necesitamos ahondar un poco más, aunque enseguida llegamos a la conclusión de que el cifrado usado es el afín. Un ataque por fuerza bruta nos devuelve la solución y los coeficientes utilizados.
En este segundo nivel recordaremos a un general romano muy conocido. Lo complicaremos un poco, sólo lo justo para que cueste algo más de cinco minutos encontrar la clave 🙂
oehoeahhjoexhkzqhfsvzhffhwrhotqk
Lo primero que nos viene a la cabeza es el cifrado César pero no va. Probando varios cifrados por sustitución al final damos con el correcto. De nuevo un ataque por fuerza bruta nos da frutos.
Este nivel también va a ser sencillo. Estos caracteres, pertenecientes a un sistema bastante conocido de encriptado, esconden una palabra que, al introducirla (en minúsculas), nos permitirá superar el nivel.
Investigando un poco llegamos a la conclusión de que se trata del cifrado Francmasón o Pig Pen.
Esta prueba es tan simple que la he dividido en dos partes que, aunque de apariencia similar, se resuelven de distinta manera. La clave es la unión de las dos palabras resultantes de descifrar las dos líneas de números y que, juntas, forman una tercera palabra.
Aquí hay que hacer un poco de trabajo de investigación: Hay que descubrir la clave que empleó un escritor francés (Una pista: «Lagardère») en una de sus novelas, que es la empleada aquí para formar la palabra clave (en minúsculas) que, por cierto, es alemana.
RI3I2MIL2I2A3
POR RESOLVER
Cripto 7
Seguimos con cosas fáciles. Se trata de descifrar este texto escrito en inglés.
kgw qkoev ol 617 qthpreoz iwjpz sdkg kgw pdeyeplk rwqdjzwe ipezwq spbbdq sgo sgwz goqkdbdkdwq iwjpz spq rwkwecdzwr ko cpmw gdq uweqozpb yozkedihkdoz ko kgw spe wlloek
Una vez descifrado, nos será fácil descubrir la clave:
pzpyozrp
Se trata de un cifrado de sustitución mono alfabético.
THE STORY OF 617 SQUADRON BEGAN WITH THE AIRCRAFT DESIGNER BARNES WALLIS WHO WHEN HOSTILITIES BEGAN WAS DETERMINED TO MAJE HIS PERSONAL CONTRIBUTION TO THE WAR EFFORT
Una vez descifrado el alfabeto la solución queda:
pzpyozrp = anaconda
Cripto 8
A veces, las cosas no son lo que parecen. Donde aparecen unos números, en realidad hay otros números distintos.
273664524572348321143738 853442616537643005319627
POR RESOLVER
Cripto 9
Para resolver algunos problemas, hay que tener una buena base. Este es un buen ejemplo de ello:
Esto es más complicado. Para descifrar este texto que contiene la clave para superar el nivel, se necesita otra clave. Para que no sea demasiado difícil, he utilizado una palabra muy sencilla de sólo cuatro letras 🙂
myiemyuvbaeewcxweghkflxw
Mediante fuerza bruta matamos dos pájaros de un tiro.
Aquí tenemos un CrackMe diferente a lo que estamos acostumbrados, ya que en vez del típico número de serie asociado a un nombre la comprobación se realiza mediante checkboxes con una matriz de 7×3. El CrackMe está realizado en Visual C++ lo que facilita en parte encontrar rápidamente la rutina de comprobación.
Comprobación
004013C5 > /8B7424 10 MOV ESI,[DWORD SS:ESP+10] ;
004013C9 . |33FF XOR EDI,EDI
004013CB > |8B86 74304000 MOV EAX,[DWORD DS:ESI+403074] ;
004013D1 . |8BCB MOV ECX,EBX
004013D3 . |50 PUSH EAX
004013D4 . |E8 6F020000 CALL <JMP.&MFC42.#3092_CWnd::GetDlgItem> ; Lee el estado del checkbox
004013D9 . |8B48 20 MOV ECX,[DWORD DS:EAX+20]
004013DC . |6A 00 PUSH 0
004013DE . |6A 00 PUSH 0
004013E0 . |68 F0000000 PUSH 0F0
004013E5 . |51 PUSH ECX ;
004013E6 . |FFD5 CALL NEAR EBP
004013E8 . |3B86 20304000 CMP EAX,[DWORD DS:ESI+403020] ; Comprueba el estado del checkbox (1 activado 0 desactivado)
004013EE . |75 20 JNZ SHORT Matrix_C.00401410 ; Salto a chico malo
004013F0 . |47 INC EDI ; Incrementa contador
004013F1 . |83C6 04 ADD ESI,4
004013F4 . |83FF 07 CMP EDI,7 ; ¿Hemos terminado de leer las columnas? ¿contador = 7?
004013F7 .^|7C D2 JL SHORT Matrix_C.004013CB ; si terminan las columnas deja pasar
004013F9 . |8B4424 10 MOV EAX,[DWORD SS:ESP+10]
004013FD . |83C0 1C ADD EAX,1C ; contador de filas
00401400 . |83F8 54 CMP EAX,54 ; 3 filas = 1C+1C+1C=54
00401403 . |894424 10 MOV [DWORD SS:ESP+10],EAX
00401407 .^\7C BC JL SHORT Matrix_C.004013C5 ; ¿Hemos terminado de leer la fila? ¿contador = 54?
00401409 . 68 D8304000 PUSH Matrix_C.004030D8 ; ASCII "Registration successful!"
0040140E . EB 05 JMP SHORT Matrix_C.00401415
00401410 > 68 C8304000 PUSH Matrix_C.004030C8 ; ASCII "Not registered!"
En la rutina de comprobación se ve fácil un CMP EDI,7 por lo que podemos deducir que si el creador no se ha molestado mucho la comprobación se realiza de izquierda a derecha y de arriba hacia abajo.
Orden de comprobación
Tal es así que si ponemos un breakpoint en 4013E8, podemos ir sacando el estado correcto de los checkboxes sin mucha molestia.