
Инструмент DNS-туннелирования, использующий PowerShell и Nslookup для эксфильтрации данных и доставки полезных нагрузок через DNS TXT/MX записи, обходящий Constrained Language Mode и защиту конечных точек.
Вдохновленный недавней работой, связанной с DNS-биконами Cobalt Strike, а также стремлением попытаться обойти Microsoft Defender for Endpoint, я потратил некоторое время на изучение того, как DNS можно использовать для передачи полезной нагрузки на целевой компьютер. Я также хотел усложнить задачу, попытавшись сделать это таким образом, чтобы это было возможно даже в режиме Constrained Language Mode PowerShell. Это исследование было нацелено на более современные версии Windows (т.е. Win10+, Server 2019+), но, как вы увидите позже, возможно, это применимо и к более старым версиям.
DNS-туннелирование — это техника, которая существует уже давно и используется различными злоумышленниками. На базовом уровне она предполагает использование протокола DNS для инфильтрации/экфильтрации данных или в качестве канала C2. Существует множество публикаций в блогах, которые можно использовать для получения дополнительной информации по этой теме.
Поскольку это старая и хорошо известная техника, многие организации внедрили методы обнаружения, чтобы попытаться ее предотвратить.
Исторически сложилось так, что типом DNS-записи, выбираемым для DNS-туннелирования, был TXT. Это связано с тем, что TXT-записи могут содержать больше данных, чем другие записи, а также они чувствительны к регистру, в отличие от других записей, что может повлиять на процесс кодирования.
Constrained Language Mode (CLM) — это ограничительный режим языка для PowerShell, который значительно сокращает возможности и разрешенную функциональность PowerShell. Если кратко, то .NET, COM-объекты и излюбленные атакующими методы, такие как (new-object net.webclient).downloadstring... недоступны. Эта ссылка содержит дополнительную информацию. Организации вводят эту политику для обычных пользователей в рамках правил уменьшения поверхности атаки. По сути, это просто усложняет нам жизнь как атакующим.
Большинство должно быть хотя бы поверхностно знакомо с 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.
Если бы кто-то запросил 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 сервера.

Это означает, что любые запросы к "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. Обратите внимание, что можно запрашивать определённые типы записей:

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

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

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

Nslookup.exe — основной элемент набора инструментов IT и очень известный двоичный файл, используемый в законных целях. Вероятность того, что ему будет разрешено выполняться даже в средах, где существует проблема белых списков приложений, высока.
Nslookup вернёт практически ту же информацию, что и наш запрос Resolve-DnsName; просто в нужный момент нам придётся обрабатывать её немного иначе.
## Превращение исполняемого файла в DNS-записи?
Итак, у нас есть средство для выполнения DNS-запросов на компьютере жертвы. Как мы можем предоставить нашу полезную нагрузку в формате, который сможет получить Nslookup?
Исполняемые файлы, конечно, являются двоичными, то есть не читаются человеком. Следовательно, данные нужно преобразовать в нечто, что можно поместить в DNS-записи и что сможет восстановить такой инструмент, как Nslookup. Доступно множество вариантов кодирования, но главное соображение — что может декодировать целевая машина, используя только штатные средства Windows и возможности, доступные в CLM? Base64 — очевидный и часто используемый ответ.