Главная / Блог / Bash скрипты для CTF: автоматизация разведки

14 мин.00

Bash скрипты для CTF: автоматизация разведки

Bash скрипты для CTF: автоматизация разведки

На TryHackMe за вечер я решал три машины из раздела Easy. На первой потратил 18 минут только на разведку: вручную запустил nmap с тремя наборами флагов, скопировал открытые порты, вбил адрес в gobuster, подождал, перешёл к nikto. На второй — повторил ту же рутину. На третьей разозлился и написал скрипт. Четвёртая машина ушла в разведку за 90 секунд. Bash скрипты для CTF — не программирование в классическом смысле, а склейка инструментов, которые вы и так запускаете руками, в одну автоматическую цепочку.

Почему bash скрипты, а не Python или готовые инструменты

Первый вопрос новичков: зачем писать bash скрипт для разведки, если существуют AutoRecon, Reconftw и десятки Python-фреймворков? Ответ укладывается в три ситуации.

На CTF-машине нет Python. Минимальные контейнеры и урезанные образы — не редкость на jeopardy-CTF. Bash есть везде, где есть терминал. Я встречал задачи, где на удалённой машине не было ничего кроме sh, curl и grep — и этого хватало.

Нужно склеить три утилиты в цепочку. Запустить nmap, выдернуть открытые порты, подставить в gobuster — пять строк на bash. То же самое на Python с argparse и обработкой исключений — проект на 50+ строк. Чувствуете разницу?

Время ограничено. На jeopardy-CTF два часа на десять задач. Написание и отладка Python-скрипта — 15 минут. Bash-обвязка — 2 минуты. На соревнованиях эта разница решает.

В терминологии MITRE ATT&CK использование bash-скриптов на целевой системе — техника T1059.004 (Unix Shell). На CTF bash пронизывает всю цепочку. Разведка файловой системы через find и ls — T1083 (File and Directory Discovery). Сбор системных данных через uname, id, cat /etc/os-release — T1082 (System Information Discovery). Брутфорс HTTP-форм через curl в цикле — T1110.001 (Password Guessing). Автоматический парсинг результатов сканирования можно соотнести с T1119 (Automated Collection), хотя тесты Atomic Red Team для этой техники реализованы только под Windows и прямой аналогии с Linux-скриптами нет.

Если посмотреть на тесты Atomic Red Team для T1059.004, среди них есть «Create and Execute Bash Shell Script» и «Harvest SUID executable files» — оба выполняются через sh на Linux. По сути, это ровно то, что делают участники CTF: создают скрипт и запускают его для сбора информации о целевой системе. Bash — не конечный инструмент атаки, а клей между nmap, curl, grep, awk. Автоматизация CTF задач через bash экономит когнитивный ресурс и позволяет сосредоточиться на логике задачи, а не на набивании одних и тех же команд.

Подготовка окружения за пять минут

Перед написанием первого скрипта проверьте три вещи.

Версия bash. Выполните bash --version — для скриптов из этой статьи подойдёт любая 4.x и выше. На macOS по умолчанию стоит bash 3.2 из-за лицензионных ограничений — обновите через brew install bash. На Windows используйте WSL2 с любым Linux-дистрибутивом.

Инструменты. Скрипты ниже работают с nmap, gobuster, curl, grep, awk и wc. В Kali Linux и Parrot OS всё установлено из коробки. На минимальных дистрибутивах доставьте через apt install nmap curl gobuster. Версии для скриптов этой статьи значения не имеют — используются только базовые флаги, стабильные годами.

Рабочая директория и шебанг. Создайте папку командой mkdir ~/ctf && cd ~/ctf. Каждый скрипт начинается с #!/bin/bash — строка указывает системе интерпретатор. После создания файла сделайте его исполняемым: chmod +x script.sh. Без этого получите Permission denied — первая ловушка, через которую проходят все. Я в первый месяц на это попадался через раз и каждый раз удивлялся заново.

Редактор — любой. nano для начинающих, vim для тех, кто готов потратить час на привыкание к модальному режиму (зато потом летаешь). На CTF скрипты часто пишут прямо в терминале: nano script.sh или cat > script.sh << 'EOF' — поднимать GUI-редактор некогда.

Скрипт для автоматизации разведки

Самая частая рутина на CTF: просканировать порты, определить сервисы, проверить веб-директории. Три команды, которые всегда идут друг за другом — идеальный кандидат для bash скрипта разведки.

