Главная / Блог / SQL-инъекции для начинающих: от кавычки до флага

13 мин.00

SQL-инъекции для начинающих: от кавычки до флага

SQL-инъекции для начинающих: от кавычки до флага

На недавнем CTF-турнире задача за 250 очков в категории web была элементарной: форма логина, два поля, кнопка Submit. Половина команд запустила sqlmap в лоб и словила бан по IP через 30 секунд — rate-limiter отрабатывал жёстко. Те, кто начал с одной кавычки в поле username и прочитал ответ сервера, решили задачу за 8 минут. Разница между первым и вторым подходом — не в инструментах, а в понимании того, что происходит за формой ввода.

SQL-инъекции стабильно сидят в OWASP Top 10 под номером A03:2021 — Injection и остаются самой частой категорией web-тасков на CTF-площадках. По MITRE ATT&CK эксплуатация SQL-инъекции — техника Exploit Public-Facing Application (T1190, Initial Access), которая открывает доступ к базам данных (T1213.006, Collection) и нередко приводит к извлечению учётных данных (T1552, Unsecured Credentials, Credential Access). Бизнес-логика атаки: получить дамп пользовательских паролей, утянуть API-токены, а в предельном случае — выполнить команды ОС на сервере через xp_cmdshell или записать веб-шелл через INTO OUTFILE. На CTF эта цепочка сжата до «найди флаг», но механика — та же самая.

Эта статья — пошаговый разбор sql-инъекций для начинающих, построенный на логике CTF-задач. Никакой академической теории реляционных баз — только паттерны, которые реально встречаются в тасках, и команды, которые дают результат.

Как найти SQL-инъекцию вручную

Прежде чем запускать сканер, нужно понять, куда именно вводить payload. Поиск sql-инъекций вручную — навык, который отличает участника CTF от оператора кнопки «Start Scan». В реальных тасках автоматика часто бесполезна: нестандартные эндпоинты, кастомные фильтры, rate-limiting. Ручной подход — база, без которой автоматизация sql injection не даёт результата. Подробнее — в нашем обзоре пентест веб-приложений.

Точки входа и первый тест на кавычку

Каждый параметр, который уходит на сервер и потенциально попадает в SQL-запрос — точка входа. И это не только видимые поля формы. Согласно методологии PortSwigger, SQL-инъекции возникают в WHERE-условиях SELECT, но также в UPDATE, INSERT, ORDER BY и даже в именах таблиц и столбцов. На CTF чаще всего уязвимы:

  • GET-параметры в URL: ?id=1, ?category=gifts, ?search=test
  • POST-данные форм: поля логина и пароля, строки поиска, фильтры
  • HTTP-заголовки: Cookie, User-Agent, X-Forwarded-For — встречаются в задачах повышенной сложности
  • JSON-поля в теле POST-запросов — частый паттерн в современных web-CTF, где фронтенд шлёт данные через fetch

Первый тест — одинарная кавычка. Вставьте ' в подозрительный параметр и отправьте запрос. Три варианта ответа:

  1. Ошибка SQL в ответе (текст вроде You have an error in your SQL syntax или unterminated quoted string) — сервер подставляет ваш ввод в запрос без экранирования. Прямой сигнал к эксплуатации.
  2. Изменение поведения — страница стала пустой, пропал контент, изменился код ответа. Вероятна инъекция, но ошибки подавлены.
  3. Ничего не изменилось — параметр может быть безопасен, либо инъекция требует другого подхода (числовой параметр, double-кавычки, экранирование).

Для числовых параметров кавычка может не сработать. Попробуйте арифметику: если ?id=2-1 возвращает тот же результат, что ?id=1 — параметр подставляется в запрос как число, и инъекция возможна без кавычек.

Boolean-тест: как найти SQL-инъекцию без видимых ошибок

Если кавычка не вызвала видимую ошибку, переходим к boolean-тесту. Он позволяет определить уязвимость даже когда приложение не показывает сообщений БД. Отправьте два запроса к одному параметру:

  1. ?id=1 AND 1=1 — условие истинно, страница должна отобразиться нормально
  2. ?id=1 AND 1=2 — условие ложно, поведение страницы должно измениться

