
На последнем jeopardy-CTF я убил 40 минут на три задачи с одинаковой механикой: скачать файл с удалённого сервера, распаковать, прогнать через strings, выцепить флаг из вывода через grep. На четвёртой задаче разозлился, открыл nano и написал bash-скрипт на шесть строк. Оставшиеся шесть задач закрылись по три минуты каждая. Bash скрипты для CTF — не программирование в классическом смысле, а склейка утилит, которые вы и так запускаете руками, в автоматическую цепочку. Разница между 40 минутами рутины и тремя минутами результата — один цикл while read и пара пайпов. Ниже — три готовых скрипта с построчным разбором, коллекция one-liners и пять ошибок, на которые наступает каждый новичок.
Bash есть везде. На урезанных CTF-контейнерах и минимальных образах Python отсутствует, но sh, grep и wget найдутся даже в BusyBox. Я встречал задания, где на удалённой машине не было ничего кроме sh и awk — и этого хватало для полной разведки файловой системы. Подробнее — в нашем статье о создание ctf заданий.
Второй момент — скорость создания. Склеить nmap, извлечение портов и gobuster — пять строк на bash. То же самое на Python с argparse и обработкой исключений — проект на 50+ строк. Когда у тебя два часа на десять задач, эта разница решает.
И третье: bash скрипты для пентеста — прямая реализация техник из MITRE ATT&CK. Запуск shell-скриптов на целевой системе маппится на технику T1059.004 Unix Shell (тактика Execution). Разведка файловой системы через find и ls — T1083 (File and Directory Discovery). Сбор системных данных через uname, id, cat /etc/os-release — T1082 (System Information Discovery). Перебор паролей через curl в цикле — T1110.001 (Password Guessing). В тестах Atomic Red Team для T1059.004 есть сценарии «Create and Execute Bash Shell Script» и «Harvest SUID executable files» — оба выполняются через sh на Linux. Это ровно то, что делают участники CTF: пишут скрипт и запускают для сбора информации.
Автоматизация CTF через bash экономит когнитивный ресурс. Вместо набивания одних и тех же команд вы сосредотачиваетесь на логике задачи — и именно это отличает участника, закрывающего 12 задач за соревнование, от того, кто закрывает 4.
Перед первым скриптом проверьте три вещи.
Версия bash. bash --version — подойдёт любая 4.x и выше. На macOS по умолчанию стоит bash 3.2 из-за лицензионных ограничений — обновите через brew install bash. На Windows — WSL2 с любым дистрибутивом Linux.
Утилиты. Скрипты из этой статьи работают с nmap, gobuster, curl, grep, awk, sed и wc. В Kali Linux и Parrot OS всё стоит из коробки. На минимальных дистрибутивах доставьте через apt install nmap curl gobuster. Конкретные версии значения не имеют — используются базовые флаги, стабильные годами.
Рабочая директория и shebang. Создайте папку: mkdir ~/ctf && cd ~/ctf. Каждый скрипт начинается со строки #!/bin/bash — она указывает системе интерпретатор. После создания файла сделайте его исполняемым: chmod +x script.sh. Без этого получите Permission denied — ловушка, через которую проходит каждый новичок без исключения. Альтернатива — запуск через bash script.sh, но это менее удобно при частом использовании.
Редактор — любой: nano для начинающих, vim для тех, кто готов потратить час на привыкание к модальному режиму. На CTF скрипты часто пишут прямо в терминале через nano script.sh — поднимать GUI-редактор некогда.
Самая частая рутина на CTF — просканировать порты, определить сервисы, проверить веб-директории. Три команды, которые всегда идут друг за другом — идеальный кандидат для автоматизации рутинных задач Linux. Логика: скрипт принимает IP-адрес целевой машины, создаёт папку под результаты, запускает nmap. Если находит HTTP на каком-либо порту — автоматически натравливает gobuster для перебора директорий. Если ни один порт с 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 без цели и зависнет в ожидании ввода — на CTF тратить на это время недопустимо.
Переменная DIR формирует имя папки вида recon_10.10.10.5, чтобы результаты каждой машины хранились отдельно и не перезаписывали друг друга. mkdir -p создаёт директорию без ошибки, если она уже существует.
Флаги nmap -sV -sC включают определение версий сервисов и запуск дефолтных NSE-скриптов. Без них nmap покажет только номера открытых портов без контекста — для CTF этого мало. Флаг -oN сохраняет результат в текстовом формате, пригодном для парсинга файлов bash-инструментами.
Дальше — самый интересный приём. Конструкция grep '/tcp.*http' находит строки вида 80/tcp open http в выводе nmap, затем grep -oE '^[0-9]+' вытаскивает номер порта из начала строки. Используются флаги GNU grep; в BusyBox флаг -oE доступен только если grep собран с опцией EXTENDED_REGEXP — на урезанных системах без неё замените на grep -o '[0-9]\+' (BRE-синтаксис). Если и -o недоступен, используйте grep '/tcp.*http' "$DIR/nmap.txt" | awk -F/ '{print $1}' | head -1 — более переносимый вариант для минимальных BusyBox-сборок. Через head -1 берём первый HTTP-порт. Если nmap не нашёл HTTP — переменная WEBPORT остаётся пустой, блок с gobuster не выполнится. Никаких ошибок, скрипт тихо завершается.
Перенаправление 2>/dev/null подавляет прогресс-бар gobuster, который засоряет терминал при автоматизированном запуске. Результаты при этом сохраняются в файл dirs.txt.
Для запуска на нескольких целях оберните вызов в цикл: while read -r ip; do ./recon.sh "$ip"; done < targets.txt. Файл targets.txt содержит по одному IP на строку. На CTF с десятком машин запускаете команду — и идёте читать условия задач, пока автоматизация пашет в фоне.
Веб-форма авторизации без rate-limiting и CSRF-защиты — классика CTF начального уровня. Четырёхзначный PIN или пароль из короткого вордлиста. По OWASP это A07:2021 (Identification and Authentication Failures). На CTF такая слабость — намеренная часть задачи. В продакшене — дыра, за которую бьют по рукам.
Перед написанием скрипта разберите запрос вручную. Откройте Developer Tools в браузере (вкладка Network), введите заведомо неверный пароль и зафиксируйте четыре параметра: URL формы (например, http://target:8080/login), метод (POST или GET), имена полей (user, pass) и текст ответа при ошибке (Access denied). Без этой ручной подготовки скрипт стреляет вслепую, и вы не узнаете, сработал он или нет.
#!/bin/bash
URL="http://target:8080/login"; FAIL="Access denied"
WORDLIST="${1:-/usr/share/wordlists/rockyou.txt}"
while read -r pass; do
resp=$(curl -s -d "user=admin" --data-urlencode "pass=${pass}" "$URL")
if ! echo "$resp" | grep -q "$FAIL"; then
echo "[+] Пароль найден: $pass"; exit 0
fi
done < "$WORDLIST"
echo "[-] Совпадений нет"
Переменная FAIL содержит маркер неверного пароля — ту самую строку, которую вы зафиксировали в Developer Tools. Конструкция ${1:-/usr/share/wordlists/rockyou.txt} берёт путь к вордлисту из первого аргумента, а если он не передан — подставляет дефолтный путь к rockyou.txt, который в Kali стоит из коробки.
Цикл while read -r pass читает вордлист построчно, не загружая файл в память целиком. Это критично: rockyou.txt весит 140+ МБ, и конструкция for pass in $(cat rockyou.txt) тупо убьёт оболочку. Флаг -r предотвращает интерпретацию обратных слешей — без него пароль pa\ss превратится в pass.
curl -s -d ... --data-urlencode отправляет POST-запрос: curl автоматически объединяет несколько -d/--data-urlencode в единое тело через &, при этом --data-urlencode кодирует спецсимволы (&, =, пробелы) в значении пароля. Флаг -s подавляет прогресс-бар. Ответ сервера попадает в переменную resp, и grep -q тихо проверяет наличие строки ошибки. Если Access denied не найден — пароль подошёл, скрипт выводит результат и завершается через exit 0.
Для ускорения можно параллелизировать через xargs -P: cat wordlist.txt | xargs -P 10 -I {} curl -s -d "user=admin&pass={}" "$URL" запустит 10 потоков одновременно. Но на CTF это редко нужно — задачи проектируются под короткие вордлисты, где линейный перебор укладывается в секунды.
Если задача требует перебора SSH или FTP — проще взять hydra: hydra -l admin -P wordlist.txt ssh://target. Одна строка, без скриптов. Написание bash скриптов для хакинга веб-форм нужно для нестандартных случаев, где hydra не справляется из-за кастомного формата ответа или многошагового процесса аутентификации.
Третий блок CTF-рутины — обработка файлов. Скачать архив, распаковать, найти среди сотен вложенных файлов тот, где спрятан флаг. Вручную это занимает вечность. Командная строка Linux для CTF решает задачу за секунды через конвейеры.
Классика: где-то на машине валяется файл с флагом формата flag{...} или CTF{...}. Команда grep -rn 'flag{' /home/ 2>/dev/null рекурсивно ищет строку, показывает имя файла и номер строки. Перенаправление 2>/dev/null подавляет ошибки доступа к защищённым директориям. Добавьте --include='*.txt' для поиска только по текстовым файлам: grep -rn --include='*.txt' 'flag{' / 2>/dev/null.
Для поиска по атрибутам — find. Файлы, изменённые за последний час: find / -type f -mmin -60 2>/dev/null. SUID-файлы — классический вектор повышения привилегий, в Atomic Red Team для T1059.004 есть тест «Harvest SUID executable files»: find / -perm -4000 -type f 2>/dev/null. Файлы с нестандартными расширениями: find . -name '*.enc' -o -name '*.b64' -o -name '*.hex'.
На соревнованиях часто дают архив с вложенными файлами или серию URL для скачивания. Скрипт ниже автоматизирует полный цикл: стягивает файлы по списку, определяет тип каждого, распаковывает архивы и ищет флаги.
#!/bin/bash
DIR="ctf_loot"; mkdir -p "$DIR" && cd "$DIR"
while read -r url; do
f=$(basename "$url"); curl -sO "$url"
file "$f" | grep -qi 'zip' && unzip -qo "$f" 2>/dev/null
file "$f" | grep -qiE 'gzip|tar' && tar xf "$f" 2>/dev/null
done < ../urls.txt
grep -rn 'flag{' . 2>/dev/null
find . -name '*.txt' -exec grep -l 'CTF{' {} \;
echo "[+] Обработка завершена, файлы в $DIR/"
Скрипт читает urls.txt построчно, скачивает через curl -sO (флаг -O сохраняет файл с оригинальным именем). Команда file определяет тип содержимого по магическим байтам, а grep -qi ищет маркеры архивов без учёта регистра. Если файл — ZIP, идёт распаковка через unzip, если tar/gzip — через tar xf. Финальные строки проходят по всем извлечённым файлам, выискивая паттерны флагов обоих форматов. Концептуально это близко к T1119 (Automated Collection) — автоматическому сбору данных. T1119 в MITRE ATT&CK формально охватывает разные платформы, но в Atomic Red Team тесты для неё реализованы только под Windows (command_prompt, powershell), так что прямая валидация на Linux через Atomic tests недоступна.
Парсинг файлов bash-утилитами — хлеб командной строки Linux для CTF. Паттерны, которые я использую на каждом соревновании:
Вытащить все IP-адреса из текстового файла: grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' file.txt | sort -u. Регулярка ищет четыре группы цифр через точку, sort -u убирает дубликаты.
Открытые порты из вывода nmap в строку через запятую: grep '^[0-9]' nmap.txt | cut -d'/' -f1 | tr '\n' ',' | sed 's/,$//'. Цепочка из четырёх утилит: grep берёт строки портов, cut отрезает /tcp, tr заменяет переводы строк на запятые, sed убирает финальную запятую. Результат: 22,80,443 — готовая строка для подстановки в другие инструменты.
Декодирование base64: base64 -d encoded.txt > decoded.txt. Несколько закодированных строк: while read -r line; do echo "$line" | base64 -d; done < encoded.txt. Hex-данные: xxd -r -p hex.txt > binary.out.
Частотный анализ слов (полезно для криптозадач): cat file.txt | tr ' ' '\n' | sort | uniq -c | sort -rn | head -20. Конвейер разбивает текст на слова, сортирует, считает повторения и выводит топ-20 по убыванию.
Не каждую задачу стоит оформлять в полноценный скрипт. Bash one-liners для пентестеров часто решают задачу быстрее.
Перебор поддоменов через DNS: while read -r sub; do host "$sub.target.com" | grep -v 'NXDOMAIN' && echo ">> $sub"; done < subdomains.txt — делает DNS-запрос для каждой строки из файла и фильтрует несуществующие записи.
HTTP-коды списка URL: while read -r url; do code=$(curl -so /dev/null -w '%{http_code}' "$url"); echo "$code $url"; done < urls.txt | grep -v '^404' — выводит код ответа для каждого адреса, отсеивая 404. Флаг -w '%{http_code}' заставляет curl показать только статус.
Генерация PIN-кодов: seq -w 0000 9999 > pins.txt — создаёт вордлист всех 4-значных PIN с ведущими нулями. Без флага -w значение 0042 превратится в 42, а это другой пароль.
Мониторинг изменений страницы: while true; do curl -s http://target/page | md5sum; sleep 5; done — каждые 5 секунд считает хеш содержимого. Если строка изменилась — на странице обновление. Помогает ловить динамические флаги и ротацию ключей на CTF.
Извлечение строк из бинарников: for f in *; do echo "=== $f ==="; strings "$f" | grep -iE 'flag|key|pass'; done — проходит по всем файлам в директории, извлекает читаемые строки и фильтрует потенциально значимые.
Быстрый ping-sweep сети: for ip in 192.168.1.{1..254}; do (ping -c 1 -W 1 "$ip" &>/dev/null && echo "$ip up") & done; wait — проверяет каждый IP в подсети параллельно с таймаутом 1 секунда. & перед done запускает каждый ping в фоне, wait дожидается завершения всех процессов.
Извлечение email-адресов: grep -oE '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}' dump.txt | sort -u — regex для email, результат без дубликатов. Полезно при анализе утечек и дампов баз данных.
Забытый chmod +x. Создали файл, запустили ./script.sh — получили Permission denied. Решение: chmod +x script.sh или запуск через bash script.sh. Вторая ошибка встречается даже чаще первой.
Пропущенные кавычки вокруг переменных. Конструкция if [ $var == "test" ] упадёт, если $var содержит пробел или пуста — bash подставит аргументы неправильно. Всегда оборачивайте: if [ "$var" == "test" ]. На CTF переменные часто содержат спецсимволы из парсинга — без кавычек скрипт ломается непредсказуемо, и отладка занимает больше времени, чем написание.
for вместо while read для больших файлов. Конструкция for line in $(cat bigfile.txt) загружает весь файл в память и разбивает по пробелам, а не по строкам. Вордлист на 14 миллионов записей (rockyou.txt) убьёт оболочку. Правильный вариант: while read -r line; do ... done < file.txt — построчное чтение без расхода памяти.
Отсутствие проверки зависимостей. Скрипт вызывает nmap, а nmap не установлен — на выходе пустые файлы и ломаные фильтры дальше по цепочке. Добавляйте в начало: command -v nmap >/dev/null || { echo "nmap не найден"; exit 1; }. Две строки, которые экономят час отладки.
Жёстко прописанные значения вместо аргументов. Скрипт с TARGET="10.10.10.5" в теле вместо TARGET="$1" — одноразовый. На CTF один скрипт запускается на десяти машинах. Используйте аргументы ($1, $2) и дефолты через ${1:-значение}. Это не усложняет код — это делает его переиспользуемым.
После нескольких соревнований у вас накопится десяток скриптов. Организуйте их правильно с самого начала — хаотичная папка с файлами типа script2_final_v3.sh через месяц превращается в помойку, где ничего не найти.
Создайте структуру: ~/ctf/bin/ для исполняемых скриптов, ~/ctf/lib/ для общих функций. Файл ~/ctf/lib/common.sh содержит переиспользуемые конструкции: проверку аргументов, цветной вывод, функции логирования. Подключение из любого скрипта — одна строка: source ~/ctf/lib/common.sh.
Добавьте ~/ctf/bin/ в переменную PATH: строка export PATH="$HOME/ctf/bin:$PATH" в ~/.bashrc. После этого вызывайте скрипты по имени из любой директории: recon.sh 10.10.10.5 вместо ~/ctf/bin/recon.sh 10.10.10.5.
Инициализируйте git-репозиторий через git init в ~/ctf/. Версионирование позволяет откатывать неудачные изменения и переносить всю коллекцию на новую машину через git clone. На CTF это особенно ценно: клонируете репозиторий на свежую виртуалку — и полный арсенал доступен за секунду.
Шаблон нового скрипта: shebang, проверка аргументов, проверка зависимостей, комментарий с описанием назначения. Четыре строки преамбулы, которые стоит копировать в каждый файл. Это не формализм — через месяц вы откроете скрипт и за две секунды вспомните, зачем он нужен.
Для параллельной работы с несколькими целями используйте tmux. tmux new-session -s ctf создаёт сессию, Ctrl+B % делит окно вертикально, Ctrl+B " — горизонтально. Одна панель — скрипт разведки, вторая — мониторинг логов, третья — ручная работа. На соревнованиях с несколькими машинами одна панель на каждую цель — параллельная разведка без переключения между терминалами.
За полтора года участия в CTF я пришёл к одному наблюдению: 80% задач начального и среднего уровня решаются десятком bash-скриптов и двумя десятками one-liners. Не потому что задачи простые — рутина в них одинаковая. Скачай, распакуй, найди, отправь. Разница между участниками, которые закрывают 12 задач за соревнование, и теми, кто закрывает 4 — не в знаниях. Знания примерно одинаковые на этом уровне. Разница — в автоматизации. Один набирает nmap -sV -sC -oN ... руками на каждой машине и ждёт. Другой запускает скрипт и через 90 секунд читает готовые результаты.
Вижу повторяющуюся ошибку: новички пытаются «выучить bash» как отдельную дисциплину. Читают книжки по 600 страниц, проходят академические курсы по shell scripting от A до Z. Для CTF не нужен весь bash. Нужны while read, grep -oE, curl -s -d, пайпы и условие [ -n "$var" ]. Шесть конструкций покрывают 90% задач. Остальное гуглится в моменте, когда задание требует чего-то нестандартного. Важнее привычка автоматизировать до второго повторения: если команду набрали дважды — это сигнал написать скрипт. Не красивый, не идеальный — рабочий. Три строки, которые экономят десять минут, ценнее элегантного решения на 50 строк, которое вы напишете «когда-нибудь потом». Если хотите выстроить базу системно и не тыкаться по разрозненным гайдам — курс IB Basics на codeby.school берёт с нуля до первых рабочих задач, без академического тона.
🚀 Хочешь закрепить на практике? Реши задачи по теме на HackerLab — категория «pentest-machines».
0 комментариев
Пожалуйста, войдите, чтобы оставить комментарий.
Загрузка комментариев...