Логика простая: скрипт принимает IP-адрес целевой машины, создаёт директорию под результаты, запускает nmap, проверяет наличие веб-сервера. Если находит HTTP на каком-либо порту — автоматически запускает перебор директорий. Предусловия: на хосте должен быть запущен хотя бы один сервис, для перебора директорий нужен HTTP. Если всё закрыто — скрипт отработает только nmap-этап.

#!/bin/bash
[ -z "$1" ] && echo "Использование: $0 <IP>" && exit 1
IP="$1"; DIR="recon_$IP"; mkdir -p "$DIR"
echo "[*] Сканирую порты $IP..."
nmap -sV -sC -oN "$DIR/nmap.txt" "$IP"
WEBPORT=$(grep '/tcp.*http' "$DIR/nmap.txt" | grep -oE '^[0-9]+' | head -1)
[ -n "$WEBPORT" ] && echo "[*] HTTP на порту $WEBPORT, запускаю gobuster..." \
  && gobuster dir -u "http://$IP:$WEBPORT" -w /usr/share/wordlists/dirb/common.txt \
  -o "$DIR/dirs.txt" 2>/dev/null
echo "[+] Результаты в ./$DIR/"

Разберём построчно. Конструкция [ -z "$1" ] проверяет, передан ли аргумент — без неё скрипт запустит nmap без цели и зависнет в ожидании ввода. Переменная DIR формирует имя папки вида recon_10.10.10.5, чтобы результаты каждой машины хранились отдельно и не перезаписывали друг друга.

Флаги nmap -sV -sC включают определение версий сервисов и запуск дефолтных NSE-скриптов — без них nmap покажет только номера открытых портов без контекста. Флаг -oN сохраняет результат в текстовом формате, пригодном для парсинга через grep.

Ключевой момент — извлечение порта веб-сервера. Конструкция grep '/tcp.*http' ищет строки вида 80/tcp open http в выводе nmap, затем grep -oE '^[0-9]+' извлекает номер порта из начала строки. Используются только POSIX-совместимые флаги, работающие в том числе в BusyBox grep. Через head -1 берём номер первого HTTP-порта. Если nmap не обнаружил HTTP — переменная WEBPORT остаётся пустой, и блок с gobuster не выполнится. Тихо и без ошибок.

Перенаправление 2>/dev/null подавляет stderr-вывод gobuster — прогресс-бар и предупреждения, которые засоряют терминал при автоматизированном запуске. Результаты сохраняются в dirs.txt.

Запуск: ./recon.sh 10.10.10.5. После завершения в папке recon_10.10.10.5/ лежат nmap.txt с полным выводом сканера и dirs.txt с обнаруженными директориями.

Расширение скрипта и работа с несколькими целями

Для пассивной разведки скрипт может оборачивать вызовы subfinder или theHarvester — запустить оба инструмента одной командой и свести результаты в общий файл через cat results_*.txt | sort -u > all_subs.txt. Эти утилиты работают без прямого взаимодействия с целью, собирая данные из публичных источников.

Для множественных целей оберните вызов в цикл: while read -r ip; do ./recon.sh "$ip"; done < targets.txt. Файл targets.txt — по одному IP на строку. На CTF с десятком машин это экономит десятки минут: запускаете и идёте читать условия задач, пока recon автоматизация linux-инструментами работает в фоне.

Добавить проверку дополнительных сервисов — пара строк. Например, grep -i ssh "$DIR/nmap.txt" && echo "SSH обнаружен" >> "$DIR/services.txt" для SSH. Каждый новый блок — 2-3 строки, не усложняющие общую логику.

Автоматизация перебора bash: брутфорс HTTP-формы

Вторая типовая задача на CTF — веб-форма авторизации без rate-limiting и CSRF-защиты. Классика начального уровня: 4-значный PIN или пароль из короткого вордлиста. Отсутствие ограничений на частоту запросов — по аналогии это API4:2023 (Unrestricted Resource Consumption) из OWASP API Security Top 10, а для классических веб-форм — Broken Authentication / Insufficient Anti-automation из OWASP Top 10. На CTF это намеренная слабость. В продакшене — дыра, за которую бьют по рукам.

