Главная / Блог / Bash для CTF: grep, awk, sed и пайплайны для быстрого поиска флагов

13 мин.00

Bash для CTF: grep, awk, sed и пайплайны для быстрого поиска флагов

Bash для CTF: grep, awk, sed и пайплайны для быстрого поиска флагов

Bash для CTF: grep, awk, sed и пайплайны для быстрого поиска флагов

На прошлогоднем jeopardy-CTF мне попался forensics-таск: архив на 47 МБ, внутри — 14 тысяч строк логов, конфигов и мусорных файлов. Флаг формата CTF{...} спрятан где-то в этой куче. Один grep -rnoP с правильным паттерном — и через две секунды флаг на экране. Коллега из команды листал файлы через less, потратил двадцать минут и сдался. Разница не в таланте, а в умении собрать правильный пайплайн из трёх-четырёх базовых утилит. Дальше разберём bash для CTF на практике: как grep, awk, sed и пайплайны экономят часы на соревнованиях.

Место в цепочке CTF-решения

Текстовые утилиты linux для пентеста и CTF — не самостоятельное оружие, а клей между этапами решения таска. По аналогии с MITRE ATT&CK (маппинг условный, для обучения) цепочка выглядит так:

  1. Получение файлов — скачиваем архив, подключаемся по SSH, получаем дамп. Bash-утилиты пока не нужны.
  2. Обнаружениеfind, ls -la, file помогают понять, что перед нами: бинарник, лог, картинка со вшитыми данными. Это File and Directory Discovery (T1083, Discovery).
  3. Извлечение данныхstrings, cat, grep вытаскивают читаемый текст из файлов. Это Data from Local System (T1005, Collection). На реальном пентесте тот же приём используется для поиска учётных данных — Credentials In Files (T1552.001, Credential Access).
  4. Декодированиеbase64 -d, трансформации через sed. Это Deobfuscate/Decode Files or Information (T1140).
  5. Получение флага — результат пайплайна.

Без предыдущего шага следующий бесполезен: нет смысла запускать grep по бинарнику, если перед этим не вытащили текст через strings. Понимание этой последовательности — и есть скорость на CTF.

Требования к окружению

  • ОС: любой Linux-дистрибутив — Kali, Ubuntu, Parrot. macOS подойдёт, но grep -P (Perl-regex) недоступен без установки GNU grep через brew install grep (после установки вызывается как ggrep -P, а не grep -P)
  • RAM: 512 МБ для текстовых тасков. Для дампов памяти от 4 ГБ — минимум 4 ГБ RAM
  • Утилиты: grep, awk, sed, find, strings, sort, uniq, cut, base64 — все входят в любой дистрибутив. Проверка: which grep awk sed
  • Версия grep: для Perl-совместимых regex нужен GNU grep 2.5+. Узнаётся через grep --version
  • Сеть: не требуется — всё работает офлайн

grep для поиска флагов: регулярные выражения на практике

grep — первое, что запускаешь на любом CTF-таске с файлами. Задача простая: выцепить строку с флагом среди потока текста.

Базовый поиск по формату флага

Формат флага почти всегда известен заранее: CTF{...}, flag{...}, picoCTF{...}, codeby{...}. Поэтому достаточно одного вызова grep -rn 'CTF{' . по текущей директории. Флаг -r включает рекурсию по поддиректориям, а -n покажет номер строки — пригодится, чтобы понять контекст находки.

Если формат флага неизвестен, берите обобщённый паттерн: grep -rnoP '[A-Za-z0-9_]+\{[^\}]+\}' .. Разбор по частям: -o выводит только совпавший фрагмент (не всю строку целиком), -P включает Perl-совместимые регулярные выражения grep, [A-Za-z0-9_]+ ловит одно или более буквенно-цифровых символов перед фигурной скобкой, а \{[^\}]+\} захватывает содержимое в фигурных скобках. Паттерн подходит для любого формата вида WORD{содержимое} — универсальная стартовая точка.