Если ответы различаются — контент пропал, изменился размер страницы, пропала строка из таблицы — параметр уязвим к boolean-based инъекции. По методологии PortSwigger, систематическое сравнение ответов на OR 1=1 и OR 1=2 — стандартный метод ручной детекции SQL-инъекций.

Для строковых параметров синтаксис отличается: ?category=electronics' AND '1'='1 (истинно) vs ?category=electronics' AND '1'='2 (ложно). Кавычки нужно балансировать, чтобы запрос оставался синтаксически корректным.

Нюанс для CTF: не отправляйте OR 1=1 в запросы, которые могут попасть в UPDATE или DELETE. На соревнованиях с общим сервером можно сломать данные для других команд. Как предупреждает PortSwigger: «Even if it appears to be harmless in the context you're injecting into, it's common for applications to use data from a single request in multiple different queries».

Типы SQL-инъекций в CTF-задачах

В web-CTF задачах встречаются три класса sql-инъекций: in-band (результат запроса виден на странице), blind (результат вычисляется по косвенным признакам) и out-of-band (данные уходят на внешний сервер). Разберём каждый на примерах из реальных тасков.

Union-based SQL injection — прямое извлечение данных

Union-based — рабочая лошадка для большинства CTF. Если приложение выводит результат SQL-запроса на страницу, через UNION SELECT можно подцепить к нему данные из любой другой таблицы. Самый быстрый способ забрать флаг, когда он лежит в БД.

Последовательность действий фиксирована:

Шаг 1 — определить количество столбцов. Сервер вернёт ошибку, если число столбцов в UNION SELECT не совпадает с основным запросом. Два способа:

Метод ORDER BY: отправляете ?id=1 ORDER BY 1, затем ORDER BY 2, ORDER BY 3 — увеличиваете номер, пока сервер не вернёт ошибку. Последнее число без ошибки = количество столбцов.

Метод UNION SELECT NULL: отправляете ?id=1 UNION SELECT NULL, затем UNION SELECT NULL,NULL, затем UNION SELECT NULL,NULL,NULL — пока не исчезнет ошибка. NULL совместим с любым типом данных в большинстве СУБД (MySQL, PostgreSQL, MSSQL), что снимает проблему несовпадения типов. В Oracle для LOB-типов может потребоваться явный CAST.

Шаг 2 — определить отображаемые столбцы. Замените NULL на строки: ?id=-1 UNION SELECT 'a','b','c'. На странице появится одна или несколько букв — именно в эти позиции подставляются payload'ы для извлечения данных. id=-1 — несуществующий ID, который убирает основной результат запроса и оставляет на странице только вывод UNION.

Шаг 3 — извлечь структуру БД. Если отображается второй столбец, запрос для имён таблиц: ?id=-1 UNION SELECT NULL,table_name,NULL FROM information_schema.tables. Далее столбцы: ?id=-1 UNION SELECT NULL,column_name,NULL FROM information_schema.columns WHERE table_name='users'.

Шаг 4 — забрать данные. Финальный запрос: ?id=-1 UNION SELECT NULL,password,NULL FROM users. Или для CTF: ?id=-1 UNION SELECT NULL,flag,NULL FROM secret_flags.

Типичная ошибка новичков: пробовать UNION SELECT без определения числа столбцов. Запрос с неправильным количеством столбцов никогда не сработает. Всегда начинайте с шага 1.

Error-based SQL injection — читаем ошибки сервера

Error-based sql injection работает, когда приложение отдаёт сообщения об ошибках базы данных в HTTP-ответе. Идея: заставить СУБД вывести нужные данные внутрь текста ошибки.

Классический sql payload для MySQL — функция EXTRACTVALUE: ввод ' AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT version()))) -- в уязвимый параметр вернёт ошибку вида XPATH syntax error: '~5.7.38'. Тильда (0x7e) — маркер начала данных, а version() — пример; на его место подставляется любой подзапрос.

Для PostgreSQL работает приём с CAST: payload ' AND CAST((SELECT table_name FROM information_schema.tables WHERE table_schema='public' LIMIT 1) AS INT) -- вернёт invalid input syntax for integer: "users" — название таблицы прямо в тексте ошибки. Красота.

