#GeratuEtxean: HackIt! Level 2

Nuestros espias han localizado la flag de este nivel, pero… ¡Alguien ha destruido la caja de password! Afortunadamente, nos han proporcionado instrucciones para enviarla, pero no acabamos de entenderlas… ¿Nos ayudas?

— BEGIN TRANSMISSION —
PAYLOAD FOUND ON trololo@54.171.128.20:34342
ACCESS b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW QyNTUxOQAAACD48tA2UHkNwf1gjzFoefbSiiA3s0+FIYWYOlxHuwDAVAAAALhSf19rUn9f awAAAAtzc2gtZWQyNTUxOQAAACD48tA2UHkNwf1gjzFoefbSiiA3s0+FIYWYOlxHuwDAVA AAAECeukBbUT2Vlozfd98BRRvKGCFRc0mdvRhAItlDfp1U7vjy0DZQeQ3B/WCPMWh59tKK IDezT4UhhZg6XEe7AMBUAAAALnJvb3RAaXAtMTcyLTMxLTYtNjUuZXUtd2VzdC0xLmNvbX B1dGUuaW50ZXJuYWwBAgMEBQYH
IDENTIFY USING 20ce8a7cc776a39ad291d4648e3e39ae.hax
SEND FLAG AS TEXT
— END TRANSMISSION —

Si nos intentamos conectar por ssh trololo@54.171.128.20 -p 34342 vemos que el servidor responde, pidiendo clave pública. Descodificando el string de ACCESS de base64, vemos una referencia a una máquina remota (AWS) y esta keyword: openssh-key-v1nonenone3

Haciendo pruebas, detectamos que la clave sigue el algoritmo DSA (no RSA). La preparamos:

$ cat id_dsa
—–BEGIN OPENSSH PRIVATE KEY—–
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACD48tA2UHkNwf1gjzFoefbSiiA3s0+FIYWYOlxHuwDAVAAAALhSf19rUn9f
awAAAAtzc2gtZWQyNTUxOQAAACD48tA2UHkNwf1gjzFoefbSiiA3s0+FIYWYOlxHuwDAVA
AAAECeukBbUT2Vlozfd98BRRvKGCFRc0mdvRhAItlDfp1U7vjy0DZQeQ3B/WCPMWh59tKK
IDezT4UhhZg6XEe7AMBUAAAALnJvb3RAaXAtMTcyLTMxLTYtNjUuZXUtd2VzdC0xLmNvbX
B1dGUuaW50ZXJuYWwBAgMEBQYH
—–END OPENSSH PRIVATE KEY—–

Y conectamos:

$ ssh -i id_dsa -p 34342 trololo@54.171.128.20
Last login: Sat Mar 28 14:34:31 2020 from xxxxxxx
-sh-4.2$

Es una shell restringida (ni ls, ni gaitas). Además, si intentas borrar algo, te añade un espacio en blanco… En fin… El autocompletamiento con tabulador funciona. Vemos que existen algunos directorios, entre ellos /bin. Añadimos /bin al PATH y disponemos de cat, dig, ls y nsupdate.

El comando dig nos da una pista…. recordemos también que no hemos usado esta parte del enunciado:

IDENTIFY USING 20ce8a7cc776a39ad291d4648e3e39ae.hax

Investigando un poco vemos que existe un proyecto con TLD .hax para DNS dinámicos. La otra pista «SEND FLAG AS TEXT » nos parece indicar que necesitamos crear un registro TXT con la Flag. ¿Pero dónde está la flag? Bueno, no había muchos directorios en la máquina restringida donde nos encontramos, así que rastreando carpetas nos encontramos con /var/tmp/secret,

-sh-4.2$ cat /var/tmp/secret
Is0latI0nFl4w3dNetW0rkz!

Pedimos un dig del TLD hax:

dig -t txt 20ce8a7cc776a39ad291d4648e3e39ae.hax

y vemos que vamos bien:

-sh-4.2$ dig -t txt 20ce8a7cc776a39ad291d4648e3e39ae.hax

; <<>> DiG 9.11.4-P2-RedHat-9.11.4-9.P2.amzn2.0.2 <<>> -t txt 20ce8a7cc776a39ad291d4648e3e39ae.hax
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 12895
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 2

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;20ce8a7cc776a39ad291d4648e3e39ae.hax. IN TXT

