Главная / Блог / Python для CTF: автоматизируем рутину от брутфорса до парсинга ответов

13 мин.00

Python для CTF: автоматизируем рутину от брутфорса до парсинга ответов

Python для CTF: автоматизируем рутину от брутфорса до парсинга ответов

Python для CTF: автоматизируем рутину от брутфорса до парсинга ответов

На последнем онлайн-CTF один парень из нашей команды убил 40 минут, вручную вбивая пароли в форму логина — 200 попыток, ноль результата, чистое страдание. Другой закрыл тот же таск за три минуты: накидал скрипт на десять строк с requests, натравил на словарь и получил флаг в терминал. Разница между ними — не объём знаний Python, а конкретный набор приёмов: отправить POST-запрос, вытащить токен из HTML, перебрать значения в цикле. Эти приёмы автоматизации CTF на Python и разберём — с рабочими скриптами, объяснением каждой строки и пометками, когда какой инструмент тащить.

Окружение и библиотеки для автоматизации CTF на Python

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

  • ОС: Linux (Kali, Ubuntu 22.04+) — рекомендовано. macOS — работает. Windows — через WSL2, потому что pwntools на чистом Windows капризничает
  • Python: 3.8 минимум, лучше 3.10+
  • RAM: 2 ГБ минимум, 4 ГБ если хотите параллельно держать Burp Suite и терминал
  • Сеть: нужна для установки пакетов и решения онлайн-CTF; локальные задачи решаются offline

Четыре библиотеки покрывают порядка 90% сценариев автоматизации задач CTF:

Библиотека Для чего Плюсы Ограничения Когда использовать
requests HTTP-запросы, брутфорс форм Простой синтаксис, сессии с куками Нет работы с raw TCP Web-задачи, API, формы логина
BeautifulSoup4 Парсинг HTML-ответов Гибкий поиск по тегам Не исполняет JavaScript Извлечение токенов, флагов, ссылок
pwntools Binary exploitation, TCP-сервисы Упаковка адресов, шеллкод, ROP Полноценно только на Linux PWN, network, бинарные протоколы
hashlib (stdlib) Хеширование Встроен в Python, ставить ничего не надо Только вычисление хешей Crypto-задачи, проверка хешей

Установка занимает минуту. Создаёте виртуальное окружение командой python3 -m venv ctf-env, активируете через source ctf-env/bin/activate (Linux/macOS) и ставите зависимости: pip install requests beautifulsoup4 pwntools. Виртуальное окружение изолирует пакеты — если что-то сломается, удалите папку и начните заново.

По статусу поддержки: requests и pwntools — активно поддерживаемые проекты с регулярными обновлениями и тысячами звёзд на GitHub. BeautifulSoup4 — стабильная библиотека, которая почти не меняется, потому что делает свою работу давно и хорошо. Все три безопасно тянуть в рабочие скрипты.

Брутфорс CTF: скрипт на requests для перебора паролей

Место в цепочке атаки

Брутфорс формы авторизации — техника Password Guessing (T1110.001, тактика Credential Access по MITRE ATT&CK). В CTF она нужна на этапе Initial Access: организаторы дают веб-приложение с формой логина и словарь (или подсказку вроде «пароль — название города»). Задача — подобрать комбинацию и попасть на страницу с флагом. В реальном пентесте тот же приём работает при внутреннем тестировании, когда есть доступ к веб-интерфейсу без WAF и rate limiting.

Механика скрипта

Логика брутфорс CTF-скрипта на Python укладывается в четыре действия: загрузить список паролей, для каждого отправить POST-запрос, проверить ответ, остановиться при успехе.

Три момента, без которых скрипт не заработает:

requests.Session() — объект сессии сохраняет куки между запросами. Без него каждый POST будет восприниматься сервером как новый визитор, и токен авторизации потеряется.

Имена полей формы — их нужно вытащить из HTML. Откройте страницу логина, нажмите F12 в браузере, найдите элемент <form> и посмотрите атрибуты name у полей <input>. Типичные варианты: username и password, но бывают user, login, pass — копируйте точное значение.

Условие успеха — в CTF работает проверка от обратного. Если в теле ответа нет строки «Invalid», «Wrong» или «Error» — скорее всего, вход удался. Альтернатива: проверять response.status_code — код 302 (редирект) после POST обычно означает успешную авторизацию.

import requests

s = requests.Session()
url = "http://ctf.example.com/login"

with open("passwords.txt") as f:
    for pwd in f:
        pwd = pwd.strip()
        r = s.post(url, data={"username": "admin", "password": pwd})
        if "Invalid" not in r.text:
            print(f"[+] Пароль найден: {pwd}")
            break

