PAUL GUERRERO software engineer
← retos

Euskal Encounter 34 · Solve It 2

Solve It 2 — Lost Fest

Resuelto
  • #ctf
  • #ia
Curioso festi, ¿qué esconde?
Enunciado del reto

Cómo se resolvió

  1. Cada día esconde un país

    El cartel no lo imprime en ningún sitio: se deduce de las bandas, que son reales y por jornada comparten origen. Y los horarios van barajados dentro de la columna, así que hay un orden que reconstruir.

  2. Las ciudades natales dibujan letras

    Pon en el mapa la ciudad de cada banda y únelas por orden de actuación. Francia: Burdeos, Nantes, Rennes, Marsella, Estrasburgo. Sube, baja en diagonal, sube. Una N. Siete días, siete trazos.

  3. El orden no es cronológico

    Leídos por jornada, los siete caracteres no forman nada. El país es dato deducido, y por eso puede ordenar: alfabético en castellano coloca cada glifo en su sitio y resuelve las formas ambiguas de una vez.

  4. Los dígitos se escriben como dígitos

    El trazo de Brasil es un cuatro y el de España un uno. No son letras que se parecen a números: son lo que sale del mapa, y así es como los escribe el autor.

Contraseña C4M1NOS

El «festi» era un JPG de 4260×2780: el cartel de un festival ficticio de siete noches, del 14 al 20 de julio, con una columna de bandas y horarios por día y, al pie, un campo UBICACIÓN en rojo tapado con un trozo de cinta. El fichero no esconde nada — sin EXIF, sin comentario JPEG, sin datos tras el final de la imagen. Lo que esconde el festival está impreso a la vista.

Fue el reto que más nos costó de la tanda, y casi todo el coste vino de un supuesto que nadie nos había dado: que los siete caracteres del puzzle se leen de lunes a domingo. La mecánica de fondo —de las más bonitas del concurso— llegó relativamente pronto; el orden de lectura nos tuvo horas persiguiendo topónimos cada vez más convincentes y todos falsos.

Y hay una historia dentro de la historia: el autor sustituyó el cartel a mitad de concurso con una nota de «cambios de última hora». Bien leído, aquel cambio era una fe de erratas que señalaba justo dónde cojeaba su puzzle. Y el diff escondía una tercera sorpresa que no vimos hasta repasarlo en frío, ya acabado el concurso.

Lo que hay en el cartel

Las 40 bandas son reales, y agrupadas por día son todas del mismo país:

DíaPaísGrupos (en orden cronológico)
LUN 14MéxicoControl Machete · Grupo Yndio · Nortec Collective · Maná · Molotov · Sak Tzevul · Acapulco Tropical
MAR 15ChinaHiperson · Low Wormwood · SMZB · Orange Ocean · Duck Fight Goose
MIÉ 16AustraliaPowderfinger · The Medics · The Pigram Brothers · Pendulum · Cold Chisel · The Avalanches · AC/DC
JUE 17JapónCapsule · Coldrain · Denki Groove · Pierrot · Greenmachine
VIE 18BrasilBanda Calypso · Vanguart · BaianaSystem · Aviões do Forró · Dead Fish
SÁB 19FranciaNoir Désir · Tri Yann · Marquis de Sade · IAM · Weepers Circus
DOM 20EspañaCeltas Cortos · Platero y Tú · Mecano · Medina Azahara · Triana · M Clan

Dos observaciones orientan. Los horarios están desordenados dentro de cada columna —el lunes empieza por «20:20 MOLOTOV» y el «16:00 CONTROL MACHETE» aparece el tercero—, así que el orden cronológico es una lectura que el reto te está pidiendo hacer. Y el país no está escrito en ninguna parte: lo deduces tú de las bandas. Ese segundo detalle parece menor y es la clave de todo, pero eso viene después.

La mecánica: geoglifos

El clic viene de juntar tres piezas que ya están sobre la mesa: un país por día, un orden interno que el reto se molesta en ocultar, y una ubicación como respuesta.

Coge las bandas de un día. Ordénalas por hora. Pon la ciudad natal de cada una en el mapa de su país y une los puntos en ese orden. El recorrido dibuja un carácter.

Siete días = siete caracteres.

El caso más limpio es Francia:

HoraBandaCiudad
18:00Noir DésirBurdeos
19:10Tri YannNantes
20:20Marquis de SadeRennes
21:35IAMMarsella
22:55Weepers CircusEstrasburgo

Burdeos → Nantes → Rennes es una subida por la costa atlántica; Rennes → Marsella, una diagonal larga hacia el sudeste; Marsella → Estrasburgo vuelve a subir. Sube, baja en diagonal, sube: una N de libro.

España sale igual de claro: Valladolid → Bilbao (un trazo corto hacia arriba), Bilbao → Madrid → Córdoba → Sevilla (una vertical larga bajando), Sevilla → Murcia (una horizontal a la derecha). Es un 1 con su banderín y su base. O una L. O una I. Ese es el problema del reto, y no es accidental.