Перед написанием скрипта разберите запрос вручную. Откройте Developer Tools в браузере (вкладка Network), введите заведомо неверный пароль и зафиксируйте четыре вещи: URL формы (например, http://target:8080/login), метод (POST/GET), имена полей (username, password) и текст ответа при ошибке (Access denied, Wrong password). Эти параметры — входные данные для скрипта. Без этой ручной подготовки скрипт будет стрелять вслепую.

#!/bin/bash
TARGET="http://target:8080/login"; FAIL="Access denied"
while read -r pass; do
  resp=$(curl -s --data-urlencode "user=admin" --data-urlencode "pass=${pass}" "$TARGET")
  if ! echo "$resp" | grep -q "$FAIL"; then
    echo "[+] Пароль найден: $pass"
    break
  fi
done < "${1:-wordlist.txt}"

Конструкция while read -r pass читает файл строка за строкой, помещая каждую строку в переменную pass. Флаг -r запрещает bash интерпретировать обратные слэши — без него пароль test\n123 превратится в testn123, и вы пропустите верный вариант. Подвох: < "${1:-wordlist.txt}" стоит после done, а не в начале цикла — синтаксическая особенность bash, к которой нужно просто привыкнуть. Конструкция ${1:-wordlist.txt} означает: если передан аргумент — использовать его как имя файла, иначе — wordlist.txt по умолчанию.

Команда curl -s --data-urlencode "user=admin" --data-urlencode "pass=${pass}" "$TARGET" отправляет POST-запрос. Флаг -s подавляет прогресс-бар — без него вывод скрипта забьётся строками состояния загрузки. Флаг --data-urlencode задаёт тело запроса с автоматическим экранированием спецсимволов (&, =, +) — без этого пароли с такими символами исказят структуру запроса.

Проверка echo "$resp" | grep -q "$FAIL" ищет в ответе строку ошибки. Флаг -q переводит grep в тихий режим — он ничего не выводит, только устанавливает код возврата: 0 при совпадении, 1 при отсутствии. Оператор ! инвертирует условие: если строки ошибки нет — пароль подошёл. Команда break прерывает цикл.

Вариант для числового PIN. Замените while read на for pin in $(seq -w 0000 9999); do и подставляйте $pin. Флаг -w в seq добавляет ведущие нули — без него генерируется 1 вместо 0001, и четырёхзначный PIN не совпадёт с ожидаемым форматом. Мелочь, а полчаса на отладку.

Отслеживание прогресса. Для длинных вордлистов добавьте счётчик: переменную N=0 перед циклом и N=$((N+1)); echo -ne "\r[*] Проверено: $N" >&2 внутри тела цикла. Вывод в stderr через >&2 не смешивается с основным результатом и перезаписывается на месте благодаря \r.

Когда скрипт не справится. CSRF-токены требуют двухшагового запроса: сначала получить страницу через curl, извлечь токен через grep, вставить в POST. Скрипт разрастётся до 20+ строк и сломается при первом изменении формы. Rate-limiting с sleep 0.5 перед done растягивает перебор 10 000 PIN: даже при быстром локальном ответе (~50 мс) базовое время составит около 8 минут, а с задержкой — свыше 90 минут. В обоих случаях hydra или Python с requests.Session() — разумный выбор.

Bash one-liners для CTF: рабочая коллекция

Не каждая задача требует полноценного скрипта. Часто нужен однострочник, решающий проблему за 10 секунд. Ниже — набор bash one-liners для CTF, которые я использую на каждом соревновании.

# Поиск флагов по файловой системе
grep -rE 'flag\{|CTF\{|HTB\{|picoCTF\{' / 2>/dev/null
# Извлечь открытые порты из nmap в строку через запятую
grep '/tcp.*open' nmap.txt | cut -d'/' -f1 | tr '\n' ',' | sed 's/,$//'
# Декодирование base64
cat encoded.txt | base64 -d
# Перебор поддоменов через DNS
while read -r sub; do host "$sub.target.com" | grep "has address"; done < subs.txt
# Проверка списка URL на HTTP-код
while read -r url; do echo -n "$url "; curl -s -o /dev/null -w "%{http_code}" "$url"; echo; done < urls.txt

Разберу неочевидные моменты. Строка с извлечением портов — grep '/tcp.*open' nmap.txt | cut -d'/' -f1 | tr '\n' ',' | sed 's/,$//' — берёт строки вида 80/tcp open http, вырезает номер порта до слэша через cut, склеивает через запятую с помощью tr и убирает финальную запятую через sed. На выходе: 22,80,443 — формат, который принимает nmap -p для точечного повторного сканирования с нужными флагами.

Команда grep -rE 'flag\{|CTF\{|HTB\{|picoCTF\{' / 2>/dev/null рекурсивно ищет флаги по всей файловой системе. Флаг -r включает рекурсию, -E — расширенные регулярные выражения с оператором | (ИЛИ). Перенаправление 2>/dev/null скрывает ошибки доступа — без него терминал забьётся строками Permission denied от закрытых директорий. В терминологии MITRE ATT&CK это можно условно соотнести с T1119 (Automated Collection), хотя тесты Atomic Red Team для этой техники существуют только под Windows и напрямую к Linux-скриптам не применимы.

Проверка HTTP-кодов использует curl -s -o /dev/null -w "%{http_code}" — конструкция выводит только статус-код, сбрасывая тело ответа. Быстрая фильтрация: какие из 50 URL живые (200), какие закрыты (403), какие не существуют (404).

Ещё один must-have для повышения привилегий: find / -perm -4000 -type f 2>/dev/null — поиск бинарников с SUID-битом. Среди тестов Atomic Red Team для T1059.004 есть «Harvest SUID executable files», делающий ровно то же самое. Найденные SUID-бинарники сверяйте с GTFOBins — если bash, awk, base64, curl или cat имеют SUID, через них часто получается читать закрытые файлы или поднимать привилегии до root.

Пять ошибок в bash скриптах, на которых горят новички

За два года на CTF я наступил почти на все эти грабли. Делюсь, чтобы вы не наступали.

Забытый chmod +x. Создали файл, запустили ./script.sh, получили Permission denied. Решение: chmod +x script.sh. Альтернатива — запускать через bash script.sh без установки бита исполнения. Но с chmod удобнее — не нужно каждый раз указывать интерпретатор.

Пробелы в присваивании. В bash VAR = "value" — это не присваивание, а вызов команды VAR с аргументами = и "value". Правильно: VAR="value" без пробелов вокруг =. Ошибка коварна — bash не всегда сообщит об этом явно, скрипт просто работает с пустой переменной, и вы тратите 20 минут на поиск бага в логике, которого нет. Классика.

Пропущенный -r в read. Без флага -r команда read интерпретирует обратные слэши как escape-символы. Пароль admin\123 превратится в admin123. На CTF это пропущенный верный пароль при автоматизации перебора bash — задача останется нерешённой, хотя пароль был в вордлисте.

Одинарные кавычки вместо двойных. В одинарных кавычках переменные не раскрываются: echo '$VAR' выведет буквальный текст $VAR. Двойные кавычки: echo "$VAR" подставят значение. Путаница между ними — причина номер один непонятных ошибок в скриптах с curl, когда вместо пароля на сервер улетает строка ${pass}.

Отсутствие проверки пустого вывода. Скрипт парсит результат nmap, но сканирование вернуло пустоту — хост не ответил, неправильный IP или сервис на нестандартном порту. Без проверки [ -s "$DIR/nmap.txt" ] (файл существует и не пустой) дальнейшие grep и cut работают с пустотой, и скрипт молча «завершается успешно» без результата. Я так потерял полчаса на HackTheBox — скрипт отработал, а в папке лежали пустые файлы.

Дополнительный совет: добавляйте set -e после шебанга. Директива останавливает скрипт при первой ошибке — без неё bash продолжит выполнение после сбоя, и результаты будут непредсказуемыми. Для отладки используйте set -x — bash напечатает каждую команду перед выполнением, и вы увидите, на какой строке всё ломается. Я включаю set -x каждый раз, когда скрипт ведёт себя странно — экономит минуты.

Когда bash скрипт для пентеста не подходит

Автоматизация через bash имеет чёткие границы. Понимание их экономит время: вместо борьбы с ограничениями языка вы сразу возьмёте правильный инструмент.

Rate-limiting и CSRF-токены. Если приложение ограничивает частоту запросов или требует уникальный токен в каждой форме — чистый curl в цикле не справится. Для CSRF нужно получить страницу, извлечь токен, вставить в POST — скрипт разрастается и ломается при любом изменении разметки. Берите hydra для стандартных протоколов или Python с requests.Session() для сложной логики.

Многопоточность. Bash-цикл обрабатывает запросы последовательно: один за другим. Перебор 100 000 паролей при среднем времени ответа 200 мс — около 5,5 часов. ffuf с 40 параллельными потоками сделает ту же работу за минуты. В bash можно запускать процессы в фоне через & и wait, но контролировать количество одновременных потоков — нетривиальная задача, и результат хрупкий.

Сложный парсинг. Если ответ сервера — JSON с вложенной структурой, grep с awk не вытянут. Используйте jq (если установлен на машине) или переходите на Python.

Бинарные протоколы. SSH-брутфорс, работа с SMB, взаимодействие с базами данных — bash не имеет нативных библиотек для этих протоколов. hydra, crackmapexec, medusa написаны на C и Python именно потому, что бинарные протоколы требуют низкоуровневой реализации.

Задача Bash Специализированный инструмент
Сканирование портов + сбор вывода Да (обёртка над nmap) nmap напрямую
Перебор директорий (<10k) Да gobuster, ffuf (быстрее)
Брутфорс HTTP без защиты Да hydra, ffuf (быстрее)
Брутфорс с CSRF-токенами Частично (хрупко) Python + requests
Перебор >100k вариантов Нет (слишком медленно) ffuf, hashcat
Парсинг JSON-ответов Частично (через jq) Python
Бинарные протоколы (SSH, SMB) Нет hydra, crackmapexec

Правило, которое я выработал для себя: если bash-скрипт перевалил за 30 строк — пора переписывать на Python. До 30 строк bash выигрывает по скорости написания и отладки.

Написание bash скриптов для пентеста — навык, который окупается не количеством строк кода, а сэкономленными минутами на CTF. Три скрипта из этой статьи закрывают около 80% рутины начального уровня: разведка, перебор, парсинг вывода. Остальные 20% — задачи, где bash уступает специализированным инструментам, и это нормально.

Я начинал с копирования чужих скриптов с GitHub, постепенно переписывая их под конкретные задачи. Первый месяц каждый скрипт ломался на кавычках и пробелах — типичный путь. Через два-три десятка решённых машин руки набирают while read -r на автомате. Главное — не пытаться сразу написать «универсальный recon-фреймворк на bash» с поддержкой всех протоколов и красивым интерактивным меню. Это ловушка: я видел, как десятки начинающих проваливались в неё — вместо решения задач неделями полируют один скрипт на 300 строк, который всё равно ломается на нестандартном ответе сервера. Лучше держать десяток коротких болванок по 10-20 строк под конкретные сценарии. Мой репозиторий устроен именно так: 15 файлов, каждый делает одну вещь. Если скрипт решил задачу за 40 секунд вместо 15 минут руками — код хороший, даже без обработки ошибок и комментариев. А если хочется не только bash-обвязки, а системное понимание безопасности от сети до приложения — на IB Basics в Codeby School эту базу разбирают с нуля, чтобы не ковыряться в Kali наугад.

🚀 Хочешь закрепить на практике? Реши задачи по теме на HackerLab — категория «pentest-machines».

Поделиться

0 комментариев

Пожалуйста, войдите, чтобы оставить комментарий.

Загрузка комментариев...

Читайте также

XXE инъекция: эксплуатация в CTF от А до Я

12 мин.

4

XXE инъекция: эксплуатация в CTF от А до Я

Полный арсенал XXE эксплуатации для CTF: чтение файлов, SSRF к облачным метаданным, blind OOB-эксфильтрация, error-based XXE и обход WAF с рабочими пейлоадами.

8 СЕНТЯБРЬ, 2026

Сканирование портов nmap для CTF: гайд

15 мин.

10

Сканирование портов nmap для CTF: гайд

Разбираем TCP handshake, типы сканирования nmap и статусы портов. Пошаговый CTF-workflow: от быстрого скана до обнаружения скрытых сервисов на нестандартных портах

8 СЕНТЯБРЬ, 2026

Форензика файловых систем в CTF: Autopsy и TestDisk

13 мин.

7

Форензика файловых систем в CTF: Autopsy и TestDisk

Пошаговый разбор анализа образов дисков на CTF: восстановление удалённых файлов в Autopsy, TestDisk для разделов, carving через PhotoRec и антифорензика по MITRE ATT&CK.

7 СЕНТЯБРЬ, 2026