Order Of Six Angles

Main Logo

Х.Р.А.М Сатаны

Home | RU | Translations | Art | Feed.xml

1 July 2024

tags: note - malware-analysis - windows

Советы для исследования .NET малвари

У нас есть малварь, которая декодирует ресурс DE.

Alt text

С помощью скрипта stego можно расшифровать картинку.

Alt text

Результат декодирования:

Alt text

Также этот скрипт способен производить обратную операцию - превращать файл в стеганографическое изображение. Может использоваться в сценарии, когда вы пропатчили вредоносный файл, и хотите обратно закодировать его в картинку, чтобы малварь в процессе своей работы расшифровала ваш пропатченный бинарник. Не забудьте указать ширину и высоту картинки, как в оригинале.

Alt text

Дамп пейлодов

  1. Часто .net малварь выполняет несколько стадий распаковки, задействуя несколько пейлодов. Чтобы просмотреть вредоносные пейлоды в памяти можно воспользоваться инстурментом ProcessHacker:

  1. Дампнуть отдельные .NET Assembly из памяти можно тулзой ExtremeDumper. hollows_hunter может не видеть все пейлоды в памяти.

  2. Также можно сделать дамп, поставив брейкпоинт на загрузку модулей в dnSpy. Способ показан в видео. Нам надо отобразить окно Модули.

Затем поставить “*” в имена отслеживаемых модулей.

Запускаем малварь и мы увидим все загружаемые модули.

Вызов отдельных методов

Способ первый

Малварь может содержать функции по расшифровки/декодированию строк или другие интересные функции, которые хочется вызвать отдельно (только их). Например, малварь содержит функцию по декодированию строки CausalitySource

Аргументы, которая она принимает:

Существует два удобных способа вызвать эту функцию с нужными аргументами. Первый - с помощью LinqPad

Linqpad - это легковесный исполнитель dotnet кода, которым удобно пользоваться в виртуалке. Можно скачивать 5 версию, так как .NET framework по умолчанию стоит в Windows 7. Теперь мы можем просто скопировать из образца код, вставить его в редактор и исполнить.

Необязательно писать полноценную программу с Main() и т.д., можно использовать просто куски кода, для этого в выпадающем меню выбираем Expression или Statement.

Например, исполнение отдельных строк кода:

Способ второй

Часто образец малвари в своих методах использует собственные константы, другие методы и т.д. и переносить кучу кода/переменные в Linqpad нерелевантно. Поэтому можно воспользоваться втором способом - powershell. Ничего устанавливать не нужно, powershell по умолчанию есть в Windows 7. Запускаем нужный экземпляр powershell, чтобы его разрядность совпадала с разрядностью образца. Первым делом нам необходимо подгрузить наш вредоносный Assembly командой:

$malware = [Reflection.Assembly]::LoadFile("path")

Также это можно сделать с помощью cmdlet (более современный способ). В этом случае файл должен иметь расширение .dll:

Add-Type -Path foo.dll

Убедиться что операция завершена успешна поможет команда просмотра списка всех загруженных Assembly:

[appdomain]::currentdomain.GetAssemblies()

Чтобы вызвать конкретный, статический метод мы используем следующую конструкцию:

[имя_namespace.имя_класса]::имя_функции("аргументы")

Например:

Если метод НЕ статический, то в начале инстанцируем класс:

$instance = New-Object имя_namespace.имя_класса
$result = $instance.имя_функции()
echo $result

Пример вызова нестатического метода:

Полезные powershell команды

*ассембли (Assembly) - любой .NET EXE или DLL

Стандартный размер шрифта powershell очень мал. Увеличить его можно кликнув правой кнопкой на иконку - Свойства.

Очистка окна консоли:

Clear-Host

Если для каких-либо операций нам необходимо подгрузить зависимость:

