SolveIt / Level2 / EE30

El level 2 del SolveIt se nos hacía conocido. Surgió en alguna otra ocasión, no recuerdo exactamente… tal vez en la Gipuzkoa Encounter de 2019. El autor (@imobilis) dibujó las pistas para solucionarlo en un papel que luego troceó y revolvió en múltiples pedazos. Verdaderamente es un buen rompecabezas.

Solve It 2: Rompecabezas. Inicialmente la imagen era de baja calidad. A lo largo del torneo el autor proporcionó nuevas imágenes donde podía observarse mejor cada una de las piezas.

Conocíamos el procedimiento de resolución: recortar cada una de las piezas e intentar recomponer la imagen original. Un trabajo de chinos. Kotxerra se puso a ello y estuvo dándole al Photoshop durante horas. Al final consiguió una buena imagen:

Completar el puzle nos llevó más horas de las que hubiéramos querido meter en este level, pero el esfuerzo mereció la pena.

@ochoto retocó la imagen para hacerla más legible:

Aunque se ve mejor, te reto a que busques ahora la solución…. Hint1: la primera palabra de la solución se lee bastante bien: «Repeated».

Hint2: Los solveits repetidos no son un pecado… 😉

SolveIt / Level1 / EE30

Los bolsillos de un ORG, así se titulaba el level 1 del SolveIt. Venía acompañado de la típica imagen con varios elementos cuyos nombres hay que encontrar para formar la contraseña. Pero otra vez, nos encontramos con un red herring, una falsa pista que nos hizo perder tiempo (jugando con Google Lens para encontrar algunos nombres…) Al final bastaba con seguir la siguiente pista al pie de la letra: buscamos los nombres de los técnicos (orgs) que podrían llevar cualquiera de estos elementos…

Los elementos de la imagen al final no eran necesarios para encontrar la solución

Así que buscando en la intranet, pudimos ver esta lista:

Agradecer a los técnicos de la Euskal su labor tampoco está de más 🙂

Y nuestro compañero Cuartango, después de haber probado otras cuantas posibles soluciones, llegó a la conclusión de que la más simple era la buena: Aitor Aritz Gorka Mikel Oier 🙂

HackIt’2022/EE30/Level3 – Asahi Linux

El nivel 3 nos consiguió engañar. Y eso que lo esperábamos… una prueba sobre Asahi Linux va a caer sí o sí; marcan ha estado metiendo miles de horas en este proyecto…

El nivel 3 es mucho más fácil de lo que parece…

Descomprimiendo el fichero zip que nos adjuntan en la prueba analizamos lo que tenemos:

Lo primero, el nombre del gz indica AGX, la arquitectura GPU del M1 (Apple Silicon). Tras leer los posts de Rosenzweig y curiosear el repositorio GitHub de Asahi, llegamos hasta este script que trata de mostrar en pantalla el conejo típico de testing:

Además, usa variables del cmdbuf.json que nos dan (depth_bias_array, scissor_array…) Así que nuestra idea era ejecutar el código de renderizado del conejo en un m1 y si funcionaba, cambiar los .bin por los que nos daban (y cargar adecuadamente las variables del cmdbuf.json). Había algunos flecos más que arreglar, pero bueno… lo primero era encontra un m1 🙂 Afortunadamente para nuestros bolsillos podemos alquilar el uso de un mac mini m1 por 2.5€ al día.

No será por no haberlo intentado con un M1 real

Seguimos el script de instalación de Asahi hasta el final:

Pero llegamos a un punto interesante… En el último paso Asahi nos pedia apagar el M1 y en el arranque seleccionar Asahi… No contamos con un «pequeño» detalle: en un ordenador remoto al arrancar no tenemos acceso a la pantalla de boot. Así que se nos quedó el m1 en modo rebooting durante toda la party. Dimos la orden de reinstalar el sistema pero no se podía porque estaba en modo rebooting ¯\_(ツ)_/¯

Así que estuvimos dando vueltas y vueltas… sin darnos cuenta de un detalle. Si hay que renderizar algo en pantalla a partir de vértices de un gráfico, necesitaremos esa malla de vértices en 3d… que estará en alguno de los .bin que nos pasan.

En este obj_150138c000.bin, por ejemplo. Si pintamos esos puntos con matplotlib (saludos a @ochoto, que se curró el script):

#!/usr/bin/env python
# coding: utf-8

import numpy as np
import matplotlib.pyplot as plt

with open("obj_150138c000.bin", 'rb') as f:
    data = np.fromfile(f,dtype="f")

numvertex = len(data) // 3
rshp = data.reshape(numvertex,3)

xs = rshp[:,0]
ys = rshp[:,1]
zs = rshp[:,2]

