R0 CREW

Nuit Du Hack CTF : Forensics 300

Закончился Nuit Du Hack CTF 2014 – Qualifications.

Мне удалось решить всего четыре таска из разный категорий Big Momma (Misc 200), Here kitty kitty ! (Steganalysis 50), Carbonara (Crypto 50) и Data extraction (Forensics 300). Причем последний сдать не успел.

Именно о нем мы и поговорим сегодня. Первая причина: таск решило всего 10 команд. Причина вторая: хотя таск и относится к категории Forensics, но фактически вся основная работа лежит в реверсе.

Задание выглядело так.
Data extraction
On a machine infected by Jupiter malware, some documents were transfered through the network.
Score 300
Link http://static.nuitduhack.com/Jupiter.pcap

Первая часть таска действительно свойственна форенсику, так как предстоит извлечь информацию из pcap файла. Воспользуемся как всегда Wireshark’ом . Во втором TCP потоке можно найти исполняемый файл виндовс переданный по сети.

Установим фильтр «tcp.stream eq 2» и через Menu->Analyze -> Follow TCP Stream (ПКМ -> Follow TCP Stream) получаем данные потока (весь обмен). Как видно из заголовков, был запрошен и успешно получен файл H5N1.exe (длина 261923).

Сохраним поток в файл и вырежем из него наш сэмпл. Начало определяется сигнатурой ‘MZ’, конец определяется, известной нам длиной файла.

Прежде чем приступить к изучению, обратим внимание на то, что это малварь (machine infected by Jupiter malware). «Береженого Бог бережет», поэтому проверим файл на virustotal на всякий случай (не одни мы такие умные и кто-то уже до нас его проверял, сервис сразу отдает отчет). Большинство заявили, что файл запакован MEW. Распакуем с помощью UnMEW (отлично распаковывается) и повторно проверим на virustotal. Ничего страшного :).

Приступим к изучению, с учетом того, что файл был запакован, его структура нарушена и IDA нам не очень помогла, сегмента два, импорт от распаковщика, сигнатуры автоматически не наложились да еще и писано на boost, сигнатур к которому в IDA нет. Про мытарства с реверсом рассказывать особенно нечего: кода много, код нормально не запускается ввиду того, что ждет ответа от своего сервера на 192.168.130.10.

Внимание!! Зверек себя копирует (на Win 7) по пути ‘C:\Users\OKOB\AppData\Roaming\mssvc.exe’ и ставит в автозапуск в реестре.

_0.ELO:00407A95 call SetAsVirus

Ворует он инфу только из PDF файлов обходя рекурсивно все каталоги, которые лежат ниже ‘C:/Users/OKOB/Documents’.

_0.ELO:00407A9E call FindPDF_Victims

Далее полный путь к файлу и тело файла преобразуется и передается по сети. Данных передаются пакетами, состоящими из блоков. Блоки двух типов: цифровые и текстовые (строковые). Каждый блок в пакете имеет номер. Номера блоков формируются по разному. Блоки объединены в пакеты (один цифровой блок и два строковых). Сформированные номера блоков в пакете 08h, 12h, 1Ah. Если представляемые цифровые данные или длина текстовых данных меньше 80h, то они представляются одним байтом, иначе двумя байтами. В первом блоке передается тип пакета (0\1), во втором блоке ключ «шифрования» и в третьем сами зашифрованные данные.

Посмотреть (поиметь) такие данные можно, сдампив в Wireshark’е TCP потоки 3…6 (фильтр «tcp.stream eq 3» и т.п.).

Код на Python, который восстанавливает из этих данных исходные PDF файлы представлен ниже.

 #!/usr/bin/env python
#-*- coding:utf-8 -*-

import sys

if len(sys.argv) != 2:
   print 'Uses: python', sys.argv[0], '<xN>'
   print 'P.S.: flag in file "x3"'
   sys.exit(0)

f = open(sys.argv[1],'rb')
fo = open('out_%c.pdf'%sys.argv[1][1:2],'wb')

fl = 1
while 1:
  try:
     xBlock = ord(f.read(1))
  except:
     break
  #print xBlock
  if xBlock == 0x08: # block1
    idx = ord(f.read(1))
    if idx >= 0x80:
      idx &= ~0x80
      idx += (ord(f.read(1)) << 7)
    #print 'index:', idx
  xBlock = ord(f.read(1))
  #print xBlock
  if xBlock == 0x12: # block2
    _len_key = ord(f.read(1))
    #print 'len_key:', _len_key
    if fl == 1:
       key1 = f.read(_len_key)
       print 'key1:', key1
    elif fl == 2:
       key2 = f.read(_len_key)
       print 'key2:', key2
    else:
       key = f.read(_len_key)
       #print 'key:', key
  xBlock = ord(f.read(1))
  #print xBlock
  if xBlock == 0x1A: # block3
    _len = ord(f.read(1))
    if _len >= 0x80:
      _len &= ~0x80
      _len += (ord(f.read(1)) << 7)
    #print 'len:', _len
    block = f.read(_len)
    #print block
  if fl == 1:
     fn = ''
     for i in range(0,_len,2):
        fn += chr(int('0x'+block[i:i+2],16)^ord(key1[i/2%len(key1)]))
     print fn
  elif fl == 2:
     key_ = ''
     for i in range(len(key1)):
        key_ += chr(ord(key1[i])^ord(key2[i%len(key2)]))
  else:
     _out = ''
     for i in range(0, _len, 2):
        _out += chr(int('0x'+block[i:i+2],16)^ord(key_[i/2%len(key1)]))
     #print _out
     fo.write(_out)
  fl += 1

f.close()
fo.close() 

Код принимает в качестве параметра имя файла дампа с данными, причем последний символ этого имени используется для формирования имени выходного файла (у меня это были цифры).

В результате оказалось, что зверьком с машины французов были украдены файлы:
C:/Documents and Settings/Administrateur/Mes documents/0DayWindows.pdf
C:/Documents and Settings/Administrateur/Mes documents/ARFID.pdf
C:/Documents and Settings/Administrateur/Mes documents/Confidential.pdf
C:/Documents and Settings/Administrateur/Mes documents/DLLH.pdf

В файле с красноречивым именем ‘Confidential.pdf’ и оказался флаг.

Содержание архива:

Jupiter.pcap – файл из задания;
H5N1.exe – исходны файл сэмпла;
H5N1_unp.exe – распакованный файл сэмпла;
Jupiter.py – файл декодера;
X1…x4 – дампы данных

NDH_F300.RAR (2.94 MB)

Результирующие PDF не прикладываю, т.к. они могут быть получены заинтересованными в полпинка.

Я файлы извлекаю немного по другому.
Применяю фильтр - http.request.method == “GET” || http.response.code == 200
Дальше выбираю строку с ответом с статусом 200, внизу в дереве появляется пункт или Media Type(в большинстве случаев) или например JPEG File Interchange Format (для JPEG), ну вобщем последний пункт в дереве. Далее на нем ПКМ->Export Selected Packet Bytes… и сохраняем с именем которое идет выше этого пакета в пакете GET.

А тебе спасибо за то что просветил как дампить только нужные данные с пакета, а то всё время думал что нужно что-то использовать типа библиотеки для питона для парсиннга pcap-файлов.

в данном случае над TCP есть еще и HTTP и можно воспользоваться вообще File->Export Object->HTTP, описал общий подход, который подошел и для дампа данных.

Очень интересно, особенно момент с форензикой!