Como el cartel es obra del autor y no se redistribuye, los siete trazos están redibujados en una figura nuestra, construida solo con datos públicos — ciudades natales, horas y siluetas de país. Todo lo que hay que ver del mecanismo se ve aquí sin tocar el original:

Figura propia con siete paneles, uno por país: sobre la silueta de cada país, las ciudades natales de las bandas unidas por flechas con los vértices numerados en orden cronológico, y debajo de cada panel el carácter que dibuja el trazo. Una tabla inferior resume día, país deducido, glifo y orden alfabético

Reconstrucción nuestra, no el cartel: silueta de país, vértices numerados en orden de actuación y, debajo, la lectura de cada trazo.

Redibujarlo pide poco: las coordenadas de las ciudades y matplotlib, con un detalle que importa —corregir el aspecto de la proyección (lon·cos(lat) frente a lat)—. Sin eso, un país alargado en latitud deforma el glifo y lees una letra por otra; con Australia y México, la diferencia entre una C y una O depende literalmente de esa corrección. Las ciudades natales salen de fuentes públicas, y no todas son inequívocas —hay bandas que se formaron en una ciudad y se asocian a otra—, pero el trazo global aguanta un punto mal puesto. Lo que no aguanta es la lectura de la letra:

DíaPaísFormaLecturas posibles
LUNMéxicozigzagS / 5 / Z / 2
MARChinados picosM
MIÉAustraliabucle abierto por la derechaC / O / D / G
JUEJapón(ver siguiente sección)
VIEBrasilun cuatro4 / A
SÁBFranciazigzag verticalN
DOMEspañavertical con base1 / I / L / J

Solo M y N son inequívocos. El resto no, y está diseñado así: es lo que impide resolver el reto de un vistazo. Hay que ganarse la desambiguación.

El autor corrige el cartel a mitad de concurso

A media noche el enunciado cambió y apareció una nota: «el jueves ha sufrido cambios de última hora». El cartel se había sustituido. El diff de imagen entre las dos versiones localiza el cambio con precisión — y no son dos regiones, como creímos durante el concurso, sino tres:

from PIL import Image; import numpy as np
A = np.array(Image.open('cartel_OLD.jpg').convert('L')).astype(int)
B = np.array(Image.open('cartel_NEW.jpg').convert('L')).astype(int)
D = np.abs(A - B) > 30        # 115.379 píxeles distintos de 11.842.800
# proyectando D sobre el eje Y salen tres bandas de filas:
#   filas  651-667   cols  800-816       154 px  → un carácter: «43 GRUPOS» pasa a «40 GRUPOS»
#   filas 1034-2178  cols ~1890-2360  ~46.700 px  → la columna del JUE, entera
#   filas 2352-2451  cols  288-3994    68.370 px  → el pie del cartel, de lado a lado

La cabecera cuadra con el conteo, columna a columna: 43 = 7+5+7+8+5+5+6 y 40 = 7+5+7+5+5+5+6. El jueves es el único día que cambia: pasó de ocho bandas con horarios irregulares a cinco con los horarios estándar del resto de días. Y aquí está el regalo: la primera y la última banda del jueves nuevo —Capsule y Greenmachine— son de la misma ciudad, Kanazawa. El recorrido se cierra sobre sí mismo. El glifo japonés pasa de polígono ambiguo a bucle cerrado inequívoco: una O.

O sea, la «nota de última hora» no era sabor narrativo: era una fe de erratas. El autor detectó que su jueves original se leía mal y lo rehízo para fijar una letra. Cuando en mitad de un CTF el autor toca un reto, lo que ha tocado es exactamente donde estaba el problema.

Queda la tercera región, la mayor en área, que nadie vio durante el concurso: en el cartel corregido el campo UBICACIÓN y su trozo de cinta desaparecen del pie. Justo el elemento que anclaba que la respuesta era un lugar. Quien descargara el cartel después del cambio no llegó a ver nunca esa pista; nosotros la teníamos porque habíamos guardado el original.

Los callejones, en corto

Los horarios como mensaje: acrósticos por orden de hora, minutos como índices de letra, conversión a husos horarios. Nada. Los horarios son atrezzo — existen solo para darte un orden.

El segundo merece más respeto, porque era un decoy computable de verdad. Con el cartel original de 43 grupos, contar las bandas de cada día y usar ese número como índice cíclico sobre el nombre del país en castellano escupe MALPICA — un topónimo real, en A Coruña. Encajaba con la ubicación tapada, usaba un dato que hasta entonces no servía para nada, y se verificó en código. Falso. La lección: que una mecánica sea computable y produzca una palabra con sentido no la valida; con siete grados de libertad, el espacio de mecánicas que escupen algún topónimo es enorme. Una hipótesis solo vale si es forzosa.

Y el tercero fue el caro: con siete letras ambiguas y «la respuesta es una ubicación», anagramamos. MADISON, SALOMON, SMÅLAND —donde se celebraba el Hultsfred, un festival mítico que quebró: «Lost Fest», el festival perdido, encajaba con todo—, y una rama entera leyendo los glifos como coordenadas. Todas falsas, cada una con una historia estupenda. El patrón es incómodo: si buscas una historia temática, siempre la encuentras. La señal real era otra: los candidatos fallaban en cadena, y leyendo los glifos en orden de día no había ni un hit contra ningún corpus. Cero. Eso no dice «los glifos están mal leídos»; dice «el orden está mal».