В CTF error-based встречается реже union-based, но решается быстрее — один запрос и данные в ответе. Главное — не пропустить текст ошибки: иногда он спрятан в JSON-поле error, в HTML-комментарии <!-- --> или в HTTP-заголовке ответа. Burp Suite тут спасает — ищите по ключевым словам error, syntax, exception в ответе.

Blind SQL injection: boolean и time-based примеры

Blind sqli — самый мучительный тип для ручной эксплуатации. Приложение не показывает ни данных из запроса, ни ошибок. Единственный канал — косвенные признаки.

Boolean-based blind — вы задаёте вопросы с ответом «да/нет» и посимвольно восстанавливаете данные. Payload для проверки первого символа пароля: ?id=1 AND SUBSTRING((SELECT password FROM users WHERE username='admin'),1,1)='a'. Если страница не изменилась — символ угадан. Изменилась — пробуем следующий. Для ускорения используют бинарный поиск по ASCII-коду: AND ASCII(SUBSTRING(...,1,1))>77 делит пространство символов пополам на каждом шаге, сокращая количество запросов с 95 (число печатных символов) до 7 на символ.

Time-based SQL injection — вместо анализа контента используется задержка ответа сервера. Payload для MySQL: ?id=1 AND IF(SUBSTRING((SELECT password FROM users LIMIT 1),1,1)='a', SLEEP(5), 0). Ответ через 5 секунд — символ угадан. Мгновенный ответ — мимо.

Ручное извлечение данных через blind — занятие для терпеливых (или мазохистов). Пароль из 32 символов через boolean-based при бинарном поиске потребует 32 × 7 = 224 запроса плюс запросы на определение длины строки — итого около 230+. Через time-based с задержкой 5 секунд — это ещё и 18 минут чистого ожидания в лучшем случае. Именно здесь автоматизация sql injection становится не роскошью, а спасением.

Автоматизация с sqlmap для начинающих

sqlmap — open-source инструмент для автоматического обнаружения и эксплуатации SQL-инъекций. Установка: git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev. Работает с Python 2.7 и 3.x на любой платформе.

Ключевое правило: sqlmap — инструмент эксплуатации, а не разведки. Как пишет автор SQLMap Cheat Sheet на highon.coffee: «I personally use SQLMap as an exploitation tool — detection is better done manually or using other detection tools such as Burp Suite scanner». На CTF это правило золотое: сначала руками находите точку инъекции и подтверждаете её одной кавычкой или boolean-тестом, потом отдаёте sqlmap для автоматического извлечения данных. Запуск sqlmap «вслепую» — десятки или сотни запросов, которые на CTF с rate-limiting приведут к бану.

Базовый запуск и ключевые флаги sqlmap

Минимальная команда: python sqlmap.py -u "http://target.com/page?id=1". sqlmap протестирует параметр id на все типы инъекций (по умолчанию — BEUSTQ: boolean, error, union, stacked, time, inline query) и после обнаружения уязвимости предложит дальнейшие действия.

Ключевые флаги для CTF-задач:

  • --dbs — перечислить базы данных на сервере
  • -D имя_базы --tables — показать таблицы в конкретной БД
  • -D имя_базы -T таблица --columns — столбцы таблицы
  • -D имя_базы -T таблица -C col1,col2 --dump — извлечь данные
  • --level=3 --risk=2 — расширенное тестирование; --level определяет количество payload'ов и точек инъекции (на level 2+ тестируются cookie, на 3+ — заголовки вроде User-Agent и Referer); --risk влияет на агрессивность (risk 3 включает OR-based payload'ы, способные повредить данные)
  • --technique=U — ограничить конкретным типом: B(boolean), E(error), U(union), S(stacked), T(time), Q(inline)
  • --dbms=mysql — указать СУБД вручную, чтобы сократить число тестовых запросов
  • -r request.txt — загрузить HTTP-запрос из файла (удобно сохранять из Burp Suite через Save Item)
  • --batch — автоматически отвечать на все вопросы sqlmap (на CTF экономит драгоценные секунды)

Типовой рабочий процесс:

python sqlmap.py -u "http://ctf.example/item?id=1" --dbs
python sqlmap.py -u "http://ctf.example/item?id=1" -D ctf_db --tables
python sqlmap.py -u "http://ctf.example/item?id=1" -D ctf_db -T flag --dump