Регулярные выражения grep для типичных задач

На соревнованиях встречаются флаги с конкретным набором символов. Если известно, что внутри скобок только hex: grep -oP 'flag\{[0-9a-f]+\}' файл. Если флаг может содержать base64-подобную строку: grep -oP 'flag\{[A-Za-z0-9+/=]+\}' файл. Для UUID-подобных флагов: grep -oP 'flag\{[0-9a-f-]{36}\}' файл.

Для поиска с контекстом — когда флаг разбит на части или валяется рядом с подсказкой — берите grep -rn -B3 -A3 'password' logs/. Флаги -B 3 и -A 3 покажут по три строки до и после совпадения. На тасках с анализом логов bash учётные данные и флаг часто оказываются в соседних строках, и без контекста находка бесполезна.

Поиск строк в бинарных файлах через strings

Частая ошибка новичков — запускать grep прямо по бинарнику. grep выдаст «Binary file matches» без полезного содержимого. Решение — связка: strings файл | grep -i 'flag'. Утилита strings извлекает все печатаемые последовательности из файла (по умолчанию от 4 символов), а grep ищет паттерн уже по чистому тексту. Флаг -i игнорирует регистр — авторы тасков иногда записывают флаг как FLAG{} или Flag{}.

Запасной вариант — grep -a 'flag' бинарник, где -a заставляет grep обрабатывать бинарный файл как текстовый. Работает, но выдаёт заметно больше мусора. Связка со strings чище и предсказуемее. Можно задать минимальную длину строки: strings -n 8 файл — извлечёт только последовательности от 8 символов, отсеяв короткие обрывки.

Пайплайны bash linux: цепочки команд

Пайплайн — конструкция, в которой вывод одной команды через оператор | подаётся на вход следующей. В контексте CTF пайплайны bash linux позволяют за один однострочник пройти путь от сырых данных до чистого флага. Тут bash для CTF и раскрывается по-настоящему.

find + xargs + grep: массовый поиск