[System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

Например, подгрузив зависимость Drawing мы можем использовать класс Bitmap:

$bmp = New-Object System.Drawing.Bitmap("D:\\path")

Вывод всех типов внутри бинарника:

$malware.gettypes()

Поиск по выводу какой-либо команды:

| Select-String -Pattern "строка_для_поиска"

Вывод только публичных классов:

$assembly.GetTypes() | ? {$_.IsClass -and $_.IsPublic}

Вывод всех текущих загруженных Assembly в отсортированном виде (также эта команда может пригодится или нет при форензике):

[appdomain]::currentdomain.getassemblies() | sort -property fullname | format-table fullname

Дамп захардкоженных переменных ассембли. Допустим у нас есть малварь, которая содержит некий статический байтовый блоб.

Если вы хотите дампнуть его, это можно сделать двумя способами. Динамически - запустить ассембли в дебаггере и дампнуть значение в рантайме. Статически - дампнуть, используя powershell. Чтобы дампнуть используя powershell мы подгружаем ассембли и сохраняем в файл желаемую переменную.

$malware = [Reflection.Assembly]::LoadFrom("path") # подгружаем наш вредоносный ассембли
[io.file]::WriteAllBytes('path_to_save_var', [namespace.class]::var_name) # сохраняем в файл

Пример:

Если требуемая переменная НЕстатическая, то просто создайте инстанс соответствующего класса.

Если вам лень постоянно вводить команды для дампа, то можно написать скрипт. Чтобы скрипт работал, не забудьте включить исполнение скриптов на системе:

set-executionpolicy remotesigned

Лайфхаки

  1. AgentTesla содержит пейлоды в виде ресурсов. Один из пейлодов является стеганографической картинкой. Если мы хотим пропатчить пейлод, который распаковывается на какой-либо стадии, нам необходимо:
    1. Преобразовать ресурс из картинки в файл
    2. Пропатчить
    3. Преобразовать файл обратно в картинку
    4. Заменить ресурс картинку В этом может помочь скрипт stego.
  2. Например, мы хотим написать YARA правило на кусок .NET кода.

*вспомогательная ссылка

*список опкодов

  1. LinqPad поддерживает работу с файлами

  1. Иногда, .NET малварь может содержать GUID. Это поле идентифицрует конкретный проект на компе разработчика. Соответственно, если у нескольких отдельных образцов один и тот же GUID, то с уверенностью можно говорить, что они были разработаны на одном компе и скорей всего одним и тем же разработчиком.

  1. Если нам попался .NET Bundle, то дампнуть файлы бандла можно с помощью AsmResolver и powershell:
[Reflection.Assembly]::LoadFrom("C:\AsmResolver\AsmResolver.DotNet.dll") | Out-Null
$extractionPath = "C:\Extracted\"
$manifest = [AsmResolver.DotNet.Bundles.BundleManifest]::FromFile("C:\RiotClientServices.exe")

foreach($file in $manifest.Files)
{
    $fileInfo = [IO.FileInfo]::new($extractionPath + $file.RelativePath)
    $fileInfo.Directory.Create()
    [IO.File]::WriteAllBytes($fileInfo.FullName, $file.getData($true))
}

Более подробная информация

  1. Библиотека AsmResolver, с помощью которой можно модифицировать .net бинарники. Например, удалять ненужные методы:
[Reflection.Assembly]::LoadFrom("C:\AsmResolver\AsmResolver.DotNet.dll") | Out-Null
$obfuscated = "C:\RiotClientServices.dll"
$moduleDef = [AsmResolver.DotNet.ModuleDefinition]::FromFile($obfuscated)
# Removing junk methods
foreach($type in $moduleDef.GetAllTypes())
{
    foreach($method in [array]$type.Methods.Where{$_.HasMethodBody})
    {
        if(($method.MethodBody.Instructions.Where{$_.Opcode.Mnemonic -like "call" -and
            $_.Operand.FullName -like "*System.Console::WriteLine*"}).count -eq  5)
        {
            $type.Methods.Remove($method) | Out-Null
        }
    }
}
  1. Если надо отдебажить библиотеку .NET, то можно воспользоваться SharpDllLoader

  2. Образцы можно группировать по TypeRef hash. Вычислить его поможет скрипт в гитлаб. TypeRef hash лучше использовать отсортированный и включая сущности, которые ссылаются сами на себя (sorted, include self-referenced entries).

  3. Если функция расшифровки строк малвари вызывается много раз, можно написать следующий скрипт (пример):

$malware = [Reflection.Assembly]::LoadFile("C:\my\f.dll")
[jKcEiEe]::TFkR(980214767)| Out-File -FilePath c:\my\output.txt
[jKcEiEe]::TFkR(980214689) | Add-Content C:\my\output.txt
[jKcEiEe]::TFkR(980214350) | Add-Content C:\my\output.txt
[jKcEiEe]::TFkR(980214730) | Add-Content C:\my\output.txt
[jKcEiEe]::TFkR(980214391) | Add-Content C:\my\output.txt
[jKcEiEe]::TFkR(980214713) | Add-Content C:\my\output.txt
[jKcEiEe]::TFkR(980214747) | Add-Content C:\my\output.txt
[jKcEiEe]::TFkR(980214670) | Add-Content C:\my\output.txt
[jKcEiEe]::TFkR(980214364) | Add-Content C:\my\output.txt
[jKcEiEe]::TFkR(980214543) | Add-Content C:\my\output.txt 
  1. Иногда малварь для сокрытия пейлода использует Fody/Costura. Costura предназначена для добавления зависимостей/ресурсов в единный бинарник. Полезные ссылки по таким кейсам:

example-analysis-of-multi-component-malware

play-ransomware-volume-shadow-copy

  1. Получение данных о X509 сертификате кодом в LINQPAD. Также можно проверить валидность файла:

void Main()
{
	string fileName = "c:\\my\\OUTLOOK.CFG";
	string text2 = "test-908@testgmail-398504.iam.gserviceaccount.com";
	X509Certificate2 x509 = new X509Certificate2(fileName, "notasecret", X509KeyStorageFlags.Exportable);
	
	Console.WriteLine("{0}Subject: {1}{0}", Environment.NewLine, x509.Subject);
    Console.WriteLine("{0}Issuer: {1}{0}", Environment.NewLine, x509.Issuer);
    Console.WriteLine("{0}Version: {1}{0}", Environment.NewLine, x509.Version);
    Console.WriteLine("{0}Valid Date: {1}{0}", Environment.NewLine, x509.NotBefore);
    Console.WriteLine("{0}Expiry Date: {1}{0}", Environment.NewLine, x509.NotAfter);
    Console.WriteLine("{0}Thumbprint: {1}{0}", Environment.NewLine, x509.Thumbprint);
    Console.WriteLine("{0}Serial Number: {1}{0}", Environment.NewLine, x509.SerialNumber);
    Console.WriteLine("{0}Friendly Name: {1}{0}", Environment.NewLine, x509.PublicKey.Oid.FriendlyName);
    Console.WriteLine("{0}Public Key Format: {1}{0}", Environment.NewLine, x509.PublicKey.EncodedKeyValue.Format(true));
    Console.WriteLine("{0}Raw Data Length: {1}{0}", Environment.NewLine, x509.RawData.Length);
    Console.WriteLine("{0}Certificate to string: {1}{0}", Environment.NewLine, x509.ToString(true));
    Console.WriteLine("{0}Certificate to XML String: {1}{0}", Environment.NewLine, x509.PublicKey.Key.ToXmlString(false));
}

// Define other methods and classes here

Быстрая разработка на .NET

Иногда необходимо по-быстрому полностью реализовать какой-нибудь алгоритм/написать код. Например, повторить кусок кода из малвари, который работает с файлами, открывает их, сохраняет и т.д..

  1. Скачиваем VSCode
  2. Устанавливаем два расширения: C# Dev Kit и C#
  3. Скачиваем dotnet sdk
  4. Чтобы убедиться, что dotnet установлен и работает корректно в командной строке выполняем команду dotnet
  5. Открываем VSCode через Developer Command Prompt командой code

  1. Создаем новый проект типа Console командой:
dotnet new console
  1. Если не хватает какой-либо зависимости:
dotnet add package System.Drawing.Common --version 7.0.0
  1. Собрать проект
dotnet clear
dotnet build
  1. Чтобы получить бинарник пригодный для запуска на другом компе (не забудьте указать нужную архитектуру):
dotnet publish -r win7-x64 

Ссылки по этой теме:

https://learn.microsoft.com/en-us/dotnet/core/rid-catalog

https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-build

https://learn.microsoft.com/en-us/dotnet/core/deploying/

Publishing your app as self-contained produces an application that includes the .NET runtime and libraries, and your application and its dependencies. Users of the application can run it on a machine that doesn’t have the .NET runtime installed.

  1. Чтобы бинарник работал на другой компе или в виртуалке, надо скопировать ВСЮ папку bin\x64\Debug\net7.0\win7-x64\publish
Вверх