Три команды — и флаг извлечён, если инъекция прямолинейная. Для POST-запросов: python sqlmap.py -u "http://target/login" --data="username=admin&password=test" или -r с файлом запроса.

Интеграция Burp Suite и sqlmap

На CTF связка Burp + sqlmap закрывает подавляющее большинство web-задач на SQL-инъекции. Рабочий процесс:

  1. Настройте браузер на прокси Burp (по умолчанию 127.0.0.1:8080)
  2. Потыкайте целевое приложение — Burp перехватит все запросы
  3. Найдите интересный запрос в HTTP History, правый клик → Save Item → файл req.txt
  4. Запустите python sqlmap.py -r req.txt

Если инъекция в cookie — sqlmap при --level=2 и выше проверит cookie-параметры автоматически. Для явного указания точки инъекции поставьте * в нужное место файла запроса: Cookie: session=abc; tracking=xyz*. Файл запроса с астериском даёт sqlmap точную информацию о месте инъекции и минимизирует количество тестовых запросов.

Tamper-скрипты для обхода фильтров

На CTF-задачах повышенной сложности встречаются WAF-фильтры и кастомная фильтрация ввода: блокируются пробелы, ключевые слова UNION и SELECT, одинарные кавычки. sqlmap поддерживает tamper-скрипты, которые трансформируют payload перед отправкой.

