Exfiltration

Catégorie: Forensique

Points: 257

Description: Notre SoC a détecté qu'un document confidentiel avait été exfiltré ! La méthode utilisée ne semble pas avancée et heureusement, une capture réseau a pu être faite au bon moment... Retrouvez ce document.

Fichiers: Exfiltration.pcap

TL;DR

Un document a ete extrait via des requêtes POST. Un xor sur le fichier retrouvé nous permet de lire le flag dans un document word.

Méthodologie

Quand on ouvre le fichier avec wireshark on s'aperçoit qu'il y a 27242 paquets à regarder !

Crying

Bon bah... C'est parti !

En survolant les paquets on remarque qu'il y a pas mal de flux TCP.

what_if_I_follow_the_TCP_stream_?

En parcourant tous les flux TCP on obtient plusieurs échanges intéressants du même type:

TCP_exfiltration

Ce qui nous interpelle c'est notamment ce message:

<h1>Welcome to my panel!</h1>
<p>If you see this page, the malware is successfully installed and
working. No further configuration is required.</p>

Et le fait que les requêtes sont faites via la méthode POST avec de la donnée passe en paramètre de la forme: data=3528706771637b6b6d63b9c7f82d736365637363656373636563786365632c11000f004c4b1116&uuid=80cxgrf7ws5phwlb7z3q

A chaque requête le paramètre data change mais pas l'uuid. l'uuid doit être un identifiant unique pour permettre au serveur distant de reconstituer le fichier exfiltre via le champ data.

Un petit script en python pour récupérer tous ces paquets et les assembler.

>_ cat extract.py
#!/usr/bin/env python3

from scapy.all import *
import binascii

packets = rdpcap('exfiltration.pcap')

data = ''
for packet in packets:
    if packet[IP].dst == '198.18.0.10' and packet[IP].src == '192.168.1.26':
        if Raw in packet:
            p = str(packet[Raw].load.decode("UTF-8"))
            p = p.split("&uuid")[0]
            p = p.split('data=')[1:]
            if p:
                p = p[0]
                data += p

print(data)

file = open("out.txt", 'wb')

file.write(binascii.unhexlify(data))
>_ ./extract.py
>_ file out.txt
out.txt: data

Malheureusement le fichier extrait est illisible.

Heureusement, lors de la conception du script j'ai oublie de filtrer uniquement sur les paquets qui contenait de la donnée brute (le champ Raw). En le testant, il plantait des qu'un paquet ne contenait pas le champ Raw. Je me suis aperçu que le paquet en question était un paquet ICMP avec une donnée intéressante: config : exfiltered_file_size=4193bytes

Pour en savoir plus on filtre dans wireshark sur les paquets ICMP:

wireshark_icmp_inspection

Et on obtient 4 infos intéressantes:

config : exfiltered_file_size=4193bytes

config : file_type=DOCX

config : data_len_for_each_packet=random

config : encryption=XOR

Le fichier fais bien 4193 bytes, donc toute la donnée a bien ete extraite des paquets.

>_ ls -l out.txt
-rw-r--r-- 1 lambdhack users 4193 May 22 20:06 out.txt

Maintenant on sait que le fichier exfiltre est un document word et que le chiffrement utilise est un xor. Pour retrouver la cle on va xorer les premiers bits du fichier avec le magic number des document words. Un petit tour sur File signature table pour trouver le magic number d'un docx.

docx_magic_number

Après avoir fait un xor, on retrouve la cle qui est ecsc évidemment !

Maintenant on xor cette cle sur tous le fichier pour retrouver le docx original.

>_ cat xor.py
#!/usr/bin/env python3
import binascii

def xor(file1_b, key):

    file_size = len(file1_b)
    key_size = len(key)
    xord_byte_array = bytearray(file_size)

    key_pad = key
    for i in range(file_size):
        key_pad += key

    # XOR between the files
    for i in range(file_size):
        xord_byte_array[i] = file1_b[i] ^ key_pad[i]

    open('data.docx', 'wb').write(xord_byte_array)

if __name__ == '__main__':
    key = 'ecsc'.encode()

    data = open('out.txt', 'rb').read()
    xor(data, key)
>_ file data.docx
data.docx: Microsoft Word 2007+

Le fichier a bien ete déchiffré.

docx_flag

FLAG_IS:

ECSC{v3ry_n01sy_3xf1ltr4t10n}