plt.plot(xs,ys)
plt.show()

Y obtenemos el resultado buscado 🙂

Una vez visto, parece fácil, ¿a que sí?

HackIt’2022/Level2/EE30 Visual Legacy

En la prueba 2 estuvimos zanganeando. Buscábamos un parser que ya estuviera programado para que nos ayudara a interpretar el contenido del wav. Craso error. marcan siempre evita este tipo de pruebas y si crees que igual tienes que programar el script que interprete este formato… ya puedes ir abriendo el IDE.

El título de la prueba, Visual Legacy viene a indicarnos que el formato que buscamos es algo antiguo y que el wav está codificando imagen de vídeo.

Visual Legacy viene a indicarnos que el formato que buscamos es algo antiguo y codifica imagen de vídeo

Si abrimos con Audacity el wav, veremos algo como lo siguiente:

Cada bloque representa una línea de un frame de un vídeo. Si hacemos zoom sobre un bloque y contamos los sincronismos horizontales (ver siguiente imagen), veremos cuántas líneas tiene cada frame (241, uno arriba o uno abajo 🙂

Haciendo algo de zoom:

Los picos negativos representan una señal de sincronismo horizontal (puede verse mentalmente como un salto de línea). La señal recoge información acerca de la luminancia de cada pixel de la imagen que está capturando. La parte positiva puede llegar hasta 0,7V para el nivel de blanco, correspondiendo a 0V el negro  Más info: https://www.adictosaltrabajo.com/2015/05/21/senal-de-video/

Buscando «video signal» en Google Images, uno de los primeros resultados es este:

¿Podría ser una seña de PAL o NTSC video? (¿es suficientemente legacy? 🙂

¿Cómo saber cuándo termina un frame y empieza el siguiente? Hay una señal de sincronismo vertical:

Tres pulsos para marcar sincronismo vertical (empieza un nuevo frame)

Con toda esa información, nuestro compañero Joserra se curró un script en Python que iba extrayendo la luminancia de cada pixel. Formando líneas y frames, y exportando cada frame a un png.

El resultado son 12000 y pico frames del famoso vídeo de Rick Astley (cómo no, el Never gonna give you up). Podemos montar los frames para formar un vídeo por el que nos podamos desplazar cómodamente:

ffmpeg -r 60 -f image2 -s 241x385 -i decode%05d.png -vcodec libx264 -crf 25   test.mp4

Y pasándolo con FF, por fin, vemos la ansiada clave:

No llegamos a tiempo en el concurso, pero pudimos terminar la prueba y documentarla para el blog 🙂

HackIt’2022, Level1, EE30

Cada vez cuesta más escribir un post. Es más sencillo crear tuits. Hay menos fricción. Un tuit es algo rápido, algo donde no necesitas pensar y reescribir mucho. Sin embargo, creo que el arte de escribir posts en un blog nos hace mejores. Espero que, al menos una vez al año, vuelva esta sensaci´ón de estar haciendo lo que tengo que hacer.

Blast from the past. El primer nivel del HackIt 2022.

Julio de 2022. Después de dos años parados, volvemos al HackIt!. Retomamos buenas sensaciones. Salimos con nuevas ideas, conocimientos, propósitos y sentimientos. El esfuerzo dedicado al HackIt siempre, siempre, merece la pena. Por supuesto, hay cosas que mejorar. Lo primero, el nivel de las pruebas. Seguramente los autores dirán que no. Que somos unos failers (cierto), que no era para tanto (cierto). Pero de lo que no nos podemos esconder es de los datos. Que nuestro grupo haya quedado tercero en la clasificación del HackIt con solo una prueba superada es algo que llama la atención. Sólo dos equipos, wopr y NavarParty han superado más de dos pruebas (tres en total, y wopr sólo tenía dos pruebas superadas a pocos minutos del final). ¿Es responsabilidad única de los organizadores? No, los organizadores bastante hacen manteniendo el concurso durante años y generando pruebas sin ayuda durante muchos HackIts. Pero hay que darle una vuelta y echar un cable para la siguiente edición. Me la apunto como tarea. Espero que dentro de un año pueda enlazar este post y comentar las mejoras sugeridas y realizadas.

Al lío, level 1 (he hecho una copia de los levels en ee30.ikasten.io, pues mi petición anual de que marcan o imobilis los suban ha sido llevada a /dev/null sin piedad)

A simple vista nos encontramos con el típico nivel 1. Código en JavaScript que… un momento, no es JS, sino VBScript… vaya. Código en VBScript que cifra una clave y que basta con entender un poco el algoritmo para descifrarla (no). En el peor de los casos, para un nivel 1, con fuerza bruta seguro que se saca (no). Un nivel 1 que sólo se pasaron unos 13 equipos (seguramente alguno más, porque hice la copia de la web un par de horas antes del cierre) y que durante las primeras horas sólo 4 grupos lo consiguieron ¿Por qué? Porque hay que entender VBScript (fácil) y porque hay que invertir el algoritmo que calcula la clave. ¿Inversión de algoritmos con aritmética modular para un nivel 1? Pues sí. ¿Es fácil de resolver? Sí ¿Es adecuado para un level 1? No lo creo.

Lo primero que hicimos fue ejecutar el script original en algo que sepa interpretar VBScript. Una buena alternativa es hacerlo con el editor de macros de LibreOffice.

LibreOffice dispone de un buen IDE para editar macros en VBScript. En la imagen podemos ver que hemos puesto un punto de ruptura en la línea 37 para evaluar el valor del array «a» (en la caja de watches).

Para no volvernos locos con un lenguaje arcaico o trabajar con un IDE más user-friendly, también podemos convertir el script de VB a JS. Hay que tener cuidado con varias cosas: 1) al acabar la conversión ejecutar el script original con una valor cualquiera de password y la conversión con el mismo valor. Así veremos que no la hemos liado. 2) Los índices de los bucles for del script en VB parecen dar a entender que VB indexa los arrays desde la posición 1. FALSO. Indexa desde la posición 0, al igual que en JS. 3) Ojito con el index j si decidimos indexar desde 0 los arrays (véase el código JS adjunto). Nos podría quedar una solución como la siguiente:

let init = "pruebaprueba14";
let val = [...init]


    let a=[];
    let tmp;
    
    for (let i = 0; i <= 13; i++){ 
        
        a[i] = val[i].charCodeAt(0);
    }
    
    for (let j = 1; j <= 42 ; j++){
      for (let i = 0; i <= 13; i++) {
        a[i] = ( a[i] + j * a[ (i + 1) % 14 ] ) % 256
      }
       
        
        tmp = a[1]
        a[1] = a[2]
        a[2] = tmp
    
        a[3] = (a[3] * 49) % 256
        a[4] = (a[4] * ((j * 2) + 1)) % 256
        a[5] = 255 - a[5]
        
        tmp = a[0]
        for (let i = 0; i<= 12; i++){
            a[i] = a[i + 1]
        }
        a[13] = tmp

         console.log(j, a)
    }

    s = a.toString() + ","

    console.log(s)
    if (s != "101,107,164,102,76,232,0,57,122,139,112,36,17,205,"){
        console.log( "Wrong!" )
    }else{
        console.log("Nice!");
    }

Tanto si lo queremos hacer con el código VB o con JS, ahora viene lo bueno. Para solucionar este level hay que revertir el algoritmo. Es decir, partiendo de la solución que se busca («101,107,164,102,76,…») ir ejecutando hacia atrás. Bueno, al principio parece fácil. Por ejemplo, si el último bucle rota los elementos del array hacia la izquierda:

Lo que tendremos que hacer al invertir es rotar hacia la derecha:

Sigamos con la inversión. Tenemos el siguiente trozo:

Las dos últimas líneas (recuerda, estamos invirtiendo, hay que empezar desde el final hacia el principio) son fáciles:

Pero con la línea del módulo 256 empieza a complicarse el tema. Nosotros partimos de un a(5) conocido y tenemos que saber como invertir la operación para descubir cuál era el valor anterior de a(5). Es decir, buscamos un número x tal que x * ((j * 2) + 1)) Mod 256 = a(5). El valor de la variable j es conocido (el valor del índice j es conocido en cada vuelta del for). El problema es que la ecuación x mod 256 = y tiene múltiples soluciones para x. Por ejemplo, si y=3, entonces una posible solución es x= 3, pero otra es x=256+3. En general x=256*n+3 es una solución (con n = cualquier entero positivo)

Pero sabemos que en cada paso, el valor de a(5) no pasará de 256, así que bastaría con buscar el valor de x iterando por todos los posibles valores:

Aplicamos la misma lógica al resto del programa y encontramos la ansiada clave original.

Agradecer como siempre a @imobilis y a @marcan42 todo el curro realizado y por seguir al pie del cañón a pesar de nuestros rants 🙂

Saludos a NavarParty, w0pr, Sauronealo e insomnia, por haber conseguido subirse al podium en el HackIt/SolveIt. Y un gran hats-off a NavarParty, por haber conseguido destronar a w0pr después de ♾️ años.

Y cañas para mis compañeros Kotxerra, Ochoto y Cuartango, que un año más me han vuelto a enseñar cómo jugar un buen HackIt y saborearlo con unas buenas cervezas 🙂

Continuará…