Обратный прокси-сервер C2 Cobalt Strike, который отражает атаки Blue Teams, антивирусов, EDR, сканеров с помощью проверки пакетов и корреляции гибких профилей.
(ранее известный как proxy2's плагин malleable_redirector)
Повысим планку устойчивости перенаправляющих серверов C2 к IR, не так ли?

В сфере Red Teaming было несколько разных отличных идей о том, как бороться с incident responders и вводить их в заблуждение, одновременно создавая устойчивую сеть перенаправляющих серверов C2.
Эта работа объединяет многие из этих отличных идей в одну легковесную утилиту, подражающую Apache2 в своей основе — простого HTTP(S) обратного прокси.
Сочетание понимания Malleable C2 профилей, знания пула плохих IP-адресов и гибкости легкого добавления новой логики проверки и ошибочной маршрутизации привело к созданию хитрого средства отпугивания IR-проверок.

Если любой недопустимый входящий пакет достигает RedWarden, вы можете redirect, reset или просто его убрать!
proxyЭта программа действует как HTTP/HTTPS обратный прокси с несколькими ограничениями, наложенными на входящие C2 HTTP запросы, выбирая, какие пакеты направлять на Teamserver, а какие отбрасывать, аналогично ограничениям файла .htaccess в Apache2 с mod_rewrite.
RedWarden был создан для решения проблемы обхода IR/AV/EDR/Sandboxes на уровне перенаправляющего сервера C2. Он призван заменить классические конфигурации Apache2 + mod_rewrite, используемые для этой цели.
Возможности:
RedWarden принимает на вход Malleable C2 профиль и hostname:port teamserver. Затем он анализирует предоставленные разделы malleable профиля для понимания контракта и пропускает только те входящие запросы, которые ему удовлетворяют, направляя остальные не туда.
Такие разделы, как http-stager, http-get, http-post и соответствующие им uri, заголовки, шаблоны prepend/append, User-Agent используются для различения легитимного запроса beacon-а и постороннего интернет-шума или IR/AV/EDR запросов.
Программа использует замечательные известные плохие диапазоны IP от: curi0usJack и другие: https://gist.github.com/curi0usJack/971385e8334e189d93a6cb4671238b10
Использование черного списка IP-адресов вместе с поиском известных плохих ключевых слов через обратные DNS-запросы Reverse-IP и проверку HTTP-заголовков повышает надежность и существенно увеличивает устойчивость перенаправляющего сервера к неавторизованным пирам, желающим изучить инфраструктуру атакующего.
Недействительные пакеты могут быть перенаправлены не туда в соответствии с тремя стратегиями:
Эта конфигурация задается в файле конфигурации:```yaml
drop_action: redirect
Ниже пример показывает результат `redirect` на `https://googole.com`:

Используйте с умом, оставайтесь в безопасности.
### Требования
Эта программа может работать только на Linux-системах, так как она использует fork для порождения множества процессов.
Также ожидается, что установлена системная команда `openssl`, так как она используется для генерации SSL-сертификатов.
Наконец, установите все зависимости Python3 PIP легко с помощью:```shell
bash $ sudo pip3 install -r requirements.txt
Минимальный конфигурационный файл RedWarden config.yaml может содержать:```yaml port:
profile: jquery-c2.3.14.profile
ssl_cacert: /etc/letsencrypt/live/attacker.com/fullchain.pem ssl_cakey: /etc/letsencrypt/live/attacker.com/privkey.pem
teamserver_url:
drop_action: reset
Затем программу можно запустить, указав путь к конфигурационному файлу:```shell
bash$ sudo python3 RedWarden.py -c config.yaml
[INFO] 19:21:42: Loading 1 plugin...
[INFO] 19:21:42: Plugin "malleable_redirector" has been installed.
[INFO] 19:21:42: Preparing SSL certificates and keys for https traffic interception...
[INFO] 19:21:42: Using provided CA key file: ca-cert/ca.key
[INFO] 19:21:42: Using provided CA certificate file: ca-cert/ca.crt
[INFO] 19:21:42: Using provided Certificate key: ca-cert/cert.key
[INFO] 19:21:42: Serving http proxy on: 0.0.0.0, port: 80...
[INFO] 19:21:42: Serving https proxy on: 0.0.0.0, port: 443...
[INFO] 19:21:42: [REQUEST] GET /jquery-3.3.1.min.js
[INFO] 19:21:42: == Valid malleable http-get request inbound.
[INFO] 19:21:42: Plugin redirected request from [code.jquery.com] to [1.2.3.4:8080]
[INFO] 19:21:42: [RESPONSE] HTTP 200 OK, length: 5543
[INFO] 19:21:45: [REQUEST] GET /jquery-3.3.1.min.js
[INFO] 19:21:45: == Valid malleable http-get request inbound.
[INFO] 19:21:45: Plugin redirected request from [code.jquery.com] to [1.2.3.4:8080]
[INFO] 19:21:45: [RESPONSE] HTTP 200 OK, length: 5543
[INFO] 19:21:46: [REQUEST] GET /
[...]
[ERROR] 19:24:46: [DROP, reason:1] inbound User-Agent differs from the one defined in C2 profile.
[...]
[INFO] 19:24:46: [RESPONSE] HTTP 301 Moved Permanently, length: 212
[INFO] 19:24:48: [REQUEST] GET /jquery-3.3.1.min.js
[INFO] 19:24:48: == Valid malleable http-get request inbound.
[INFO] 19:24:48: Plugin redirected request from [code.jquery.com] to [1.2.3.4:8080]
[...]
Приведенный выше вывод содержит строку, указывающую на то, что был неавторизованный, не соответствующий нашему профилю C2 входящий запрос, который был отклонен из-за несовместимой строки User-Agent:``` [...] [DROP, reason:1] inbound User-Agent differs from the one defined in C2 profile. [...]
## Варианты использования
### Настройка IP-геолокации для источников трафика вашего Beacon
Вы отлично справились с Pre-Phish и OSINT. Теперь вы знаете, где находятся ваши цели, и имеете некоторые подсказки о том, откуда должен исходить трафик, или, по крайней мере, как обнаружить полностью вспомогательный трафик.
Как наложить IP-геолокацию на запросы Beacon на редиректоре?
RedWarden приходит на помощь!
Допустим, вы хотите принимать трафик только из Польши, Европа.
Ваши результаты Pre-Phish/OSINT показывают, что:
- `89.64.64.150` — это легитимный IP-адрес одной из ваших целей, исходящий из Польши
- `59.99.140.76` — этот адрес не является легитимным и достиг ваших систем как обычный шумовой пакет из Интернета.
Вы можете использовать утилиту RedWarden `lib/ipLookupHelper.py` для сбора IP-гео-метаданных об этих двух адресах:```shell
bash$ python3 ipLookupHelper.py
Usage: ./ipLookupHelper.py <ipaddress> [malleable-redirector-config]
Use this small utility to collect IP Lookup details on your target IPv4 address and verify whether
your 'ip_geolocation_requirements' section of proxy2 malleable-redirector-config.yaml would match that
IP address. If second param is not given - no
Первое предоставляет:
(omitted)
``````shell
bash$ python3 ipLookupHelper.py 89.64.64.150
[dbg] Following IP Lookup providers will be used: ['ip_api_com', 'ipapi_co']
[.] Lookup of: 89.64.64.150
[dbg] Calling IP Lookup provider: ipapi_co
[dbg] Calling IP Lookup provider: ip_api_com
[dbg] New IP lookup entry cached: 89.64.64.150
[.] Output:
{
"organization": [
"UPC Polska Sp. z o.o.",
"UPC.pl",
"AS6830 Liberty Global B.V."
],
"continent": "Europe",
"continent_code": "EU",
"country": "Poland",
"country_code": "PL",
"ip": "89.64.64.150",
"city": "Warsaw",
"timezone": "Europe/Warsaw",
"fulldata": {
"status": "success",
"country": "Poland",
"countryCode": "PL",
"region": "14",
"regionName": "Mazovia",
"city": "Warsaw",
"zip": "00-202",
"lat": 52.2484,
"lon": 21.0026,
"timezone": "Europe/Warsaw",
"isp": "UPC.pl",
"org": "UPC Polska Sp. z o.o.",
"as": "AS6830 Liberty Global B.V.",
"query": "89.64.64.150"
},
"reverse_ip": "89-64-64-150.dynamic.chello.pl"
}
а последнее дает:```shell bash$ python3 ipLookupHelper.py 59.99.140.76 [dbg] Following IP Lookup providers will be used: ['ip_api_com', 'ipapi_co'] [dbg] Read 1 cached entries from file. [.] Lookup of: 59.99.140.76 [dbg] Calling IP Lookup provider: ip_api_com [dbg] New IP lookup entry cached: 59.99.140.76 [.] Output: { "organization": [ "", "BSNL Internet", "AS9829 National Internet Backbone" ], "continent": "Asia", "continent_code": "AS", "country": "India", "country_code": "IN", "ip": "59.99.140.76", "city": "Palakkad", "timezone": "Asia/Kolkata", "fulldata": { "status": "success", "country": "India", "countryCode": "IN", "region": "KL", "regionName": "Kerala", "city": "Palakkad", "zip": "678001", "lat": 10.7739, "lon": 76.6487, "timezone": "Asia/Kolkata", "isp": "BSNL Internet", "org": "", "as": "AS9829 National Internet Backbone", "query": "59.99.140.76" }, "reverse_ip": "" }
Теперь вы видите, что первый имел `"country": "Poland"`, а второй — `"country": "India"`. Обладая этим знанием, мы готовы сформулировать наши ограничения в виде объемного YAML-словаря:```yaml
ip_geolocation_requirements:
organization:
continent:
continent_code:
country:
- Poland
- PL
- Polska
country_code:
city:
timezone:
Каждая запись этого словаря принимает регулярное выражение для сопоставления с определенными IP-гео метаданными IP-адреса входящего пира.
Мы используем три записи в свойстве country, чтобы разрешить запросы, имеющие одно из указанных значений.
Установив это в своей конфигурации, вы можете проверить, будет ли другой IP-адрес пропущен через дискриминатор IP-геолокации RedWarden или нет, с помощью утилиты ipLookupHelper, принимающей второй параметр:

Самая последняя строка сообщает, будет ли пакет заблокирован или принят.
И это все! Настраивайте ограничения IP-геолокации разумно и безопасно, внимательно проверяйте логи RedWarden на наличие записей DROP, связанных с IP-гео, и поддерживайте свой C2-трафик в чистоте и порядке!
Если вы используете промежуточные системы, такие как AWS Lambda или CloudFlare, в качестве ваших Domain Fronting/редиректоров, вы наверняка сталкивались с ситуацией, когда некоторые из ваших пакетов не могли быть приняты Teamserver, поскольку они отклонялись от согласованного malleable контракта. Был ли это поврежденный или удаленный HTTP-заголовок, переупорядоченные куки или что-то еще — уверен, это отняло у вас много часов жизни.
Для борьбы с проблемами настройки каналов C2 и повреждениями в промежуточных системах RedWarden предлагает функциональность восстановления пакетов Beacon.
Это делается путем проверки того, каким Malleable Profile ожидает пакет, и может восстанавливать настроенные HTTP-заголовки до их согласованных значений в соответствии с требованиями профиля.
Рассмотрим следующий простой профиль:``` http-get { set uri "/api/abc"; client {
header "Accept-Encoding" "gzip, deflate";
metadata {
base64url;
netbios;
base64url;
parameter "auth";
}
}
...
Видите этот `Accept-Encoding`? Каждый запрос Beacon должен содержать этот заголовок и это значение. Что произойдет, если ваш Beacon попадет на системы CloudFlare, и они отправят запрос, из которого будет удален этот заголовок или он будет иметь `Accept-Encoding: gzip` вместо этого? Teamserver отбросит запрос на месте. Установив этот заголовок в разделе конфигурации RedWarden под названием `repair_these_headers`, вы сможете сохранить ваше соединение.:```yaml
#
# This option repairs Beacon requests's header value by restoring to what was expected in Malleable C2 profile.
#
# If RedWarden validates inbound request's HTTP headers, according to policy drop_malleable_without_expected_header_value:
# "[IP: DROP, reason:6] HTTP request did not contain expected header value:"
#
# and detects some header is missing or was overwritten along the wire, the request will be dropped.
#
# We can relax this policy a bit however, since there are situations in which Cache systems (such as Cloudflare) could tamper with our
# requests thus breaking Malleable contracts. What we can do is to specify list of headers, that should be overwritten back to their values
# defined in provided Malleable profile.
#
# So for example, if our profile expects:
# header "Accept-Encoding" "gzip, deflate";
#
# but we receive a request having following header set instead:
# Accept-Encoding: gzip
#
# Because it was tampered along the wire by some of the interim systems (such as web-proxies or caches), we can
# detect that and set that header's value back to what was expected in Malleable profile.
#
# In order to protect Accept-Encoding header, as an example, the following configuration could be used:
# repair_these_headers:
# - Accept-Encoding
#
# Default: <empty-list>
#
repair_these_headers:
- Accept-Encoding
Начиная с Cobalt Strike 4.7+ я заметил, что Teamserver автоматически удаляет заголовок Content-Encoding без уведомления, нарушая тем самым наш контракт malleable http-(get|post).server.
Поскольку RedWarden следовал контракту, Beacon либо отбрасывал ответы, либо декомпрессировал их неправильно.
Эта опция определяет, какие заголовки из ответов Teamserver должны быть удалены до того, как они достигнут процесса Beacon:```yaml
remove_these_response_headers:
Теперь RedWarden по умолчанию будет удалять заголовок `Content-Encoding` из ответов Teamserver, чтобы сохранить совместимость с версиями CS4.7+.
### Примеры вывода
Давайте посмотрим на вывод, который генерирует прокси.
При опции `verbose: True` подробность будет установлена на INFO, которая в лучшем случае сообщает о принятых запросах в отличие от отклонённых.
Запрос может быть принят, если он соответствует всем критериям, настроенным в конфигурационном файле RedWarden. Такая ситуация будет сопровождаться записью в журнале `[ALLOW, ...]`:```
[INFO] 2021-04-24/17:30:48: [REQUEST] GET /js/scripts.js
[INFO] 2021-04-24/17:30:48: == Valid malleable http-get (variant: default) request inbound.
[INFO] 2021-04-24/17:30:48: [ALLOW, 2021-04-24/19:30:48, 111.222.223.224] "/js/scripts.js" - UA: "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko"
[INFO] 2021-04-24/17:30:48: Connected peer sent 2 valid http-get and 0 valid http-post requests so far, out of 15/5 required to consider him temporarily trusted
[INFO] 2021-04-24/17:30:48: Plugin redirected request from [attacker.com] to [127.0.0.1:5555]
Если запрос не пройдет какую-либо из проверок, которые RedWarden проводит для каждого запроса, соответствующая строка [DROP, ...] будет выведена с информацией о причине сброса.:```
[INFO] 2021-04-24/16:48:28: [REQUEST] GET /
[ERROR] 2021-04-24/16:48:29: [DROP, 2021-04-24/18:48:28, reason:1, 128.14.211.186] inbound User-Agent differs from the one defined in C2 profile.
[INFO] 2021-04-24/16:48:29: [DROP, 2021-04-24/18:48:28, 128.14.211.186] "/" - UA: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36"
[ERROR] 2021-04-24/16:48:29: [REDIRECTING invalid request from 128.14.211.186 (zl-dal-us-gp3-wk107.internet-census.org)] GET /
### Тонкая настройка политик сброса
Есть множество причин, определяющих, может ли запрос быть отклонён. Каждая из этих проверок может быть независимо включена или отключена в соответствии с требованиями или в процессе тонкой настройки или исправления ошибочных решений:
Выдержка из `example-config.yaml`:```yaml
#
# Fine-grained requests dropping policy - lets you decide which checks
# you want to have enforced and which to skip by setting them to False
#
# Default: all checks enabled
#
policy:
# [IP: ALLOW, reason:0] Request conforms ProxyPass entry (url="..." host="..."). Passing request to specified host
allow_proxy_pass: True
# [IP: ALLOW, reason:2] Peer's IP was added dynamically to a whitelist based on a number of allowed requests
allow_dynamic_peer_whitelisting: True
# [IP: DROP, reason:1] inbound User-Agent differs from the one defined in C2 profile.
drop_invalid_useragent: True
# [IP: DROP, reason:2] HTTP header name contained banned word
drop_http_banned_header_names: True
# [IP: DROP, reason:3] HTTP header value contained banned word:
drop_http_banned_header_value: True
# [IP: DROP, reason:4b] peer's reverse-IP lookup contained banned word
drop_dangerous_ip_reverse_lookup: True
# [IP: DROP, reason:4e] Peer's IP geolocation metadata contained banned keyword! Peer banned in generic fashion.
drop_ipgeo_metadata_containing_banned_keywords: True
# [IP: DROP, reason:5] HTTP request did not contain expected header
drop_malleable_without_expected_header: True
# [IP: DROP, reason:6] HTTP request did not contain expected header value:
drop_malleable_without_expected_header_value: True
# [IP: DROP, reason:7] HTTP request did not contain expected (metadata|id|output) section header:
drop_malleable_without_expected_request_section: True
# [IP: DROP, reason:8] HTTP request was expected to contain (metadata|id|output) section with parameter in URI:
drop_malleable_without_request_section_in_uri: True
# [IP: DROP, reason:9] Did not found append pattern:
drop_malleable_without_prepend_pattern: True
# [IP: DROP, reason:10] Did not found append pattern:
drop_malleable_without_apppend_pattern: True
# [IP: DROP, reason:11] Requested URI does not aligns any of Malleable defined variants:
drop_malleable_unknown_uris: True
# [IP: DROP, reason:12] HTTP request was expected to contain <> section with URI-append containing prepend/append fragments
drop_malleable_with_invalid_uri_append: True
По умолчанию все эти проверки включены.
Включение debug: True затопит буфер консоли множеством строк журнала, описывающих каждый шаг, который RedWarden выполняет в своем сложном процессе принятия решений.
Если вы хотите видеть полные тела ваших запросов и ответов - установите debug и trace в true и погрузитесь в пучину журналирования!
- Может ли эта программа работать без Malleable Profile?
Да, может. Однако логика проверки запросов будет отключена, остальное должно работать нормально: определение геолокации по IP, логика обратного поиска, список заблокированных IP и т.д.
- Можно ли легко адаптировать эту программу для других C2-фреймворков? Таких как Mythic, Covenant и т.д.?
Легко — нет. С некоторыми усилиями — да. Как я описал ниже, инструмент написан плохо, что сделает адаптацию для других C2 болезненной. Однако это вполне реализуемо при наличии времени и усилий.
- Мои пакеты отбрасываются. Почему?
Попробуйте включить debug: True и trace: True, чтобы собрать как можно больше журналов. Затем вам нужно будет просмотреть журналы и выяснить, что происходит. Выглядят ли пакеты именно так, как вы ожидали в вашем Malleable профиле? Или, возможно, произошло незаметное вмешательство по сети, из-за которого RedWarden отбросил пакет (и это могло бы заставить Teamserver также его отбросить?)
example-config.yaml и работать с ним.Код — ОДИН ХРЕНОВ БОЛЬШОЙ АДСКИЙ БЕСПОРЯДОК — я признаю это — и для этого есть честная причина: проект разрабатывался на 90% во время реальных Red Team-задач. Как мы все знаем, такие задачи включают в себя множество дел, оставляя почти нет времени на разработку полноценного сложного инструмента. Не говоря уже о критичности этой программы в настройке проекта. Инструмент изначально начинался как простой прокси-скрипт на Python2, затем эволюционировал в прокси с плагинами, получил плагин malleable_redirector — и с тех пор я изо всех сил старался поддерживать обратную совместимость proxy2 (бедный я, я был как Microsoft!) с другими плагинами, которые я для него создал, и придерживаться его первоначального назначения.
Однако пришло время отпустить его, переименовать и начать исправлять все внесенные плохие запахи кода.
С учетом всего сказанного, пожалуйста, проявите некоторую долю сострадания ко мне при открытии вопросов, отправке pull request'ов и старайтесь помочь, а не осуждать! :-) Спасибо!
Этот и другие проекты — результат бессонных ночей и большой тяжелой работы. Если вам нравится то, что я делаю, и вы цените, что я всегда отдаю сообществу, Рассмотрите возможность купить мне кофе (или лучше пиво), чтобы просто сказать спасибо! 💪
Mariusz Banach / mgeeky, '19-'21 <mb [at] binary-offensive.com> (https://github.com/mgeeky)