Descripción
Caesar, one of our employees, has detected recent communications between one of our servers and one server in Tel Aviv. We have captured the traffic in order to analyze it. It seems that the communication it’s composed of just ICMP packets but this server contains confidential information, so we need to be sure that it hasn’t been compromised.
Write-Up
Para la realización del reto disponemos del fichero IDS_Dump.pcap. Tras abrirlo con Wireshark o algún otro programa de análisis de tráfico podemos comprobar que tal y como indica la descripción, el fichero está compuesto por paquetes de tipo ICMP entre dos equipos. En la siguiente captura se muestran los primeros 20 paquetes

En total hay 398 paquetes, 199 de ellos de tipo Echo Request y 199 de tipo Echo Reply. Podemos observar que el tamaño del campo de datos de los paquetes varía, para comprobar los distintos tamaños hacemos uso de la herramienta tshark, extraemos el tamaño y contamos las apariciones de cada uno de los tamaños.
pwnshadow@hackiit:Odd_Traffic# tshark -nr IDS_Dump.pcap -Y icmp.type==8 -T fields -e data.len | sort | uniq -c 23 24 22 30 11 36 16 42 24 48 15 54 11 60 22 66 23 72 21 78 11 84
Podemos observar que cada tamaño aparece entre 11 y 24 veces, además, hay 10 tamaños distintos entre 24 y 84 bytes, con una separación entre cada tamaño de 6 bytes.
Analizando el campo de datos de los paquetes podemos ver que todos están formados por 6 bytes que cambian de valor con cada pareja de paquetes y el resto de bytes son siempre el mismo patrón de datos, siendo más largo o más corto en función de la longitud del paquete, en concreto, el patrón es el siguiente
00 00 00 00 00 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b
Ese patrón es el patrón por defecto de ping de linux, que en función del tamaño de datos indicado es más largo o más corto. Respecto a los primeros bytes, revisando el manual de la herramienta ping descubrimos que es:
ICMP PACKET DETAILS An IP header without options is 20 bytes. An ICMP ECHO_REQUEST packet contains an additional 8 bytes worth of ICMP header followed by an arbitrary amount of data. When a packetsize is given, this indicated the size of this extra piece of data (the default is 56). Thus the amount of data received inside of an IP packet of type ICMP ECHO_REPLY will always be 8 bytes more than the requested data space (the ICMP header). If the data space is at least of size of struct timeval ping uses the beginning bytes of this space to include a timestamp which it uses in the computation of round trip times. If the data space is shorter, no round trip times are given.
Todo parece apuntar a que el campo de datos no contiene nada que podamos utilizar, es por ello que decidimos mirar los tiempos de los paquetes, parece ser que se envian por ráfagas, para analizarlo mejor añadimos una columna que nos muestre el tiempo entre paquetes. Para ello vamos a Edit -> Preferences -> Columns -> + en Type indicamos Delta time y en title el nombre que queramos, por ejemplo Δt.
Con esta nueva columna podemos observar que el tiempo que tarda en llegar la respuesta a cada ICMP ECHO es de ~10 ms, filtramos por icmp.type == 8 con el fin de ver cuanto tiempo tarda en enviar un nuevo ping desde que recibe la respuesta del anterior. El resultado es el siguiente:

Podemos observar que destacan 4 tiempos de espera, 0.0, 0.5, 1.5 y 3.5 segundos. Además, podemos ver que o realiza un envío y luego espera o realiza 3 envíos sin espera entre ellos y luego espera un tiempo, esto se puede entender como que hay dos tipos de envíos, 1 unidad o 3 unidades. Si multiplicamos por dos los tiempos de espera tenemos números más redondos, 1, 3 y 7. Tras pensar en estos tiempos llegamos a la conclusión de que se trata de código Morse.
- 1 paquete -> punto
- 3 paquetes sin esperas -> raya
- 0.5 segundos de espera -> espacio entre partes de una misma letra
- 1.5 segundos de espera -> espacio entre letras
- 3.5 segundos de espera -> espacio entre palabras