La clave: el orden de lectura

Nadie dijo nunca que los siete caracteres se leyeran de lunes a domingo. Lo dimos por hecho porque están dibujados así.

El país de cada día no está impreso: es algo que tú deduces. Y si es un dato derivado, puede ser el criterio de ordenación. Ordena los siete países alfabéticamente en castellano y lee su glifo:

#PaísDíaGlifo
1AustraliaMIÉC
2BrasilVIEA (el «4»)
3ChinaMARM
4EspañaDOMI (el «1»)
5FranciaSÁBN
6JapónJUEO (el bucle nuevo)
7MéxicoLUNS (el «5»)

C · A · M · I · N · O · S = CAMINOS. Sin anagrama. Y lo que convierte esto en la solución y no en otra corazonada es que resuelve las tres ambigüedades a la vez y explica la corrección del autor: Japón tiene que ser O — justo la letra que el autor rehízo el cartel para fijar. Fijada esa O, Australia no puede ser otra (CAMINOS no lleva dos): su bucle abierto es una C, y el jueves nuevo, de paso, te calibra la fuente — te enseña cómo dibuja este autor un círculo cerrado, y el de Australia no lo es. España es I, no L ni J. Una hipótesis que desambigua todo sin elegir a dedo es de otra categoría que una que produce un topónimo bonito. Y de paso explica por qué MADISON parecía tan sólido: con el jueves viejo la gente leía D ahí, y {M,A,D,I,S,O,N} es precisamente el multiset que sale. El decoy estaba construido.

En castellano, además — y esto no es una anécdota sino el control negativo más barato de toda la cadena:

glifo = {'Australia': 'C', 'Brasil': 'A', 'China': 'M', 'España': 'I',
         'Francia': 'N', 'Japón': 'O', 'México': 'S'}
en    = {'Australia': 'Australia', 'Brasil': 'Brazil', 'China': 'China', 'España': 'Spain',
         'Francia': 'France', 'Japón': 'Japan', 'México': 'Mexico'}
print(''.join(glifo[p] for p in sorted(glifo)))              # CAMINOS
print(''.join(glifo[p] for p in sorted(glifo, key=en.get)))  # CAMNOSI

El mismo mecanismo con los países en inglés escupe CAMNOSI — basura. Que un orden dé palabra y el otro no demuestra que el idioma del cartel es parte de la clave, no un accidente de presentación.

El formato

Falta un paso que en este concurso es casi la norma: los glifos con forma de dígito se escriben como dígito. Brasil dibuja un 4 y España un 1; no son «una A» y «una I» que casualmente parecen números, son las formas que el mapa produce, y así es como las escribe el autor. Ese es el motivo real de la ambigüedad del reto: la contraseña mezcla letras y dígitos, y para leerla hay que aceptar la forma tal cual sale del mapa en vez de «corregirla» a texto.

Precisión, ya puestos: por esa misma regla el zigzag de México es un 5, así que la escritura coherente sería C4M1N05; la oficial usa S en la última. Es una inconsistencia del autor y en la práctica no cambia nada, por un matiz que no descubrimos hasta después: el formulario normaliza el leet, así que la forma plana también habría entrado. El paso del leet es cómo se lee el resultado, no una cerradura adicional — el reto ya estaba resuelto al llegar a CAMINOS.

Lo que nos llevamos

  1. Fallo en cadena = falta una clave estructural. Cuando varias lecturas ambiguas producen cada una un candidato plausible y todos caen, deja de generar candidatos y pregúntate qué supuesto no has cuestionado.
  2. Enumera los órdenes. El orden de lectura es un grado de libertad como cualquier otro, y aquí era el grado de libertad. Los órdenes derivables del enunciado van antes que relajar la lectura de los símbolos.
  3. La convergencia temática no es evidencia. Hultsfred era una historia real, documentada y ajustada al título. Y falsa. Exige que la mecánica sea forzosa, no que el destino «mole».
  4. Cuando el autor toca el reto en vivo, ha señalado el sitio. La corrección del jueves no era contenido nuevo: era el autor diciéndote dónde estaba la letra que rompía su puzzle.

Sobre el proceso: este reto se resolvió con asistencia de IA, y el reparto es instructivo. La máquina hizo bien el trabajo pesado —transcribir, barrer acrósticos, geolocalizar, renderizar los glifos, enumerar lecturas contra diccionarios completos— y fue quien propuso la mecánica del geoglifo, la idea no obvia del reto. Lo que no hizo fue frenar: pasó horas generando candidatos cada vez más ingeniosos dentro de un marco equivocado. La clave del orden alfabético salió al relanzar el análisis en frío, con el problema crudo y sin el historial de callejones. La lección no va de modelos: el sesgo viaja en el contexto acumulado, y la forma barata de romperlo es empezar de cero con los mismos datos.

Solución:

C4M1NOS

El material — los scripts, las figuras y el writeup técnico completo de este nivel están en euskal34/solve-it/2.