РАЗБОР
Reverse
На проверке
Я не взломал их C2. Я заставил бота сдать его сам | РАЗБОР ТОГО КАК Я РЕВЕРСНУЛ КАСТОМНЫЙ ПРОТОКОЛ С2 ГРУППИРОВКИ 2137GANG
[ обложка статьи ]
Режим чтения
Я не взломал их C2. Я заставил бота сдать его сам
## Как вскрыть командный канал майнера, который убивает твою отладку, — и почему ключ для этого не нужен
*Автор: SKYNET · Telegram @SIMCARDER22 · *
---
## Почему это вообще интересно
Обычная малварь боится отладчика. Эта — не боится, потому что убивает его. Не «детектит gdb и выходит», а просто выпиливает соседние процессы в своём namespace, попутно снося с машины другой майнер-конкурент. Поймать её «в лоб» не выходит. А значит, чтобы понять, что она отправляет наверх, приходится придумывать что-то умнее стандартного набора:
Дальше — весь путь: от ловушки до расшифрованного канала. С кодом, граблями и выводами.
---
## 1. Как всё началось
У меня стоит свой honeypot: Cowrie на 22-м порту плюс веб-панель, которая выглядит как простаивающая крипто-нода (валидатор Solana — лакомая цель). Мимо идёт настоящий мировой брутфорс, и иногда в него попадается что-то крупное.
Из одного заражения выпал комплект:
Семейство опозналось по строкам:
Железное правило сразу: всё дальше — только на изолированном стенде. Никаких обращений к чужим серверам. Строго пассивный перехват того, что бот отправляет сам.
---
## 2. Что это за зверь
| Параметр | Значение |
|---|---|
| Семейство | RedTail /
| Тип | Linux-криптомайнер Monero + самораспространяющийся червь |
| Майнер | XMRig 6.26.0 (GCC 15.1.0) |
| Сборка | статическая линковка, stripped, UPX, не PIE (база
| Архитектуры | x86_64, i686, aarch64, arm7, riscv |
| ~95% кода | открытый: BoringSSL, libssh2 1.11.1, hwloc 2.14.0, snappy 1.2.2, libuv 1.52.1, abseil |
| Кастом |
Маскируется под что угодно живое:
Персистенс — сервис
«Не PIE» — это ключевая деталь. Адреса в файле совпадают с адресами в памяти. Запомни, это нам пригодится.
---
## 3. Инфраструктура
| IP | порт | роль | хостинг | ASN |
|---|---|---|---|---|
|
|
|
|
|
Корреляция, за которую зацепился взгляд: C2 (
---
## 4. Протокол
Заголовок кадра — ровно 32 байта:
Порты C2 на TLS-клиенте отвечают
Крипто-стек вокруг:
---
## 5. Фокус с DoT: резолвер, который мне доверился
Бот резолвит свои C2 через DNS-over-TLS на порт 853, ротируя публичные резолверы (8.8.8.8, 1.1.1.1 и т.п.) — выглядит как обычный трафик. Скучно? Нет.
Сертификат TLS он не проверяет.
Я подсунул ему свой DoT-сервер с самоподписанным сертификатом — и получил полный MITM: бот спокойно отдавал мне имена, которые резолвит. Так родился полный список C2-доменов — 11 штук, из которых три оказались не в публичных отчётах:
---
## 6. Тупик: он убивает отладку
Дальше — самое интересное, ради чего стоит читать.
В бинарнике есть ChaCha20 (классический CRYPTOGAMS). Я нашёл единую C-обёртку, через которую проходят все SIMD-варианты:
Кажется, всё просто: ставим брейкпоинт, читаем ключ из
Она убивает всё в своём PID-пространстве. При старте сносит конкурентов (включая другой майнер —
Как я ни подходил:
| Подход | Результат |
|---|---|
|
|
| gdb как PID 1 контейнера | выжил, но бот форкается и re-exec'ит себя — уходишь не в тот процесс |
| seccomp-запрет
| eBPF/uprobe | ядро не даёт как надо в этом контейнере |
И вишенка: ключ ChaCha20 генерируется в рантайме из сильной энтропии и затирается. Классический перебор (LCG, Mersenne Twister, RC4 со словом «redtail») — ноль, шум.
Классический тупик. Пока не переформулируешь задачу.
---
## 7. Разворот: зачем мне ключ, если нужен текст?
Смотрите. Ключ нужен, чтобы расшифровать то, что бот отправляет. А что, если снять запись до шифрования?
Ищу точку, куда запись приходит ещё открытой. По декомпиляции вижу сигнатуру:
Отладчика у нас нет (см. выше). Отладчик нам не нужен. Патчим бинарь.
### Патч, который бот не убьёт
Логика в три хода:
1. Не PIE → адрес в файле = адрес в памяти.
2. В
3. Пролог целевой функции переписываем на
Находим щель (у меня — между
И точка входа превращается в:
Грабли, стоившие полдня: первая версия стаба давала мусор. Причина — в одну строку:
Почему это работает: бот умеет убивать процессы. Отладчик — процесс. А патч — это байты внутри самого бота. Убивать нечего. Ты против него не «снаружи с отладчиком», ты уже внутри его кода.
---
## 8. Что оказалось внутри
Прогон дал 25 МБ плейнтекста (991 сессия). Бот сам, «добровольно», складывал содержимое канала.
Конфиг майнера — слово в слово:
Команды распространения (то, чем червь «закрепляется» и чистит место):
~40 путей RCE — это и есть карта его атаки:
Телеметрия бэкона: модель CPU, cgroups,
---
## 9. Про кошелёк: почему его нет и не будет
Самый частый вопрос — «а кошелёк-то где?». Отвечаю окончательно: его у бота нет.
В конфиге майнера — только
Проверил всё: все пять архитектур, дампы памяти живого процесса, весь перехваченный трафик XOR-брутфорсом и не только. Ноль адресов Monero. Это не «мыло не докрутили» — это бай-дизайн. Приватный пул с IP-based auth: сеть майнеров одна, а кошелёк один и живёт только у оператора.
Вывод практический: видишь
---
## 10. Что забрать с собой (выводы)
1. «Не могу снять ключ» ≠ «не могу прочитать трафик». Часто достаточно взять плейнтекст на входе шифрования — на один шаг раньше.
2. Против малвари, убивающей отладчики, работает не отладчик, а патч. Ты входишь внутрь её же кода.
3. Не PIE + nop-щели в
4. MITM на «доверчивом» транспорте бесплатен, если он не проверяет сертификат. DoT «под Chrome» — не значит безопасный.
5. Приватный пул = кошелёк недостижим по определению. Не гоняйся.
И главное: в борьбе с малварью побеждает не самый громкий инструмент, а тот, кто раньше переформулировал задачу. Я не сломал их C2 — я дал боту возможность сдать его сам.
---
## 11. Честно о границах
---
Разбираю малварь, которая считает себя умнее аналитика. Пишите, если есть что разобрать вместе.
Telegram: @SIMCARDER22 · SKYNET
---
## Как вскрыть командный канал майнера, который убивает твою отладку, — и почему ключ для этого не нужен
*Автор: SKYNET · Telegram @SIMCARDER22 · *
---
## Почему это вообще интересно
Обычная малварь боится отладчика. Эта — не боится, потому что убивает его. Не «детектит gdb и выходит», а просто выпиливает соседние процессы в своём namespace, попутно снося с машины другой майнер-конкурент. Поймать её «в лоб» не выходит. А значит, чтобы понять, что она отправляет наверх, приходится придумывать что-то умнее стандартного набора:
gdb, strace, DynamoRIO, Frida — мимо.Дальше — весь путь: от ловушки до расшифрованного канала. С кодом, граблями и выводами.
---
## 1. Как всё началось
У меня стоит свой honeypot: Cowrie на 22-м порту плюс веб-панель, которая выглядит как простаивающая крипто-нода (валидатор Solana — лакомая цель). Мимо идёт настоящий мировой брутфорс, и иногда в него попадается что-то крупное.
Из одного заражения выпал комплект:
Код:
loader.bin (bash-загрузчик)
x86_64 i686 aarch64 arm7 riscv (ELF, UPX)
Семейство опозналось по строкам:
libredtail, путь сборки /var/build/redtail/scripts/, systemd-юнит your-redtail.service. Внутри — XMRig 6.26.0.Железное правило сразу: всё дальше — только на изолированном стенде. Никаких обращений к чужим серверам. Строго пассивный перехват того, что бот отправляет сам.
---
## 2. Что это за зверь
| Параметр | Значение |
|---|---|
| Семейство | RedTail /
libredtail (внутреннее имя) || Тип | Linux-криптомайнер Monero + самораспространяющийся червь |
| Майнер | XMRig 6.26.0 (GCC 15.1.0) |
| Сборка | статическая линковка, stripped, UPX, не PIE (база
0x400000) || Архитектуры | x86_64, i686, aarch64, arm7, riscv |
| ~95% кода | открытый: BoringSSL, libssh2 1.11.1, hwloc 2.14.0, snappy 1.2.2, libuv 1.52.1, abseil |
| Кастом |
libredtail (форк libevent) + протокол dcb + червь |Маскируется под что угодно живое:
Код:
php-fpm: pool www
nginx: worker process
postgres: user dbname 127.0.0.1 idle
/bin/mariadbd
systemd-worker /bin/systemtd /bin/-bash /usr/bin/.sh
Персистенс — сервис
your-redtail.service + @reboot в crontab (при этом он затирает чужие crontab: crontab -r; echo "@reboot {}" | crontab -).«Не PIE» — это ключевая деталь. Адреса в файле совпадают с адресами в памяти. Запомни, это нам пригодится.
---
## 3. Инфраструктура
| IP | порт | роль | хостинг | ASN |
|---|---|---|---|---|
|
130.12.180.51 | 43782 | C2, канал dcb | Omegatech/Virtualine | AS202412 ||
130.12.180.51 | 2137 | майнинг-прокси (стратум) | Omegatech/Virtualine | AS202412 ||
31.56.209.165 | 2137 | C2 / прокси | SWISSNET LLC | AS209373 ||
217.60.103.56 | 80/443 | сервер раздачи | SWISSNET LLC | AS209373 ||
95.215.19.53 | 853 | C2 под DNS-over-TLS | Materialism s.r.l. | AS39287 |Корреляция, за которую зацепился взгляд: C2 (
31.56.209.165) и сервер раздачи (217.60.103.56) сидят на одном хостинге (SWISSNET, AS209373). Управление и раздача — одна инфраструктура, один оператор.---
## 4. Протокол
dcb (не TLS!)Заголовок кадра — ровно 32 байта:
Код:
40 68 0d 1b magic (0x1B0D6840)
05 00 00 00 версия = 5
64 63 62 00 tag "dcb"
00 00 00 00 флаги
00 00 05 18 длина payload (big-endian) = размер файла − 32
.. .. .. .. nonce[12] (в перехваченных — нули)
Порты C2 на TLS-клиенте отвечают
WRONG_VERSION_NUMBER — значит это свой бинарный протокол, а не TLS. Красиво: аналитик, который машинально лезет TLS-сканером, видит «мусор» и идёт мимо.Крипто-стек вокруг:
- X25519 (ECDH) — обмен со статическим публичным ключом сервера;
- ChaCha20-Poly1305 (AEAD) — защита записей;
- транспорт —
libredtail(форк libevent:evbuffer+ TLS) поверхlibuv; - метки ключей (вытащил из дампа):
server-key,client-key,server-nonce,client-nonce.
---
## 5. Фокус с DoT: резолвер, который мне доверился
Бот резолвит свои C2 через DNS-over-TLS на порт 853, ротируя публичные резолверы (8.8.8.8, 1.1.1.1 и т.п.) — выглядит как обычный трафик. Скучно? Нет.
Сертификат TLS он не проверяет.
Я подсунул ему свой DoT-сервер с самоподписанным сертификатом — и получил полный MITM: бот спокойно отдавал мне имена, которые резолвит. Так родился полный список C2-доменов — 11 штук, из которых три оказались не в публичных отчётах:
Код:
proxy.identities.network proxy.insanitycpp.cx proxy.insanecppdev.com
p.2137gang.{st, pl, net, shop, store} ← ротация TLD
proxies.identities.network proxies.insanitycpp.cx proxies.insanecppdev.com
↑ эти три — оператор держит отдельно
proxy.* — командный канал; proxies.* — точка входа майнинг-прокси. Одна и та же база, разные роли. Такое разделение в отчётах не описано.---
## 6. Тупик: он убивает отладку
Дальше — самое интересное, ради чего стоит читать.
В бинарнике есть ChaCha20 (классический CRYPTOGAMS). Я нашёл единую C-обёртку, через которую проходят все SIMD-варианты:
Код:
// 0x614ce0
chacha20_ctr32( out, in, len, key /* rcx */, nonce /* r8 */ )
Кажется, всё просто: ставим брейкпоинт, читаем ключ из
rcx. И вот тут малварь показала характер.Она убивает всё в своём PID-пространстве. При старте сносит конкурентов (включая другой майнер —
kdevtmpfsi, кстати из ботнета Kinsing) и заодно любую отладку, потому что отладчик — это просто ещё один сосед-процесс.Как я ни подходил:
| Подход | Результат |
|---|---|
|
gdb напрямую | бот его убивает ||
unshare + net-ns | harness гибнет вместе с gdb (даже dpkg при сборке образа умирал!) || gdb как PID 1 контейнера | выжил, но бот форкается и re-exec'ит себя — уходишь не в тот процесс |
| seccomp-запрет
kill | gdb больше не убивают, но теперь падает сам gdb (внутренний баг) || eBPF/uprobe | ядро не даёт как надо в этом контейнере |
И вишенка: ключ ChaCha20 генерируется в рантайме из сильной энтропии и затирается. Классический перебор (LCG, Mersenne Twister, RC4 со словом «redtail») — ноль, шум.
Классический тупик. Пока не переформулируешь задачу.
---
## 7. Разворот: зачем мне ключ, если нужен текст?
Смотрите. Ключ нужен, чтобы расшифровать то, что бот отправляет. А что, если снять запись до шифрования?
Ищу точку, куда запись приходит ещё открытой. По декомпиляции вижу сигнатуру:
C:
// FUN_004fdb20 @ 0x4fdb20
int dcb_send_record(ctx, type, const void *record /* rdx */, size_t len /* rcx */, ...);
record здесь — ещё плейнтекст. Значит дампим rdx длиной rcx.Отладчика у нас нет (см. выше). Отладчик нам не нужен. Патчим бинарь.
### Патч, который бот не убьёт
Логика в три хода:
1. Не PIE → адрес в файле = адрес в памяти.
2. В
.text есть «щели» из nop'ов между функциями → кладём туда стаб.3. Пролог целевой функции переписываем на
jmp в стаб; в конце стаба возвращаемся в функцию.Находим щель (у меня — между
.fini и .rodata, 0x7f8300), пишем стаб:
Код:
; --- simplification ---
push rax; push rcx; push rdx; push rsi; push rdi; push r11; push r14
mov r14, rdx ; rdx = указатель на запись
mov eax, 257 ; sys_openat
lea rsi, [rip+path] ; путь к файлу-дампу
mov edx, 0x441 ; O_WRONLY|O_APPEND|O_CREAT
mov r10d, 0x1a4 ; 0644
syscall ; open
... ; write(fd, &len, 8)
... ; write(fd, record, len)
pop r14; pop r11; pop rdi; pop rsi; pop rdx; pop rcx; pop rax
push r15; mov r15, rdx; push r14 ; восстановленный пролог
jmp back
И точка входа превращается в:
Код:
0x4fdb20: E9 <rel32 → 0x7f8300> ; jmp в стаб
90 90 ; nop x2
Грабли, стоившие полдня: первая версия стаба давала мусор. Причина — в одну строку:
mov edx, 8 (запись длины) затирал rdx с указателем на данные. Лечится сохранением указателя в r15 до этого. Классика, о которой узнаёшь только по факту.Почему это работает: бот умеет убивать процессы. Отладчик — процесс. А патч — это байты внутри самого бота. Убивать нечего. Ты против него не «снаружи с отладчиком», ты уже внутри его кода.
---
## 8. Что оказалось внутри
Прогон дал 25 МБ плейнтекста (991 сессия). Бот сам, «добровольно», складывал содержимое канала.
Конфиг майнера — слово в слово:
JSON:
{
"autosave": true, "opencl": false, "cuda": false,
"cpu": { "enabled": true, "huge-pages": true, "max-threads-hint": 100 },
"randomx": { "mode": "auto", "1gb-pages": true, "rdmsr": false, "wrmsr": true },
"pools": [
{ "nicehash": true, "url": "proxies.identities.network:2137" },
{ "nicehash": true, "url": "proxies.insanitycpp.cx:2137" },
{ "nicehash": true, "url": "proxies.insanecppdev.com:2137" }
]
}
Команды распространения (то, чем червь «закрепляется» и чистит место):
Код:
crontab -r ; echo "@reboot {}" | crontab -
iptables -F ; iptables -I INPUT -p tcp --dport {} -j ACCEPT
~40 путей RCE — это и есть карта его атаки:
Код:
/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php CVE-2017-9841
/cgi-bin/.%2e/.%2e/…/bin/sh CVE-2021-41773 / CVE-2021-42013
/index.php?-d+allow_url_include=on+…+php://input CVE-2024-4577
+ Docker Engine API :2375 (без auth) и SSH-брутфорс
Телеметрия бэкона: модель CPU, cgroups,
ProcessName, LinuxCgroup — оператор собирает «паспорт» хоста.---
## 9. Про кошелёк: почему его нет и не будет
Самый частый вопрос — «а кошелёк-то где?». Отвечаю окончательно: его у бота нет.
В конфиге майнера — только
url пула и nicehash. Логина с XMR-адресом нет. Бот логинится на прокси оператора фиктивными кредами x:x, а настоящий кошелёк подставляется на стороне прокси — где-то там, куда нам хода нет.Проверил всё: все пять архитектур, дампы памяти живого процесса, весь перехваченный трафик XOR-брутфорсом и не только. Ноль адресов Monero. Это не «мыло не докрутили» — это бай-дизайн. Приватный пул с IP-based auth: сеть майнеров одна, а кошелёк один и живёт только у оператора.
Вывод практический: видишь
nicehash: true и login: "x" — не трать время на охоту за кошельком. Его там нет.---
## 10. Что забрать с собой (выводы)
1. «Не могу снять ключ» ≠ «не могу прочитать трафик». Часто достаточно взять плейнтекст на входе шифрования — на один шаг раньше.
2. Против малвари, убивающей отладчики, работает не отладчик, а патч. Ты входишь внутрь её же кода.
3. Не PIE + nop-щели в
.text — идеальные условия для инъекции стаба. Проверяй флаги сборки первым делом.4. MITM на «доверчивом» транспорте бесплатен, если он не проверяет сертификат. DoT «под Chrome» — не значит безопасный.
5. Приватный пул = кошелёк недостижим по определению. Не гоняйся.
И главное: в борьбе с малварью побеждает не самый громкий инструмент, а тот, кто раньше переформулировал задачу. Я не сломал их C2 — я дал боту возможность сдать его сам.
---
## 11. Честно о границах
- Всё — на изолированном стенде, без контакта с инфраструктурой оператора.
- Никаких эксплойтов и рабочего вооружения — только защитный разбор и методика.
- IOC и YARA — по запросу.
- Атрибуция не утверждается: RedTail — финансовая группировка, не APT.
---
Разбираю малварь, которая считает себя умнее аналитика. Пишите, если есть что разобрать вместе.
Telegram: @SIMCARDER22 · SKYNET
---
AI-выжимка
сгенерировано ИИ
Тезисы статьи скоро
Статья читается полностью. Тезисы со ссылками на разделы появятся позже.
Содержание
Codeby Academy
Практика и мастерство
От основ до продвинутого — программы для практиков Codeby.
Перейти к курсу →
Поиск в обсуждении
Комментарии
0