;; AUTHORITY SECTION:
hax. 60 IN NS ns1.hax.

;; ADDITIONAL SECTION:
ns1.hax. 60 IN A 127.0.0.1

;; Query time: 0 msec
;; SERVER: 172.31.6.65#53(172.31.6.65)
;; WHEN: Sat Mar 28 16:41:53 UTC 2020
;; MSG SIZE rcvd: 136

Nos queda hacer el nsupdate … pero antes, veamos quién resuelve los DNS en esta máquina:

-sh-4.2$ cat /etc/resolv.conf
nameserver 172.31.6.65

$ nsupdate

server 172.31.6.65

update add 20ce8a7cc776a39ad291d4648e3e39ae.hax. 300 TXT «Is0latI0nFl4w3dNetW0rkz!»

send

Y listo! Pulsamos en el botón que nos ha preparado la ORG en ese level y pasamos de nivel

#GeratuEtxean: HackIt! Level 1

Esta edición de la GipuzkoaEncounter ha sido un tanto extraña. Confinados en casa, hemos intentado llevarlo lo mejor posible. Aunque no es para nada lo mismo y todos preferimos la presencial, esta edición online no ha estado nada mal. Y para no perder las buenas costumbres, vayamos con el write-up.

¡Ayuda! ¡Nos han hackeado la cuenta de Discord! Parece que han hecho cosas raras. Hemos conseguido un log de lo que han hecho…

Nos pasan un fichero .har (un dump de una conexión HTTP). Aunque internamente es simplemente un enorme fichero JSON, lo mejor para visualizarlo de forma rápida es abrirlo directamente con el editor de Chrome DevTools, pestaña Network. Importamos el fichero HAR para echarle un vistazo y vemos lo siguiente.

Podemos ver puntos verdes en la línea del timeline, donde van varios POST

Seleccionando uno de esos puntos:

Mensajes POST hacia el servidor de Discord. Se ve que el usuario está tecleando contenido («0ur53»)

El problema es que no vale con recorrer esos mensajes de tipo POST. Hay una complicación: el autor del reto ha editado algunos trozos (mensajes con método HTTP PATCH) e incluso borrado otros (mensajes DELETE).

Solucionamos con un pequeño script en nodejs

HackIt 2019, level 3³

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.

$ tshark -r dump -T fields -e data

1b0010408d2e07aeae7d91401400000000000040855ae9b632828401
12004a0000000059c86aa10000000000001ac9
0a0021000000028daf8dbd
0a000e000000028daf8dbd

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.

HackIt! 2019, Level 2

Este level parece que se le atragantó a muchos grupos. Aunque estuvimos unas cuantas horas dándole vueltas, una vez resuelto te das cuenta de que, lo que lo hacía complejo, realmente eran varios red-herring o falsas pistas. Si las seguías, estabas muerto. El level empieza con 3 ficheros: yellow, red, green. Aquí está el primer anzuelo: ¿para qué estos colores?… En fin, sacando strings, el que más llama la atención es red.

Juanan-2:2 juanan$ strings -n 12 red|more
Ktablered1000red1000^LCREATE TABLE red1000(redz blob)N
ytablered100red100
CREATE TABLE red100(reda varchar(10),redb varchar(10))H
utablered9red9
CREATE TABLE red9(reda varchar(10),redb varchar(10))H
utablered8red8
CREATE TABLE red8(reda varchar(10),redb varchar(10))H
utablered7red7
CREATE TABLE red7(reda varchar(10),redb varchar(10))H
utablered6red6
CREATE TABLE red6(reda varchar(10),redb varchar(10))H
utablered5red5
CREATE TABLE red5(reda varchar(10),redb varchar(10))H
utablered4red4
...
CREATE TABLE red1(reda varchar(10),redb varchar(10))
0000000 5473 6572 6d34 3352 000a

Vaya… una base de datos, probablemente SQLite. Y el campo redz de la tabla red1000 es de tipo blob. Estuvimos dándole vueltas y vueltas a esto. Conseguimos incluso importar la estructura de las tablas.

En la tabla red1, la columna reda tiene algo:

Pero eso ya salía en los strings, no hacía falta liarse la manta con SQLite… Mmmh, veamos qué significa:

misterio = [0x54,0x73,0x65,0x72,0x6d,0x34,0x33,0x52,0x00,0x0a]
import binascii
print("".join( chr(c) for c in misterio))
Tserm43R

¿Tserm43R? WTF? @ochoto comentó en el grupo que tal vez habría que darle la vuelta a cada par de valores (big endian?) porque los últimos bytes son un salto de línea + fin del string invertidos (0x00, 0x0a). Vamos allá (quitando el salto de línea):

misterio = [0x54,0x73,0x65,0x72,0x6d,0x34,0x33,0x52]
"".join([chr(a)+chr(b) for a,b in [i for i in zip(misterio[1::2], misterio[::2])]])

'sTre4mR3'

Tiene sentido, parece un trozo de string en h4x0r. Dejémoslo ahí y vayamos a por green. Este fue más fácil:

$ binwalk -e green

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
27337196      0x1A121EC       HPACK archive data
33554432      0x2000000       gzip compressed data, has original file name: "trololo", from Unix, last modified: 2019-07-15 23:29:50

$ ls -al _green.extracted/
total 8
drwxr-xr-x   3 juanan  wheel    96 Jul 25 21:28 .
drwxrwxrwt@ 70 root    wheel  2240 Jul 29 22:15 ..
-rw-r--r--   1 juanan  wheel     8 Jul 25 21:28 trololo

$ cat _green.extracted/trololo
ce1VEd!

Vaya, si concatenamos red con green (mismo orden que el enunciado), obtenemos ‘sTre4mR3ce1VEd!’. Tiene muy buena pinta. Sólo nos queda un fichero, yellow. Es un fichero binario, sin ningún magic number ni strings asociados. Tras muchas vueltas, se nos ocurrió algo evidente (a que sí, @navarparty? XDDD), abrirlo con Audacity:

Bingo, se oye a alguien deletreando, en inglés y a mucha velocidad, la parte que nos falta del password. Ajustando la velocidad y teniendo en cuenta que las zonas más oscuras de la señal reflejan mayúsculas, obtenemos R0tT3nB1t.

Así que… R0tT3nB1tsTre4mR3ce1VEd!

Nota: este post no refleja la dificultad de la prueba. No fue «tan fácil» como parece 🙂 Estuvimos muuuuuuuuuucho tiempo analizando los 3 binarios hasta encontrar la secuencia de pasos y herramientas adecuadas.

HackIt! 2019, level 1

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:

function q(e){var a=">,------------------------------------------------------------------------------------[<+>[-]],----------------------------------------------------[<+>[-]],------------------------------------------------------------------------------------------------------------------[<+>[-]],----------------------------------------------------------------------------------------------------------------[<+>[-]],-------------------------------------------------[<+>[-]],--------------------------------------------------------------------------------------------------------------------[<+>[-]],-----------------------------------------------------------------------------------[<+>[-]],-------------------------------------------------------------------[<+>[-]],------------------------------------------------------------------------------------------------------------------[<+>[-]],-------------------------------------------------[<+>[-]],----------------------------------------------------------------------------------------------------------------[<+>[-]],------------------------------------------------------------------------------------[<+>[-]],[<+>[-]][-]+<[>>>++[>+++[>+++++++++++++++++++<-]<-]>>.-------------.-.<<<<[-]<[-]]>[>>>++[>+++[>+++++++++++++++++<-]<-]>>+.[>+>+<<-]>+++++++++++.>--..<----.<<<[-]]";let r=0,f=0;var i=a.length,c=new Uint8Array(3e4),s="",b=10240,k=0;for(r=0;r<i&&!(b<0);r++)switch(b--,a[r]){case">":f++;break;case"<":f>0&&f--;break;case"+":c[f]=c[f]+1&255;break;case"-":c[f]=c[f]-1&255;break;case".":s+=String.fromCharCode(c[f]);break;case",":k>=e.length?c[f]=0:c[f]=e.charCodeAt(k),k++;break;case"[":if(!c[f])for(var t=0;a[++r];)if("["===a[r])t++;else if("]"===a[r]){if(!(t>0))break;t--}break;case"]":if(c[f])for(t=0;a[--r];)if("]"===a[r])t++;else if("["===a[r]){if(!(t>0))break;t--}}return s}
$(function(){$('#password').keyup(function(e){$('#password').css({'background-color':q($('#password').val())});});});

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='')