Скрипт идёт по файлу построчно, убирает переносы строки через strip(), отправляет POST и проверяет ответ. При совпадении — выводит пароль и останавливается. Словарь на 10 000 строк прогоняется за 10–30 секунд в зависимости от задержки сервера.

Когда техника НЕ работает

Rate limiting — сервер блокирует IP после N неудачных попыток. В CTF встречается редко, но бывает. Обход: добавить time.sleep(0.3) между запросами или использовать прокси-ротацию (для CTF обычно избыточно).

CSRF-токен — если форма требует одноразовый токен, скрипт сломается без его предварительного извлечения. Решение — парсинг ответа, разберём в следующем разделе.

JavaScript-рендеринг — если форма работает через AJAX и DOM-манипуляции, простой POST не сработает. Нужен headless-браузер (Selenium, Playwright), но в большинстве CTF формы работают через стандартный HTML.

[Применимо: CTF web-задачи, внутренний пентест без WAF. Не применимо: внешний пентест с Cloudflare/rate limiting.]

Парсинг HTTP-ответов в Python: CSRF-токены и флаги

Зачем автоматизировать парсинг

Большинство web-задач CTF требуют не одного изолированного запроса, а цепочки: GET для получения страницы со скрытым полем, парсинг этого поля, POST с извлечённым значением. Без автоматического парсинга ответов HTTP такую цепочку не замкнёшь в цикле брутфорса. В терминологии MITRE ATT&CK это Automated Collection (T1119) — программный сбор данных из ответов сервера для дальнейшей эксплуатации.

Три метода BeautifulSoup, которые покрывают 95% задач

Библиотека BeautifulSoup принимает HTML-строку и превращает её в дерево объектов с удобным поиском:

  • soup.find("input", {"name": "csrf_token"}) — находит первый <input> с атрибутом name="csrf_token" и возвращает объект, из которого можно вытащить ["value"]
  • soup.find_all("a", href=True) — возвращает список всех ссылок на странице, полезно для краулинга и поиска скрытых эндпоинтов
  • tag.text или tag.get_text() — извлекает текстовое содержимое тега, убирая HTML-разметку

Типичный CTF-сценарий — форма с CSRF-защитой. Без токена POST-запрос вернёт 403 или «Invalid token». Скрипт на Python для CTF решает проблему в два шага: GET для получения токена, POST для отправки данных:

from bs4 import BeautifulSoup
import requests

s = requests.Session()
page = s.get("http://ctf.example.com/login")
soup = BeautifulSoup(page.text, "html.parser")
token = soup.find("input", {"name": "csrf_token"})["value"]

r = s.post("http://ctf.example.com/login", data={
    "csrf_token": token, "username": "admin", "password": "test"
})

При комбинации с брутфорсом этот блок оборачивается в цикл: перед каждой попыткой скрипт заново получает страницу, извлекает свежий токен и подставляет его в POST. Это замедляет перебор вдвое (два запроса вместо одного), но без этого шага брутфорс формы с CSRF-защитой просто не работает.

Извлечение флага из ответа

После успешного входа флаг обычно где-то в HTML. Стандартные форматы: flag{...}, CTF{...}, codeby{...}. Надёжный способ — регулярка: вызов re.search(r'flag\{[^}]+\}', response.text) найдёт флаг в любом месте страницы. Если формат известен из правил соревнования (а он почти всегда указан), такой подход работает быстрее ручного ковыряния в HTML.

Ещё один приём: если флаг спрятан в атрибуте тега (например, <div data-flag="flag{secret}">), BeautifulSoup извлечёт его через soup.find("div")["data-flag"]. Проверяйте не только текст страницы, но и атрибуты элементов — организаторы CTF любят прятать флаги в неожиданных местах.

Pwntools и сокеты: автоматизация pwn-задач CTF

Когда requests уже недостаточно

Категории pwn и network в CTF работают через raw TCP: подключаешься к серверу на конкретном порту, получаешь текстовый или бинарный вывод и отправляешь payload. Библиотека requests тут бесполезна — она заточена под HTTP. Нужен либо стандартный модуль socket, либо pwntools.

По MITRE ATT&CK: работа с сетевыми сервисами через Python — Execution: Python (T1059.006), а эксплуатация уязвимости в удалённом сервисе — Exploit Public-Facing Application (T1190, Initial Access). Скрипты Python для CTF в категории pwn чаще всего нацелены на переполнение буфера, format string уязвимости или логические ошибки в серверном приложении.

Socket vs pwntools: как выбрать

Модуль socket из стандартной библиотеки даёт полный контроль над TCP-соединением: socket.connect(), socket.send(), socket.recv(). Код получается многословным — нужно вручную обрабатывать буферы, следить за кодировками и таймаутами. Для понимания основ — полезно. Для соревнований — слишком медленно.