Часто используемые скрипты:

  • space2comment — заменяет пробелы на /**/ (обход фильтра пробелов)
  • randomcase — рандомизирует регистр: SeLeCt вместо SELECT
  • between — заменяет > на NOT BETWEEN 0 AND
  • charencode — URL-кодирует payload
  • space2hash — заменяет пробелы на %23\n (для MySQL, где # — комментарий)

Подключение: python sqlmap.py -u "http://target/page?id=1" --tamper=space2comment,randomcase. Скрипты указываются через запятую и применяются последовательно.

Tamper-скрипты генерируют заметно больше трафика. На CTF с жёстким rate-limiting добавьте --delay=1 (пауза между запросами) и --timeout=15 (увеличенный тайм-аут). Также полезен --random-agent — некоторые CTF-платформы блокируют дефолтный user-agent sqlmap (sqlmap/1.x...), так что без этого флага можно даже не начинать.

Сквозной разбор: цепочка атаки от обнаружения до флага

Собираем всё в одну последовательность. Задача: URL http://challenge.ctf/products?category=electronics, нужно найти флаг.

Разведка. Открываем страницу — список товаров. Параметр category подозрителен: скорее всего подставляется в WHERE category = '...'. По MITRE ATT&CK это фаза Vulnerability Scanning (T1595.002, Reconnaissance).

Ручной тест. Добавляем кавычку: ?category=electronics'. Страница выдаёт ошибку SQL — инъекция подтверждена. Пробуем ?category=electronics' OR '1'='1' -- — возвращаются все товары, включая скрытые. Инъекция in-band.

Определение столбцов. ?category=electronics' ORDER BY 3 -- — работает. ORDER BY 4 — ошибка. Столбцов три.

Поиск отображаемых позиций. ?category=-1' UNION SELECT 'x','y','z' -- — на странице видны y и z во втором и третьем столбце.

Извлечение структуры. ?category=-1' UNION SELECT NULL,table_name,NULL FROM information_schema.tables -- — видим таблицы products, users, secret_flags.

Получение флага. ?category=-1' UNION SELECT NULL,flag,NULL FROM secret_flags -- — флаг на странице. Шесть запросов — задача решена.

Альтернативный путь через sqlmap. После подтверждения инъекции кавычкой:

python sqlmap.py -u "http://challenge.ctf/products?category=electronics" \
  --technique=U --dbms=mysql -T secret_flags --dump --batch

Флаг --batch для автоответов, --technique=U и --dbms=mysql минимизируют количество запросов — мы уже знаем тип инъекции и СУБД. Без этих подсказок sqlmap отправит 50-100 запросов на фазу детекции, прежде чем начнёт работать. На CTF с rate-limiting это разница между баном и флагом.

Обход авторизации через sql — отдельный частый кейс. Классический payload для формы логина: username admin' --, password — любое значение. Запрос превращается в SELECT * FROM users WHERE username = 'admin' --' AND password = '...', условие пароля комментируется, и вы входите как admin. Этот приём — один из самых ходовых на начальных CTF-задачах.

Предусловия и ограничения описанных техник

Все примеры в статье рассчитаны на MySQL/MariaDB 5.x+ и PostgreSQL 9.x+. Синтаксис information_schema, функции SLEEP(), SUBSTRING(), EXTRACTVALUE() специфичны для этих СУБД. Для Microsoft SQL Server payload'ы отличаются: вместо SLEEP(5)WAITFOR DELAY '0:0:5', вместо LIMITTOP. Oracle не поддерживает information_schema — структуру извлекают через all_tables и all_tab_columns.

sqlmap автоматически адаптируется к СУБД после фазы fingerprinting, но если вы указали --dbms=mysql на PostgreSQL-сервере — получите ложные результаты или не обнаружите инъекцию вовсе. Не уверены в СУБД — не указывайте --dbms, пусть sqlmap разберётся сам.

Тип инъекции Скорость извлечения Видимость для WAF Типичная сложность CTF-таска
Union-based Высокая Высокая Начальная
Error-based Высокая Средняя Начальная-средняя
Boolean-based blind Низкая Низкая Средняя
Time-based blind Очень низкая Низкая Средняя-высокая
Out-of-band Средняя Низкая Высокая

Rate-limiting на CTF-платформах — серьёзное ограничение. Если сервер возвращает HTTP 429 после N запросов, sqlmap без --delay бесполезен. Некоторые площадки блокируют дефолтный user-agent sqlmap (sqlmap/1.x...) — --random-agent решает проблему.

Половина тасков на web-CTF решается SQL-инъекцией того или иного типа. Но я вижу одну и ту же ошибку у новичков: они учат sqlmap, а не SQL. Знание флагов --dbs и --dump не спасёт, когда инъекция в ORDER BY, payload нужно собрать руками, а готового tamper-скрипта для конкретного фильтра не существует. На практике 80% времени уходит на ручную фазу — понять структуру запроса, определить тип инъекции, обойти фильтр — и только 20% на автоматическое извлечение данных. Те, кто пропускает ручной этап, проигрывают не потому что инструмент плохой, а потому что не понимают, чем именно инструмент оперирует.

Вторая системная проблема — непонимание места SQL-инъекции в цепочке атаки. В реальном пентесте SQLi — не финальная точка, а начало: эксплуатация публичного приложения (T1190) ведёт к извлечению паролей из БД (T1552.001), использованию этих учёток для входа в смежные системы (T1078, Valid Accounts), а при наличии прав DBA — к записи хранимых процедур для закрепления (T1505.001, SQL Stored Procedures). CTF упрощает эту цепочку до одного шага, но интуиция формируется только когда понимаешь полную картину. Мой совет: решите хотя бы 15 задач на SQLi руками, без sqlmap. Прочувствуйте разницу между boolean-based и time-based на собственных нервах. После этого sqlmap станет не костылём, а мультипликатором навыка. Если хочешь не просто writeup, а пройти всю атаку самому с лабой на каждый кейс — WAPT на codeby.school/wapt покрывает эту цепочку от ручной инъекции до автоматизации.

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

Поделиться

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

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

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

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

Анализ трафика в Wireshark для CTF: от PCAP до флага

13 мин.

5

Анализ трафика в Wireshark для CTF: от PCAP до флага

Алгоритм решения network forensics CTF: Protocol Hierarchy, display-фильтры, Follow TCP Stream, DNS exfiltration, tshark-пайплайны. С таблицей решений и командами.

11 СЕНТЯБРЬ, 2026

Реверс-инжиниринг в Ghidra для начинающих: шаг за шагом

15 мин.

6

Реверс-инжиниринг в Ghidra для начинающих: шаг за шагом

Пошаговый разбор бинарника в Ghidra: от file и strings до CTF-флага. Навигация, декомпилятор, stripped-бинарники, XOR-паттерны и ошибки новичков.

10 СЕНТЯБРЬ, 2026

Buffer overflow с нуля: перезапись адреса возврата

8 мин.

5

Buffer overflow с нуля: перезапись адреса возврата

Разбираем переполнение стека на конкретном бинарнике: компилируем без защит, находим смещение до адреса возврата в GDB и перезаписываем EIP. Эксплойт на Python.

10 СЕНТЯБРЬ, 2026