Hacemos un pequeño script en python haciendo uso del módulo dpkt en python para intentar recuperar el mensaje enviado:
import dpkt import datetime from decimal import Decimal filename = 'IDS_Dump.pcap' string = [] word = [] letter = '' oldTs = None count = 0 CODE = {'A': '.-', 'B': '-...', 'C': '-.-.', 'D': '-..', 'E': '.', 'F': '..-.', 'G': '--.', 'H': '....', 'I': '..', 'J': '.---', 'K': '-.-', 'L': '.-..', 'M': '--', 'N': '-.', 'O': '---', 'P': '.--.', 'Q': '--.-', 'R': '.-.', 'S': '...', 'T': '-', 'U': '..-', 'V': '...-', 'W': '.--', 'X': '-..-', 'Y': '-.--', 'Z': '--..', '0': '-----', '1': '.----', '2': '..---', '3': '...--', '4': '....-', '5': '.....', '6': '-....', '7': '--...', '8': '---..', '9': '----.', ' ': ' ' } for ts, pkt in dpkt.pcap.Reader(open(filename, 'rb')): eth = dpkt.ethernet.Ethernet(pkt) ip = eth.data if isinstance(ip.data, dpkt.icmp.ICMP): if oldTs is None: oldTs = ts icmp = ip.data if icmp.type == 8: diff = int((ts-oldTs)/0.5) if diff != 0: # if delay if count == 1: # point letter += '.' elif count == 3: # dash letter += '-' if diff == 3: # space between letters word.append(letter) letter = '' if diff == 7: # space between words word.append(letter) string.append(word) word = [] letter = '' count = 1 else: count += 1 oldTs = ts if count == 1: letter += '.' elif count == 3: letter += '-' word.append(letter) string.append(word) result = '' for word in string: for letter in word: lKey = [key for key, value in CODE.items() if value == letter][0] result += str(lKey) result += ' ' print(result)
pwnshadow@hackiit:Odd_Traffic# python3 decode_morse.py BPM NTIO QA UWZAM QA VMDMZ BWW WTL NWZ KBN
Parece que el resultado está cifrado, en la descripción indicaba el nombre de Caesar, posiblemente se trate de cifrado César. Añadimos las siguientes líneas al código python anterior:
def encrypt(string, shift): cipher = '' for char in string: if char == ' ': cipher = cipher + char elif char.isupper(): cipher = cipher + chr((ord(char) + shift - 65) % 26 + 65) else: cipher = cipher + chr((ord(char) + shift - 97) % 26 + 97) return cipher for i in range(0, 26): print(i, encrypt(result, i))
Volvemos a ejecutar el código y comprobamos el resultado
pwnshadow@hackiit:Odd_Traffic# python3 decode_morse.py BPM NTIO QA UWZAM QA VMDMZ BWW WTL NWZ KBN 0 BPM NTIO QA UWZAM QA VMDMZ BWW WTL NWZ KBN 1 CQN OUJP RB VXABN RB WNENA CXX XUM OXA LCO 2 DRO PVKQ SC WYBCO SC XOFOB DYY YVN PYB MDP 3 ESP QWLR TD XZCDP TD YPGPC EZZ ZWO QZC NEQ 4 FTQ RXMS UE YADEQ UE ZQHQD FAA AXP RAD OFR 5 GUR SYNT VF ZBEFR VF ARIRE GBB BYQ SBE PGS 6 HVS TZOU WG ACFGS WG BSJSF HCC CZR TCF QHT 7 IWT UAPV XH BDGHT XH CTKTG IDD DAS UDG RIU 8 JXU VBQW YI CEHIU YI DULUH JEE EBT VEH SJV 9 KYV WCRX ZJ DFIJV ZJ EVMVI KFF FCU WFI TKW 10 LZW XDSY AK EGJKW AK FWNWJ LGG GDV XGJ ULX 11 MAX YETZ BL FHKLX BL GXOXK MHH HEW YHK VMY 12 NBY ZFUA CM GILMY CM HYPYL NII IFX ZIL WNZ 13 OCZ AGVB DN HJMNZ DN IZQZM OJJ JGY AJM XOA 14 PDA BHWC EO IKNOA EO JARAN PKK KHZ BKN YPB 15 QEB CIXD FP JLOPB FP KBSBO QLL LIA CLO ZQC 16 RFC DJYE GQ KMPQC GQ LCTCP RMM MJB DMP ARD 17 SGD EKZF HR LNQRD HR MDUDQ SNN NKC ENQ BSE 18 THE FLAG IS MORSE IS NEVER TOO OLD FOR CTF 19 UIF GMBH JT NPSTF JT OFWFS UPP PME GPS DUG 20 VJG HNCI KU OQTUG KU PGXGT VQQ QNF HQT EVH 21 WKH IODJ LV PRUVH LV QHYHU WRR ROG IRU FWI 22 XLI JPEK MW QSVWI MW RIZIV XSS SPH JSV GXJ 23 YMJ KQFL NX RTWXJ NX SJAJW YTT TQI KTW HYK 24 ZNK LRGM OY SUXYK OY TKBKX ZUU URJ LUX IZL 25 AOL MSHN PZ TVYZL PZ ULCLY AVV VSK MVY JAM
Finalmente hemos conseguido la flag: THE FLAG IS MORSE IS NEVER TOO OLD FOR CTF
Tan solo hace falta ponerla en el formato del CTF
hackiit_flag{MORSE IS NEVER TOO OLD FOR CTF}
Recent Comments