pwntools оборачивает сокеты в удобный интерфейс. remote("host", port) создаёт TCP-подключение, process("./binary") запускает локальный бинарник. Оба возвращают объект с методами sendline(), recvuntil(), recvall(), interactive(). Плюс pwntools даёт функции упаковки адресов (p32(), p64()), генерации циклических паттернов (cyclic(), cyclic_find()) и класс ELF для анализа бинарников (из документации: ELF(path, checksec=True) — загружает файл, позволяет получить адреса функций, секции, проверить защиты).

Характерный CTF-сценарий: сервер на порту 1337 отправляет математическое выражение и ждёт ответ за 2 секунды. Руками не успеть — нужна автоматизация:

from pwn import *

r = remote("ctf.example.com", 1337)
line = r.recvuntil(b"= ?").decode()
expr = line.split(":")[1].replace("= ?", "").strip()
result = str(eval(expr))  # пример для демонстрации концепции
r.sendline(result.encode())
print(r.recvall().decode())

Шесть строк: подключение, чтение до маркера, извлечение выражения, вычисление, отправка ответа, получение флага. На чистом socket этот код растянулся бы строк на двадцать с обработкой буферов и таймаутов.

Binary exploitation: классы ROP и FmtStr

Для задач на переполнение буфера pwntools даёт класс ROP (из документации: ROP(elfs, base=None, badchars=b'')) для автоматического построения ROP-цепочек. Для format string эксплуатации — класс FmtStr с методами write(addr, data) и execute_writes(). Полный цикл решения pwn-задачи: загрузить бинарник через ELF('./vuln'), проверить защиты через checksec(), найти offset через cyclic() и cyclic_find(), собрать payload и отправить через process() (локально) или remote() (на сервер).

Ограничения pwntools

  • Linux-only для полной функциональности. Модули shellcraft и asm на macOS работают с ограничениями, на Windows — только через WSL2
  • Тяжёлые зависимости: установка тянет десятки пакетов и занимает несколько минут на чистом окружении
  • Не для HTTP: для web-задач requests проще и быстрее. Pwntools — про TCP и бинарники
  • Устаревшие writeup: многие решения в интернете написаны для Python 2 — при копировании проверяйте синтаксис (print как функция, bytes вместо str)

[Применимо: CTF pwn/network, локальная эксплуатация бинарников. Не применимо: web-задачи, задачи с JavaScript-фронтендом.]

Hashlib и itertools: автоматизация crypto-задач

Место в цепочке

Криптографические CTF-задачи часто сводятся к подбору входных данных по известному хешу — это Password Cracking (T1110.002, Credential Access) или Deobfuscate/Decode Files or Information (T1140, Defense Evasion). Типичная формулировка: «дан MD5-хеш, найдите пароль из 5 строчных букв».

Когда Python достаточно

Для коротких паролей (4–6 символов) с быстрыми хешами (MD5, SHA-1) Python-скрипт — оптимальный инструмент. Написать и запустить его быстрее, чем возиться с hashcat, его правилами и масками. Модуль hashlib вычисляет хеш одной строкой: hashlib.md5(b"test").hexdigest(). Модуль itertools.product генерирует все комбинации символов заданной длины.

import hashlib
from itertools import product
from string import ascii_lowercase

target = "5d41402abc4b2a76b9719d911017c592"
for combo in product(ascii_lowercase, repeat=5):
    word = "".join(combo)
    if hashlib.md5(word.encode()).hexdigest() == target:
        print(f"[+] Найдено: {word}")
        break

Скрипт перебирает 26^5 = 11 881 376 комбинаций. На современном CPU это занимает от 10 до 60 секунд — зависит от позиции искомого слова в алфавитном порядке.

Когда Python слишком медленный

Если длина пароля больше 6–7 символов или алфавит включает цифры и спецсимволы — количество комбинаций уходит в миллиарды. Для 8 символов из ascii_lowercase — 208 миллиардов вариантов, Python будет молотить часами. Тут переключайтесь на hashcat с GPU-ускорением или John the Ripper. Для алгоритмов с намеренным замедлением (bcrypt, Argon2) перебор на Python нереалистичен даже для 4 символов — нужны специализированные инструменты и железо.

Отдельная история — кастомные хеш-схемы вроде sha256(md5(password) + salt). Hashcat поддерживает десятки стандартных режимов, но кастомную схему проще реализовать коротким скриптом на Python, чем описывать через hashcat-правила. В CTF кастомные схемы встречаются регулярно — и вот тут скрипты Python для CTF незаменимы.

Как читать чужие CTF-скрипты и строить свои

Чтение writeup — навык не менее важный, чем написание кода