Получили директорию с сотнями файлов разных типов — нужно найти флаг. Команда find . -type f | xargs grep -l 'flag{' 2>/dev/null перебирает все файлы и выводит имена тех, где встречается подстрока flag{. find генерирует список файлов, xargs группирует их в аргументы для grep, а -l заставляет grep выводить только имена файлов. Перенаправление 2>/dev/null глушит ошибки доступа — без него экран забьётся строками «Permission denied».

Для файлов с пробелами в именах (а на тасках это бывает специально, чтобы сломать наивные скрипты): find . -type f -print0 | xargs -0 grep -l 'flag{'. Параметр -print0 и -0 используют нулевой байт как разделитель вместо переноса строки, и пробелы перестают быть проблемой.

Декодирование на лету: strings + base64 + grep

На forensics-тасках флаг часто закодирован в base64 и спрятан внутри бинарника. Типичный пайплайн:

strings dump.bin \
  | grep -oP '[A-Za-z0-9+/=]{20,}' \
  | while read line; do
      echo "$line" | base64 -d 2>/dev/null
    done \
  | grep -i 'flag'

Разбор: strings извлекает текст из бинарника, grep -oP ловит строки длиной от 20 символов из base64-алфавита, цикл while read декодирует каждую строку, 2>/dev/null глушит ошибки невалидного base64, а финальный grep ищет флаг в декодированном выводе. Ограничение: пайплайн работает, только если base64-строка целиком попадает в одну строку вывода strings; для многострочного base64 одного tr -d '\n' часто недостаточно, так как strings разрывает последовательности на границах непечатаемых символов. Надёжнее сначала вытащить текст в файл (strings -n 8 dump.bin > tmp.txt), руками найти границы base64-блока и склеить, либо использовать binwalk/foremost для автоматического извлечения вложенных данных. Это практическая реализация Deobfuscate/Decode (T1140) — приём работает и в CTF, и на пентесте при разборе конфигурационных файлов.

Статистический анализ: sort + uniq

Когда в логе тысячи строк и непонятно, откуда начинать, — начните со статистики. Команда awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -20 покажет топ-20 значений первого поля (обычно IP-адрес) по частоте обращений. Аномальный IP с тысячами запросов — первый кандидат на детальное изучение. Дальше фильтруете: grep '10.0.0.42' access.log | grep -i 'flag\|admin\|secret'.

Тот же подход работает для любого поля: замените $1 на нужный номер — $7 для URL, $9 для HTTP-кода ответа, $10 для размера тела.

awk примеры для CTF: разбор логов по полям

awk — полноценный язык обработки текста в linux, но на CTF он нужен главным образом для одной задачи: вытащить конкретное поле из структурированных данных. Если grep ищет строку целиком, то awk работает с колонками внутри строки.

Извлечение полей и нестандартные разделители

Типичная строка access-лога Apache: 192.168.1.1 - - [10/Oct/2023:13:55:36] "GET /secret HTTP/1.1" 200 2326. По умолчанию awk разбивает строку по пробелам и табуляциям. Команда awk '{print $7}' access.log выведет седьмое поле — URL запроса. Команда awk '{print $1, $7, $9}' access.log выведет IP, URL и код ответа — три поля, с которых начинается любой анализ.

Для данных с другим разделителем — -F. Разбор /etc/passwd по двоеточиям: awk -F: '{print $1, $3}' /etc/passwd — выводит имя пользователя и UID. CSV с запятой: awk -F, '{print $3}' data.csv. На CTF-тасках попадаются и экзотические форматы — pipe-delimited (|), логи с произвольными разделителями. Указывайте нужный символ через -F.

Условная фильтрация и подсчёт

awk умеет фильтровать строки по условию. Найти записи с HTTP-ответом 500 и выше: awk '$9 >= 500 {print $0}' access.log. Отфильтровать POST-запросы к конкретному эндпоинту: awk '$6 ~ /POST/ && $7 ~ /login/' access.log. Оператор ~ проверяет совпадение с regex, а && — логическое «и».

Подсчёт уникальных значений в конкретном поле — одна из самых частых задач при поиске по логам:

awk '{print $7}' access.log | sort | uniq -c | sort -rn | head -10

Результат: топ-10 запрашиваемых URL с количеством обращений. Аномально популярный путь вроде /admin/flag.txt или /backup/dump.sql сразу бросается в глаза. Связка awk | sort | uniq -c | sort -rn — один из самых ходовых рецептов обработки текста в linux, запомните его как готовый шаблон.

sed команды linux: трансформация и очистка данных

sed — потоковый редактор. На CTF он нужен, когда данные надо не найти, а трансформировать: убрать мусор, заменить символы, вырезать подстроку по шаблону.

Замена, удаление и извлечение подстрок

Базовый синтаксис замены: sed 's/что/на_что/g' файл. Флаг g — замена всех вхождений в строке, без него заменится только первое. Практический пример: авторы misc-тасков обожают leet-speak. Команда echo 'fl4g{r3pl4c3_m3}' | sed 's/4/a/g; s/3/e/g' вернёт flag{replace_me} — две замены через точку с запятой в одном вызове.

Удаление мусорных строк: sed '/^$/d' файл убирает пустые строки, sed '/^#/d' config.txt — комментарии. Очистить HTML от тегов: sed 's/<[^>]*>//g' page.html — полезно, когда флаг спрятан в HTML среди разметки. Удалить первые N строк заголовка: sed '1,5d' файл — убирает строки с 1 по 5.

Группы захвата: хирургическое извлечение флага

Самый мощный приём sed для CTF — извлечение подстроки через группы: конструкция echo 'User flag is: CTF{s3d_m4st3r} here' | sed -n 's/.*\(CTF{[^}]*}\).*/\1/p' вырезает ровно флаг из любого окружающего текста. \(...\) определяет группу захвата, \1 ссылается на неё, а комбинация -n с флагом p выводит только совпавшие строки. Если в файле сотни строк и лишь одна содержит флаг — этот однострочник выдаст чистый результат без мусора.

Для замены с перестановкой частей: echo '2023-10-15' | sed 's/\([0-9]*\)-\([0-9]*\)-\([0-9]*\)/\3.\2.\1/' превратит 2023-10-15 в 15.10.2023. На тасках с логами иногда нужно привести даты к единому формату для сортировки.

Bash one-liners для CTF: готовые рецепты

Набор однострочников, которые работают практически на каждом соревновании. Каждый решает конкретную задачу из CTF-практики.

Рекурсивный поиск файлов с флагом: grep -rnl 'flag{' . 2>/dev/null-l выводит только имена файлов, без содержимого.

Вытащить все URL из файла: grep -oP 'https?://[^\s"<>]+' файл — Perl-regex ловит HTTP- и HTTPS-ссылки целиком.

Файлы, изменённые за последние 10 минут: find / -mmin -10 -type f 2>/dev/null — на wargames вроде OverTheWire свежий файл почти всегда содержит подсказку или флаг.

Декодировать base64 из файла: cat encoded.txt | base64 -d — для чистого base64. Если файл содержит мусор вперемешку с base64: grep -oP '[A-Za-z0-9+/=]{4,}' файл | base64 -d 2>/dev/null.

Поиск по истории bash: cat ~/.bash_history | grep -i 'pass\|flag\|secret\|key' — на пентесте это Shell History (T1552.003, Credential Access; ранее выделялась как отдельная техника T1139 Bash History, ныне deprecated). На CTF — частый источник флагов на SSH-машинах. Я однажды нашёл так флаг за 10 секунд — предыдущий участник забыл почистить историю.

Рекурсивный поиск приватных ключей: grep -rl 'BEGIN.*PRIVATE KEY' /home/ 2>/dev/null — Private Keys (T1552.004, Credential Access).

Уникальные email из дампа: grep -oP '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}' дамп.txt | sort -u.

Подсчёт ошибок в логе: grep -c 'ERROR\|FATAL\|CRITICAL' app.log-c считает количество совпадений вместо вывода строк.

Найти файлы по расширению и проверить содержимое: find . -name '*.txt' -exec grep -l 'flag' {} + — ищет flag только в .txt-файлах.

grep vs awk vs sed: когда что применять

Утилита Сильная сторона Ограничение Когда использовать Когда не использовать
grep Быстрый поиск по паттерну, рекурсия по директориям Не трансформирует текст, не работает с полями Найти строку с флагом, отфильтровать по ключевому слову Нужно вытащить поле из CSV или заменить подстроку
awk Работа с полями, условная логика, арифметика Избыточен для простого поиска строки Разбор логов, CSV, извлечение колонки, подсчёт Простой поиск подстроки — grep справится быстрее
sed Замена, удаление, трансформация текста на лету Неудобен для сложной условной логики Очистка мусора, декодирование leet-speak, удаление тегов Фильтрация по значению поля или математические условия

Алгоритм выбора: нужно просто найти строку — grep. Нужно вытащить конкретное поле из структурированных данных — awk. Нужно заменить или удалить часть текста — sed. Нужно несколько операций подряд — пайплайн: grep | awk | sed. На практике 90% задач закрываются первыми двумя.

Частые ошибки при поиске строк в файлах linux

Забыли кавычки вокруг паттерна. Команда grep flag{test} file.txt сломается — bash интерпретирует фигурные скобки как brace expansion. Правильно: grep 'flag{test}' file.txt. Одинарные кавычки отключают интерпретацию спецсимволов шеллом.

Не экранировали точку в regex. Точка — это «любой символ», не буквальная точка. Ищете IP 192.168.1.1? Пишите grep '192\.168\.1\.1' с обратными слешами. Без экранирования 192.168.1.1 совпадёт и с 192x168y1z1.

grep по бинарнику без strings. grep пропускает бинарные файлы молча или пишет «Binary file matches». Берите strings файл | grep 'flag' или grep -a 'flag' файл. Первый вариант чище — меньше ложных срабатываний.

Путаница между -E и -P. -E — расширенные POSIX-regex: не нужно экранировать +, ?, {}. -P — Perl-совместимые regex: поддерживают \d (цифра), \w (буква/цифра), \s (пробел), lookahead и lookbehind. Для CTF -P мощнее, но недоступен на macOS без GNU grep. Если работаете на Mac — ставьте через brew install grep и вызывайте как ggrep.

Жадные квантификаторы. Паттерн grep -oP '\{.*\}' на строке {first} и {second} захватит {first} и {second} целиком — .* жадный и забирает максимум. Нежадный вариант: grep -oP '\{.*?\}' — захватит {first} и {second} отдельно. Знак ? после * делает квантификатор ленивым. Это одна из самых коварных ловушек регулярных выражений regex для новичков — на ней сгорают стабильно.

Забыли 2>/dev/null. Рекурсивный grep -rn или find в системных директориях генерирует сотни ошибок «Permission denied». Добавьте 2>/dev/null в конец команды. Без этого полезный вывод тонет в потоке ошибок.

Двойные кавычки вместо одинарных. В двойных кавычках bash раскрывает переменные: grep "$HOME" файл подставит путь к домашней директории, а не будет искать литерал $HOME. Если в паттерне есть $, ! или обратные кавычки — одинарные кавычки, в них bash ничего не интерпретирует.

Пропустили флаг из-за невидимых символов. Иногда флаг содержит непечатаемые символы: \r (carriage return из Windows), нулевые байты, BOM-маркеры. Если grep находит строку, но площадка флаг не принимает — прогоните через cat -A (покажет ^M для \r) или xxd для побайтового просмотра. Очистка: sed 's/\r$//' файл удалит Windows-переносы. Для тех кто в танке — \r это символ возврата каретки, наследие DOS, и он любит ломать всё, что выглядит «нормально» в терминале.

Навыки linux командной строки для CTF — не зазубривание флагов каждой утилиты, а понимание потока данных через пайплайн. Каждая команда делает одно простое действие: grep фильтрует, awk разбирает по полям, sed трансформирует, sort и uniq считают. Мощь — в комбинации этих простых блоков.

По опыту решения тасков на picoCTF, HackTheBox и участия в jeopardy-соревнованиях — 80% forensics- и misc-задач закрываются одним-двумя пайплайнами из трёх-четырёх команд. Оставшиеся 20% требуют Python или специализированные инструменты вроде Volatility и Autopsy. Но даже в этих случаях предварительный strings | grep экономит время, потому что показывает, с чем вообще имеешь дело, прежде чем поднимать тяжёлую артиллерию.

Среди коллег по CTF-команде я вижу устойчивый паттерн: те, кто быстро закрывают forensics, не знают больше команд. Они знают, как комбинировать базовые. Один пайплайн из find, xargs, strings, grep -oP и sort -u покрывает задачу, на которую человек без этого навыка тратит полчаса ручного перебора. Разница между пятью решёнными тасками за соревнование и пятнадцатью — не в количестве выученных утилит, а в скорости сборки пайплайна. Мой совет: не гонитесь за экзотическими инструментами и не пытайтесь осилить тысячестраничный мануал по awk целиком. Отрабатывайте связку: увидел файл — filestringsgrep -oP 'паттерн' — ответ. Доведите её до автоматизма на десяти тасках — следующие двадцать будут решаться вдвое быстрее. Если хочется закрыть базу системно — не только bash, но и сети, веб, криптографию — на codeby.school/ib-basics есть IB Basics, от нуля до первых рабочих задач без академического тона.

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

Поделиться

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

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

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