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}

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *