Главная / Блог / SQL-инъекции CTF: от определения типа уязвимости до извлечения флага

12 мин.00

SQL-инъекции CTF: от определения типа уязвимости до извлечения флага

SQL-инъекции CTF: от определения типа уязвимости до извлечения флага

SQL-инъекции CTF: от определения типа уязвимости до извлечения флага

На прошлогоднем online CTF попалось задание: форма поиска товаров, один GET-параметр q, ни одной SQL-ошибки в ответе. Вставил ' AND 1=1-- — результаты на месте. Вставил ' AND 1=2-- — пустая страница. Boolean-based blind, MySQL. Через 15 минут ручного перебора подтвердил имя таблицы, переключился на sqlmap с флагом --technique=B — флаг получен за три минуты. Трое участников из чата на этом таске застряли: запускали sqlmap без параметров, инструмент слал сотни запросов и ничего не находил. Проблема не в инструменте — без ручной разведки автоматизация слепа. Умение определить тип SQL-инъекции и выбрать технику эксплуатации — вот что отделяет решённые CTF-задания от зависших.

Место SQL-инъекции в CTF и цепочке атаки

В CTF-заданиях категории Web SQL-инъекция — одна из самых частых уязвимостей. По классификации OWASP Top 10 инъекции сидят на позиции A03:2021 — Injection и стабильно входят в тройку критичных рисков веб-приложений. В реальных атаках SQL-инъекция используется на этапе Initial Access — техника Exploit Public-Facing Application (T1190) по MITRE ATT&CK. Дальнейшее извлечение данных из базы — Collection, конкретнее — Databases (T1213.006). Подробнее — в нашем обзоре пентест веб-приложений.

Цепочка в CTF-задании компактнее, чем на реальном пентесте, но методология та же:

  1. Разведка — найти точку входа (форму, параметр URL, Cookie)
  2. Определение типа — error-based, UNION-based или blind
  3. Эксплуатация — извлечь структуру БД (таблицы, столбцы)
  4. Извлечение данных — достать флаг из целевой таблицы

SQL-инъекция возникает, когда пользовательский ввод попадает в SQL-запрос без валидации и экранирования. Сервер интерпретирует часть ввода как SQL-код, а не как данные. В CTF это обычно GET/POST-параметр, реже — Cookie или HTTP-заголовок типа User-Agent и Referer.

Определяем тип SQL-инъекции: алгоритм для CTF

Первое действие в CTF-задании — понять, с каким типом инъекции работаешь. От этого зависит выбор техники и скорость решения. Алгоритм ниже закрывает большинство CTF-тасков:

Шаг Действие Что наблюдаете Тип инъекции
1 Вставить ' или " в параметр SQL-ошибка в ответе Error-based
2 Подставить AND 1=1 и AND 1=2 Разный контент страницы Boolean-based blind
3 Подставить AND SLEEP(5) Ответ задержан на 5 секунд Time-based blind
4 Попробовать UNION SELECT NULL,... Дополнительные данные в выводе UNION-based

Порядок важен: начинайте с простого и двигайтесь к сложному. Получили ошибку на первом шаге — уже повезло.

Error-based и UNION-based SQL injection

Error-based — самый информативный тип. Если приложение показывает SQL-ошибку при вводе ', вы получаете сразу несколько подсказок: тип СУБД, фрагмент запроса, иногда имена таблиц. На SQLite (частый выбор для лёгких CTF-тасков) ошибка выглядит как near "'": syntax error. На MySQL — You have an error in your SQL syntax. По тексту ошибки сразу определяете синтаксис для дальнейших пейлоадов.

Ограничение: error-based работает только если приложение отображает ошибки. В заданиях среднего уровня и выше ошибки подавлены — переходите к blind-методам.

UNION-based применима, когда результат SQL-запроса отображается на странице — список товаров, профиль пользователя, результаты поиска. Согласно документации PortSwigger, для работы UNION-атаки должны выполняться два условия: количество столбцов в UNION SELECT совпадает с оригинальным запросом, и типы данных столбцов совместимы. Самый быстрый путь к флагу, когда он применим.

Blind SQL injection: boolean-based и time-based

Blind-инъекции встречаются в CTF чаще всего на заданиях средней сложности. Согласно описанию OWASP, blind SQL injection — тип атаки, при котором база отвечает на вопросы «да/нет», а атакующий восстанавливает данные по поведению приложения.

Boolean-based blind: страница меняется в зависимости от истинности условия. Классическая проверка — ?id=1 AND 1=1 показывает нормальную страницу, а ?id=1 AND 1=2 возвращает пустую или изменённую. Есть разница — можно задавать базе любые вопросы и получать ответы через поведение страницы.

Time-based blind: никакой разницы в контенте и HTTP-коде, но при добавлении AND SLEEP(5) (MySQL) или AND pg_sleep(5) (PostgreSQL) ответ приходит с задержкой. Самый медленный, но самый надёжный метод — работает даже когда всё остальное заблокировано.

Ограничение time-based blind: извлечение одного символа требует одного HTTP-запроса с задержкой. Для 32-символьного флага это больше двух минут чистого ожидания. В CTF с ограничением по времени ручная эксплуатация time-based нецелесообразна — сразу переходите к sqlmap.

UNION-based SQL injection: пошаговое извлечение данных

Основная техника для CTF-заданий начального уровня, и именно здесь у новичков чаще всего ломается понимание. Разберём полный цикл.

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

  • ОС: Kali Linux, Ubuntu 20.04+, macOS или Windows через WSL
  • Python 2.7 или 3.x (для sqlmap)
  • sqlmap: установка через git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git (проект активно поддерживается, коммиты каждую неделю)
  • Burp Suite Community Edition — для перехвата и повторной отправки запросов
  • curl — для быстрых ручных проверок из терминала
  • RAM: 2 ГБ достаточно для всех перечисленных инструментов
  • Сеть: доступ к целевому CTF-серверу (online-режим)

От количества столбцов до дампа данных

Определяем количество столбцов. Два метода, оба рабочих:

Метод ORDER BY — подставляете ' ORDER BY 1--, затем ' ORDER BY 2--, ' ORDER BY 3-- и далее. Столбец в ORDER BY задаётся по индексу, имена знать не нужно. Когда номер превысит реальное количество столбцов — получите ошибку вида The ORDER BY position number N is out of range. Последний успешный номер = количество столбцов.

Метод NULL — подставляете ' UNION SELECT NULL--, потом ' UNION SELECT NULL,NULL--, потом ' UNION SELECT NULL,NULL,NULL--. Когда количество NULL совпадёт с количеством столбцов — ошибка исчезнет. NULL используется потому, что он совместим с любым типом данных, как отмечает PortSwigger в документации по UNION-атакам.

На MySQL после -- нужен пробел или символ # для комментария. На Oracle каждый SELECT требует FROM, поэтому синтаксис: ' UNION SELECT NULL FROM DUAL--. Мелочь, но на CTF именно на таких мелочах теряют время.

Находим строковые столбцы. Допустим, в оригинальном запросе три столбца. Нужно определить, какие из них принимают текстовые данные — именно через них вы будете вытаскивать информацию.

' UNION SELECT 'test',NULL,NULL--
' UNION SELECT NULL,'test',NULL--
' UNION SELECT NULL,NULL,'test'--

Если столбец не поддерживает строки — ошибка Conversion failed when converting the varchar value 'test' to data type int. Нет ошибки и на странице появилось слово test — столбец подходит. Запоминаете его позицию.

Извлекаем данные. Зная позицию строкового столбца (допустим, второй), переходим к разведке структуры базы. Для MySQL, PostgreSQL и SQLite используется information_schema:

' UNION SELECT NULL,table_name,NULL FROM information_schema.tables--
' UNION SELECT NULL,column_name,NULL FROM information_schema.columns WHERE table_name='flags'--
' UNION SELECT NULL,flag,NULL FROM flags--

Первая строка показывает все таблицы в базе. Вторая — столбцы целевой таблицы. Третья — сами данные с флагом. В CTF-заданиях таблица с флагом обычно называется flag, flags, secret или users, а столбец — flag, value или password.

Если нужно вывести два поля через один столбец (например, username и password), используйте конкатенацию: на MySQL — CONCAT(username,':',password), на PostgreSQL и Oracle — username || ':' || password.

Ограничение UNION-based: техника не работает, если результат запроса не отображается на странице, если приложение фильтрует слово UNION, или если information_schema недоступна. Некоторые CTF-задания на SQLite используют sqlite_master вместо information_schema. Для SQLite запрос к метаданным: ' UNION SELECT NULL,sql,NULL FROM sqlite_master--.

Blind SQL injection: ручная эксплуатация

Boolean-based: посимвольное извлечение

Принцип boolean-based blind — формируете условие, которое проверяет конкретный символ в конкретной позиции. Ответ «да» (нормальная страница) или «нет» (изменённая) говорит, угадали ли вы.

Чтобы узнать первый символ пароля пользователя admin: ?id=1 AND SUBSTRING((SELECT password FROM users WHERE username='admin'),1,1)='a'. Страница нормальная — первый символ a. Пустая — пробуете b, c и далее. Функция SUBSTRING в MySQL принимает три аргумента: строку, позицию и длину.

Оптимизация: вместо полного перебора алфавита — бинарный поиск. Отправляете SUBSTRING(...,1,1) > 'm' — если «да», символ в верхней половине алфавита, если «нет» — в нижней. За 6-7 запросов определяете один символ вместо 26. Разница между «решил за 10 минут» и «сидел час».

На практике в CTF вручную стоит проверить первые 2-3 символа, чтобы подтвердить: инъекция работает, имя таблицы и столбца угаданы правильно. После этого — переключаетесь на sqlmap для полного дампа.

Time-based: задержка как единственный канал

Time-based используется, когда ни контент страницы, ни HTTP-код не меняются при разных условиях. Единственный индикатор — время ответа.

Для MySQL: ?id=1 AND IF(SUBSTRING((SELECT password FROM users LIMIT 1),1,1)='a', SLEEP(5), 0). Первый символ a — ответ приходит через 5 секунд. Нет — мгновенно.

Для PostgreSQL: pg_sleep(5) вместо SLEEP(5). Для MSSQL: WAITFOR DELAY '0:0:5'. Тип СУБД определяет синтаксис задержки — этот момент часто сбивает новичков. Пейлоад для MySQL не сработает на PostgreSQL, и наоборот.

Ограничение: time-based даёт ложные срабатывания при нестабильном соединении. Сервер отвечает с джиттером 2-3 секунды — увеличьте задержку до 10 секунд. На локальных CTF-стендах проблема минимальна, на удалённых серверах — регулярна.

Автоматизация через sqlmap

Ручной подход vs sqlmap

Критерий Ручная эксплуатация sqlmap
Скорость извлечения Минуты на символ (blind) Автоматизированный перебор
Понимание уязвимости Глубокое — видите каждый запрос Поверхностное — тулза работает за вас
Обход нестандартных фильтров Гибко — адаптируете пейлоад Через tamper-скрипты (не всегда хватает)
Объём трафика Минимальный Сотни и тысячи запросов
Когда использовать Разведка, подтверждение инъекции После подтверждения — дамп данных
Когда НЕ использовать Дамп базы из сотен записей Без предварительной ручной разведки

Совет из практики CTF: sqlmap — инструмент эксплуатации, а не обнаружения. Сначала руками подтверждаете инъекцию, определяете тип и СУБД — потом передаёте sqlmap конкретные параметры. Чем больше информации дадите, тем меньше запросов он отправит и тем быстрее получите результат. Запускать sqlmap вслепую — как сканировать nmap'ом весь интернет в надежде найти свой таргет.

Ключевые флаги и пошаговый workflow

Допустим, вы подтвердили boolean-based blind инъекцию в GET-параметре id, СУБД — MySQL. Полная последовательность:

# Шаг 1: подтверждение инъекции и fingerprint СУБД
python sqlmap.py -u "http://target.ctf/page?id=1" --dbms=mysql --technique=B --batch
# Шаг 2: получаем список баз данных
python sqlmap.py -u "http://target.ctf/page?id=1" --dbms=mysql --technique=B --dbs --batch
# Шаг 3: получаем таблицы конкретной БД
python sqlmap.py -u "http://target.ctf/page?id=1" --dbms=mysql -D ctf_db --tables --batch
# Шаг 4: дампим таблицу с флагом
python sqlmap.py -u "http://target.ctf/page?id=1" --dbms=mysql -D ctf_db -T flags --dump --batch

Флаг --batch отвечает на все вопросы по умолчанию — в CTF экономит время, когда не хочется подтверждать каждое действие вручную.

Разбор ключевых флагов:

--technique=BEUSTQ — выбор техники: B (boolean), E (error), U (union), S (stacked), T (time), Q (inline). Тип инъекции известен — укажите одну букву, это сократит количество запросов в разы.

--level — от 1 до 5. На level 1 проверяются только GET/POST. На level 3 — Cookie и User-Agent. На level 5 — все заголовки. Для CTF хватает 2-3.

--risk — от 1 до 3. Risk 3 включает OR-based пейлоады, которые могут изменить данные. В CTF обычно безопасно, на реальном пентесте — осторожно.

--string и --not-string — для boolean-based blind: указываете текст, который появляется при истинном условии. Ускоряет детект.

-r request.txt — загрузка HTTP-запроса из файла Burp Suite. Полезно для POST-запросов со сложными параметрами и CSRF-токенами. В Burp Suite достаточно щёлкнуть правой кнопкой по запросу и выбрать «Save item».