После каждого CTF-соревнования участники публикуют writeup — разборы решений с кодом. Умение быстро прочитать чужой Python exploit скрипт и адаптировать его под новую задачу — навык, который прокачивается только практикой. Три вещи, на которые смотрите в первую очередь:

Импорты — по первым строкам сразу видно категорию задачи. from pwn import * — pwn или network. import requests — web. from Crypto.Cipher import AES — crypto с нестандартной криптографией. Это определяет, какие знания нужны для понимания решения.

Точка взаимодействия с сервером — ищите remote(), process(), requests.post(), socket.connect(). Это центральная часть скрипта: всё остальное — подготовка данных до взаимодействия и обработка ответа после.

Payload — строка или байтовый массив, который отправляется серверу. В pwn-задачах это обычно конструкция из b"A" * offset + p64(address). В web-задачах — словарь параметров в data={}. Понимание структуры payload — ключ к пониманию уязвимости.

Пошаговый процесс решения CTF-таска

Автоматизация задач CTF — это не про знание синтаксиса, а про последовательность действий:

Шаг 1 — Разведка вручную. Откройте задачу в браузере, изучите в DevTools (F12) структуру запросов: какие URL, какие методы, какие параметры. Отправьте тестовый запрос через Burp Suite или curl — убедитесь, что понимаете логику приложения.

Шаг 2 — Минимальный скрипт. Воспроизведите ручной запрос в Python: один вызов requests.get() или requests.post() с теми же параметрами. Сравните ответ с тем, что видели в браузере. Если отличается — копайте в сторону кук, заголовков или User-Agent.

Шаг 3 — Цикл. Оберните запрос в for или while: если нужен брутфорс — перебирайте значения из файла; если нужна цепочка — выстраивайте последовательность GET → парсинг → POST. Добавьте проверку условия успеха.

Шаг 4 — Отладка. Скрипт не находит флаг? Вставьте print(r.status_code, r.text[:300]) после каждого запроса. Частые причины: опечатка в имени поля формы, отсутствие CSRF-токена, редирект, который requests не отрабатывает (попробуйте allow_redirects=False и проверьте заголовок Location).

Шаг 5 — Сохранение. Работающий скрипт — это шаблон для будущих задач. Складывайте решения в папку ~/ctf-scripts/ с именами вида bruteforce_csrf.py, hash_md5_brute.py. Через десять соревнований у вас будет библиотека, которую нужно только адаптировать под новый URL.

Комбинирование библиотек в одном скрипте

CTF-задачи среднего уровня часто требуют объединить несколько приёмов в одном скрипте. Характерный сценарий: GET-запрос получает страницу, BeautifulSoup извлекает закодированный токен, base64.b64decode() раскодирует его, hashlib.sha256() вычисляет контрольную сумму, POST отправляет результат. Четыре библиотеки, один скрипт, один флаг. Переключение между инструментами внутри скрипта — нормальная практика: requests и BeautifulSoup работают на уровне HTTP, hashlib — на уровне данных, pwntools — на уровне TCP. Конфликтов между ними нет, они дополняют друг друга.

CTF scripting tutorial по сути сводится к одной мысли: каждая задача — это последовательность «получить данные → обработать → отправить результат». Разница между категориями — в инструменте на каждом шаге. Web → requests + BeautifulSoup. Pwn → pwntools. Crypto → hashlib + itertools. Forensics → struct + binascii для работы с бинарными форматами. Научитесь выбирать правильный инструмент под задачу — и перестанете убивать 40 минут на ручной перебор паролей.

За год участия в CTF я пришёл к выводу, который многим покажется спорным: новичкам не нужен курс по Python перед первым соревнованием. Нужен один решённый таск. Берёте самую простую web-задачу, открываете документацию requests, пишете скрипт из пяти строк — и вот вы уже понимаете, зачем нужны сессии и что такое POST-параметры. Следующий таск — добавляете BeautifulSoup. Третий — пробуете pwntools. Каждая решённая задача учит конкретному приёму, который невозможно забыть, потому что он привязан к реальному результату, а не к абстрактному упражнению из учебника. Типичная ошибка — пытаться освоить весь инструментарий до старта. Люди неделями изучают декораторы и метаклассы, а потом не могут отправить POST-запрос с куками. Обратный подход работает быстрее: начни с задачи, подтяни ровно столько теории, сколько нужно для решения, двигайся к следующей. Через двадцать тасков у вас будет арсенал из рабочих скриптов и понимание, когда какая библиотека нужна. Если базового Python и понимания HTTP пока не хватает даже для первого шага — на IB Basics можно закрыть эту основу за пару месяцев, с упором на практические задачи.

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

Поделиться

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

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

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