Ciberseg 2019: forense
En este post voy a explicar mis soluciones a los retos del Ciberseg de 2019. En concreto, este artículo se corresponde con los de la categoría de forense.
El Ciberseg es un congreso que tiene lugar todos los años por estas fechas en la Universidad de Alcalá de Henares. La verdad es que los años anteriores siempre ha sido divertido, y este año no ha sido menos :) Además, el podio ha estado muy reñido y hubo sorpresas de última hora :D (al final gané en la última hora, literalmente, por apenas unos pocos puntitos).
En fin, estos son los retos y sus soluciones. Para los que haga falta, dejaré también los recursos necesarios que nos aportaron para intentar el reto por vuestra cuenta.
1.- Exfiltration files (25 puntos)
La descripción de este primer reto dice:
Hemos interceptado esta foto parece que oculta algún mensaje.
Esta es la imagen que nos dan:
Si buscamos con binwalk
, nos dice que hay una imagen dentro:
$ binwalk imagen.jpg
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 JPEG image data, JFIF standard 1.01
51961 0xCAF9 JPEG image data, EXIF standard
51973 0xCB05 TIFF image data, big-endian, offset of first image directory: 8
71107 0x115C3 Unix path: /www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http
74915 0x124A3 Copyright string: "Copyright 1999 Adobe Systems Incorporated"
Sin embargo, no puede extraer la información. No sé qué criterio sigue binwalk para
extraer a veces sí y a veces no los datos. Pero bueno, podemos usar
dd id=imagen.jpg of=imagen_extracted.jpg bs=51961 skip=1
para extraer los datos. Luego,
simplemente abrimos la nueva imagen y obtenemos la solución:
Otra forma de resolverlo es mirando las cadenas de texto:
$ strings imagen.jpg | grep -Poi 'flag{[^}]*}'
flag{RapidoYFurioso}
flag{RapidoYFurioso}
De cualquier forma, la flag es: flag{RapidoYFurioso}
.
2.- Break the gesture (50 puntos)
Este reto no tenía descripción. Simplemente hay un archivo: gesture.key.
Aunque me costó un rato darme cuenta, este archivo es el que se usa en Android para guardar el patrón de desbloqueo del terminal.
Para sacar el patrón, se puede utilizar cualquier herramienta. Por ejemplo, yo utilicé androidpatternlock y la saca en cuestión de segundos:
$ time python2 aplc.py ../gesture.key
################################
# Android Pattern Lock Cracker #
# v0.2 #
# ---------------------------- #
# Written by Chema Garcia #
# http://safetybits.net #
# chema@safetybits.net #
# @sch3m4 #
################################
[i] Taken from: http://forensics.spreitzenbarth.de/2012/02/28/cracking-the-pattern-lock-on-android/
[:D] The pattern has been FOUND!!! => 210345876
[+] Gesture:
----- ----- -----
| 3 | | 2 | | 1 |
----- ----- -----
----- ----- -----
| 4 | | 5 | | 6 |
----- ----- -----
----- ----- -----
| 9 | | 8 | | 7 |
----- ----- -----
It took: 1.1906 seconds
real 0m1.251s
user 0m3.894s
sys 0m0.084s
Y ya está. No tiene ninguna complicación más.
La flag es: flag{210345876}
.
3.- Pcap (250 puntos)
La descripción del reto dice así:
Creo que se han llevado algo. ¿Me puedes decir el nombre del archivo?
También se adjunta esta captura de red.
Al examinarla con Wireshark vemos que hay muchos paquetes de TCP, HTTPS… Si los ordenamos de mayor a menor tamaño, esperando que el archivo exfiltrado sea grande, vemos un par de paquetes TCP con texto en claro que parece Base64:
Si seguimos la traza de esa conversación, vemos que hay bastantes más datos. Cuando los extraemos y decodificamos en Base64 obtenemos una cadena que tiene toda la pinta de ser un volcado hexadecimal:
$ base64 -d b64
37 7A BC AF 27 1C 00 04 65 3D 77 8C 58 0F 00 00 00 00 00 00 6A 00 00 00 00 00 00 00 ...
Este volcado hexadecimal se corresponde con un archivo 7z que tenemos que examinar para ver qué archivos fueron exfiltrados:
$ base64 -d b64 | tr -d '\r\n\t ' | xxd -r -ps > file.7z
$ file file.7z
file.7z: 7-zip archive data, version 0.4
$ 7z l file.7z
(...)
Scanning the drive for archives:
1 file, 4066 bytes (4 KiB)
Listing archive: file.7z
--
(...)
Date Time Attr Size Compressed Name
------------------- ----- ------------ ------------ ------------------------
2018-11-21 16:13:51 ....A 19385 3928 credit_cards.txt
------------------- ----- ------------ ------------ ------------------------
2018-11-21 16:13:51 19385 3928 1 files
Pues ya está. Sólo hay un archivo en el comprimido: credit_cards.txt
.
La flag es: flag{credit_cards.txt}
4.- Pcap2 (300 puntos)
La descripción dice:
No network traffic
Y se adjunta esta captura.
Si la abrimos vemos que es una captura USB. Sé que habría que extraer el ID del USB que nos interesa, estudiar los valores y blablabla; pero no tenía ganas de hacer eso y preferí buscar en internet algún script porque este es un reto muy común y normalmente basta con extraer las pulsaciones del teclado. Si queréis una explicación más detallada, aquí están los write-ups de @xbytemx otra persona que participó en la competición. Está muy bien explicado y se sigue con facilidad.
En fin, el caso es que yo utilicé este script, que lo saqué de este artículo:
# Sacado de
# https://medium.com/@ali.bawazeeer/kaizen-ctf-2018-reverse-engineer-usb-keystrok-from-pcap-file-2412351679f4
newmap = {
2: "PostFail",
4: "a", 5: "b", 6: "c", 7: "d", 8: "e", 9: "f", 10: "g", 11: "h", 12: "i",
13: "j", 14: "k", 15: "l", 16: "m", 17: "n", 18: "o", 19: "p", 20: "q", 21: "r",
22: "s", 23: "t", 24: "u", 25: "v", 26: "w", 27: "x", 28: "y", 29: "z", 30: "1",
31: "2", 32: "3", 33: "4", 34: "5", 35: "6", 36: "7", 37: "8", 38: "9", 39: "0",
40: "Enter", 41: "esc", 42: "del", 43: "tab", 44: "space",
45: "-", 47: "[", 48: "]", 56: "/",
57: "CapsLock", 79: "RightArrow", 80: "LetfArrow"
}
myKeys = open('hexoutput.txt')
i = 1
for line in myKeys:
bytesArray = bytearray.fromhex(line.strip())
# print "Line Number: " + str (i)
for byte in bytesArray:
if byte != 0:
keyVal = int(byte)
if keyVal in newmap:
# print "Value map: " + str (keyVal) + " --> " + newmap [keyVal]
print newmap[keyVal]
else:
# print "No map found for this value: " + str(keyVal)
print format(byte, '02X')
i+=1
Exportando el CSV con Wireshark (como dice en ese artículo) y decodificando los valores, nos sale la solución:
$ python2 decode.py
f
l
l
a
a
a
g
g
PostFail
PostFail
[
PostFail
u
u
s
s
b
m
o
n
i
t
t
o
o
r
PostFail
PostFail
]
PostFail
No sé por qué aparecen los valores repetidos, porque no he perdido ni un segundo en saber cómo funcionan esto de las interrupciones del teclado capturadas en un pcap, aunque quizá debería…
La flag es: flag{usbmonitor}
5.- Creo que me espían… (350 puntos)
La descripción dice así:
Sospechamos que el dispositivo Android ha sido comprometido y queremos que lo investigues. Para ello te damos la partición data y necesitamos saber cual les la APK que lo ha comprometido.
Y se adjunta la imagen del dispositivo.
Se puede descomprimir la imagen con ewfmount
y luego montarla como un dispositivo más.
Una vez se tiene montada, empezamos a navegar por el sistema de archivos. La primera
parada es el directorio app
, que parece que tiene un par de aplicaciones candidatas,
pero sólo están ahí para despistar.
Lo que nos importa realmente es mirar las aplicaciones instaladas (es decir, las que
están bajo /data/
). De todas estas, sólo unas pocas tienen datos y apenas un par son
instaladas por el usuario. El resto son las que vienen por defecto para usar el sistema
(teléfono, contactos, etc.); así que, en lugar de buscar la aplicación maliciosa, podemos
intentar buscar el punto de entrada de esta aplicación. Los candidatos más
prometedores son: com.android.email
y com.android.browser
.
Tras analizar la aplicación de correo vemos que está limpia, por lo que pasamos a nuestra segunda opción: el navegador.
Al entrar en /data/com.android.browser/
vemos un archivo llamado wCLxU.dex
.
Spoiler alert! Esta es la aplicación maliciosa.
Yo no me di cuenta en su momento, así que seguiré como si no lo hubiera visto…
Rebuscando entre las BDD que hay en el directorio databases/
hay una que se llama
browser2.db y donde podemos ver algo interesante:
sqlite> select * from history;
(...)
_id title url created date visits user_entered
8 http://192.168.74.128/i6ADxOqMEyyI http://192.168.74.128/i6ADxOqMEyyI 0 1516629327266 1 0
9 http://192.168.74.128/i6ADxOqMEyyI/EeMVfx/ http://192.168.74.128/i6ADxOqMEyyI/EeMVfx/ 0 1516629327667 1 0
Esto tiene buena pinta. Por lo visto, alguien se descargó un archivo de un directorio con un nombre raro en un servidor que no tiene ni nombre de dominio, accediendo directamente por la IP (que, además, pertenece a una red privada). Esto tiene toda la pinta de ser lo que buscamos.
Sabiendo que el usuario se descargó algo chungo, empezamos a indagar en
cache/webviewCacheChromium/
, que es donde parece que se guarda la caché del navegador.
Sabiendo que buscamos un archivo .apk
, podemos hacer un grep rápido para ver si
suena la campana:
$ grep -PoR '[A-Za-z0-9]*[.]apk' .
./f_0000d6:wCLxU.apk
./f_0000d7:graphilos.apk
./f_0000d7:graphilos.apk
./f_0000d7:graphilos.apk
./f_0000d7:graphilos.apk
./f_0000d7:graphilos.apk
./f_0000d7:graphilos.apk
./f_0000d7:graphilos.apk
./f_0000d7:graphilos.apk
./f_0000d7:graphilos.apk
./f_0000d7:heagoo.apk
./f_0000d7:heagoo.apk
./f_0000d7:heagoo.apk
./f_0000d7:heagoo.apk
./f_0000d7:heagoo.apk
./f_0000d7:heagoo.apk
./f_0000d7:heagoo.apk
./f_0000d7:heagoo.apk
./f_0000d7:heagoo.apk
./f_0000d7:com.apk
./f_0000d7:com.apk
./f_0000d7:com.apk
./f_0000d7:com.apk
./f_0000d7:com.apk
./f_0000d7:com.apk
./f_0000d7:com.apk
./f_0000d7:com.apk
./f_0000d7:com.apk
./f_0000d7:www.apk
./f_0000d7:www.apk
./f_0000d7:www.apk
./f_0000d7:www.apk
./f_0000d7:www.apk
./f_0000d7:www.apk
./f_0000d7:www.apk
./f_0000d7:www.apk
./f_0000d7:www.apk
./f_0000d7:www.apk
./f_0000d7:www.apk
./f_0000d7:.apk
Et voilà! De todos esos resultados, el más sospechoso es el primero, wCLxU.apk
.
¿Recordáis el archivo ese que vimos antes que se llamaba wCLxU.dex
? Pues ahora es
cuando algo hizo click en mi cabeza y me di cuenta de que había perdido como una hora
rebuscando entre las tripas del historial de navegación, cuando tenía la solución delante
de mis narices…
En fin… La flag es: flag{wCLxU.apk}
Y sin habernos dado cuenta, ya hemos terminado todos los retos de forense :)
Siempre me lo paso bien con los retos del Ciberseg, y este año no ha sido menos. Espero poder competir el año que viene, que seguro que se superan otra vez.
También quiero dar mi enhorabuena a los organizadores por todo su esfuerzo y su creatividad para crear retos fuera de lo común :D