--tamper — подключение скрипта для обхода фильтров. Подробнее — в следующем разделе.

Если sqlmap не находит инъекцию, хотя вы подтвердили её руками: повысьте --level до 5 и --risk до 3, явно укажите точку инъекции через * в значении параметра (-u "http://target.ctf/page?id=1*"), подключите tamper-скрипт, или передайте запрос через файл с -r.

Ограничения техник и обход фильтров в CTF

CTF-задания среднего и высокого уровня намеренно ставят преграды. Вот основные и способы их обхода.

Фильтрация ключевых слов. Приложение блокирует UNION, SELECT, SLEEP. Обходы: смешанный регистр (uNiOn SeLeCt) — работает против case-sensitive фильтров; двойная запись (UNUNIONION SELSELECTECT) — работает против фильтров, удаляющих слово однократно; комментарии внутри слов (UN/**/ION SEL/**/ECT) — работает против простых regex-фильтров; URL-кодирование (%55NION %53ELECT).

Фильтрация пробелов. Замена пробела на /**/, табуляцию %09, перенос строки %0a, или скобки: UNION(SELECT(1),(2),(3)). В sqlmap для этого есть tamper-скрипт space2comment.

Фильтрация кавычек. Если ' и " заблокированы — используйте числовые пейлоады: ?id=1 AND 1=1 вместо ?id=1' AND '1'='1. Для строковых значений — функция CHAR(): CHAR(97,100,109,105,110) вместо 'admin'.

Tamper-скрипты sqlmap для обхода фильтров: --tamper=space2comment заменяет пробелы на /**/, between заменяет > на BETWEEN, randomcase рандомизирует регистр. Комбинируйте несколько: --tamper=space2comment,between,randomcase. Универсального обхода нет — подбирайте комбинацию под конкретный фильтр.

Rate limiting. Некоторые CTF-платформы ограничивают количество запросов. В sqlmap используйте --delay=1 (пауза 1 секунда) и --threads=1 (один поток). В ручном режиме это не проблема — вы и так отправляете запросы медленнее любой автоматизации.

Чеклист: SQL-инъекция в CTF от начала до флага

  1. Перечислить все входные точки: GET/POST параметры, Cookie, заголовки (User-Agent, Referer, X-Forwarded-For)
  2. Вставить ' и " в каждый параметр — проверить ответ на SQL-ошибки
  3. По тексту ошибки определить СУБД: MySQL, PostgreSQL, SQLite, MSSQL
  4. Если ошибок нет — проверить AND 1=1 vs AND 1=2 на разницу в контенте (boolean-based)
  5. Если контент не меняется — проверить AND SLEEP(5) на задержку (time-based)
  6. Для UNION-based: определить количество столбцов через ORDER BY N
  7. Найти строковые столбцы через UNION SELECT 'test',NULL,...
  8. Извлечь список таблиц: information_schema.tables (MySQL/PostgreSQL) или sqlite_master (SQLite)
  9. Извлечь столбцы целевой таблицы из information_schema.columns
  10. Дампнуть данные: UNION SELECT column FROM table
  11. При наличии фильтров — применить обход: регистр, двойная запись, комментарии, кодирование
  12. Для автоматизации: python sqlmap.py -u URL --dbms=... --technique=... --dump --batch

SQL-инъекции в CTF часто воспринимают как «уже разобранную тему» — мол, 2025 год, OWASP предупреждает двадцать лет, параметризованные запросы изобрели, проехали. Но на каждом CTF-турнире вижу одну и ту же картину: половина участников запускают sqlmap с голым URL и ждут чуда, вторая — вручную перебирают символы в time-based без бинарного поиска. Обе стратегии проигрышные. Ручная разведка без автоматизации — медленно, автоматизация без разведки — слепо. Мой подход к CTF-таскам по Web простой: потратить 5-10 минут на ручное определение типа, СУБД и точки входа, а потом отдать собранные параметры sqlmap. Этот навык переносится на реальный пентест один в один: ручная sql инъекция через curl или Burp Suite подтверждает уязвимость, а sqlmap с конкретными флагами делает тяжёлую работу по извлечению данных. И ещё момент, который редко обсуждают: умение читать SQL-ошибки и понимать структуру запроса формируется только через ручную практику — никакой инструмент его не заменит. Если только начинаешь и хочешь закрыть базу системно — IB Basics на codeby.school ведёт от основ сетей и ОС до первых задач по веб-уязвимостям, без хаотичного сёрфинга по YouTube.

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

Поделиться

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

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

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