Главная / Блог / SQL-инъекции для начинающих: ручной поиск и sqlmap

12 мин.00

SQL-инъекции для начинающих: ручной поиск и sqlmap

SQL-инъекции для начинающих: ручной поиск и sqlmap

На CTF-соревновании в категории web задание за 100 очков выглядело элементарно: форма поиска на учебном интернет-магазине, поле ввода и кнопка. Ребята с опытом решали его за три минуты. Я потратил сорок — копировал payload из чужих writeup, вставлял ' OR 1=1-- в поле и не понимал, почему оно то работает, то нет. Перелом наступил, когда я разобрал каждый символ payload вручную, от одиночной кавычки до UNION SELECT. С тех пор подобные таски решаются быстрее, чем грузится страница с условием. Эта статья — тот самый разбор, которого мне не хватало на старте.

SQL-инъекция в контексте CTF и реальных атак

SQL-инъекция возникает, когда приложение вставляет пользовательский ввод прямо в SQL-запрос без фильтрации. Вместо данных база получает команду — и послушно её выполняет. По OWASP инъекции сидят на позиции A03:2021 в топ-10 критических рисков веб-приложений. По отчёту Vaadata, в 2023 году SQL-инъекции были зафиксированы как CVE 2159 раз. Это не архаичная уязвимость — это ежедневная реальность. Подробнее — в нашем статье о пентест веб-приложений.

В терминах MITRE ATT&CK эксплуатация SQL-инъекции в публичном веб-приложении описывается техникой Exploit Public-Facing Application (T1190, Initial Access). Дальше по kill chain: извлечённые из базы учётные данные попадают под Databases (T1213.006, Collection), а скомпрометированные логины-пароли — под Valid Accounts (T1078, Initial Access / Privilege Escalation). В продвинутых сценариях атакующий закрепляется через SQL Stored Procedures (T1505.001, Persistence) и выводит данные через Exfiltration Over C2 Channel (T1041).

Для CTF-игрока всё это сжимается в одну задачу: найти точку входа, определить тип инъекции, вытащить флаг из базы данных. Большинство веб-тасков начального и среднего уровня на площадках вроде picoCTF и HackTheBox Starting Point содержат именно SQL-инъекции — это фундамент категории web.

Как устроена уязвимость изнутри

Серверный код, который обрабатывает поиск товара. На PHP это часто выглядит так:

$query = "SELECT * FROM products WHERE name = '" . $_GET['search'] . "'";
$result = mysqli_query($conn, $query);

Если пользователь вводит ноутбук, запрос к базе будет корректным: SELECT * FROM products WHERE name = 'ноутбук'. Но стоит ввести ' OR '1'='1' --, и строка запроса превращается в SELECT * FROM products WHERE name = '' OR '1'='1' --'. Условие '1'='1' всегда истинно, двойной дефис -- превращает остаток строки в комментарий — база возвращает все записи таблицы. Данные пользователя перестали быть данными и стали частью управляющей логики запроса.

Корневая ошибка — конкатенация пользовательского ввода с SQL-строкой без параметризации. От языка программирования это не зависит: Python с f-строками, JavaScript с шаблонными литералами, Java со склейкой строк — уязвимы одинаково.

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

Ручной поиск — не прихоть олдскульного пентестера, а необходимый навык. Без него невозможно понять, что делает sqlmap и почему он иногда не находит очевидную инъекцию. Методология PortSwigger Web Security Academy описывает систематический подход к обнаружению SQL-инъекций в пять шагов.

Определяем точки входа

Прежде чем что-то ломать, нужно понять, где приложение принимает данные и передаёт их в SQL-запрос. Очевидные места — параметры в URL (?id=1, ?search=test) и поля форм (логин, пароль, поиск). Менее очевидные, но часто уязвимые — cookies, HTTP-заголовки вроде User-Agent или X-Forwarded-For и даже значения в JSON-теле API-запроса. По данным PortSwigger, SQL-инъекции могут возникать не только в WHERE-части SELECT-запросов, но и в INSERT, UPDATE, в ORDER BY и даже в именах таблиц и столбцов.

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

Первый шаг — отправить символ ' в каждую точку входа. Одиночная кавычка ломает синтаксис SQL-строки, и если приложение уязвимо, сервер вернёт ошибку. В MySQL это будет что-то вроде You have an error in your SQL syntax, в PostgreSQL — unterminated quoted string, в MSSQL — Unclosed quotation mark. Ошибка появилась — точка входа потенциально уязвима.

Boolean-тест: OR 1=1 против OR 1=2

Следующий шаг — проверка через булевы условия. Отправляем ' OR 1=1 -- в параметр и смотрим ответ. Затем — ' OR 1=2 --. Если ответы отличаются (первый возвращает данные, второй — нет), инъекция подтверждена. Суть в том, что 1=1 всегда истинно, а 1=2 всегда ложно — разница в ответах доказывает, что наш ввод обрабатывается как SQL-код.

Проверяем комментариями

Символы комментариев — лучшие друзья при ручном тестировании. Двойной дефис -- работает в MySQL, PostgreSQL и MSSQL. Символ # — в MySQL. Конструкция /* */ — блочный комментарий, работает практически везде. Попробуйте ' -- в поле ввода: если ошибка пропадает (по сравнению с одной кавычкой без комментария), значит комментарий успешно «отрезал» остаток запроса.

Time-based проверка

Когда приложение не показывает ни ошибок, ни разницы в ответах — помогает задержка. Отправьте ' AND SLEEP(5) -- для MySQL или '; WAITFOR DELAY '0:0:5' -- для MSSQL. Если ответ пришёл через пять секунд вместо обычных миллисекунд — инъекция есть, просто она «слепая». Именно этот тип сложнее всего эксплуатировать вручную, и именно здесь sqlmap экономит часы работы.

UNION-based SQL injection: пошаговый разбор

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

Определяем количество столбцов

Оператор UNION требует, чтобы количество столбцов в обоих SELECT-запросах совпадало. Определить число столбцов можно двумя способами.

Способ с ORDER BY: последовательно увеличиваем номер столбца, пока не получим ошибку. ' ORDER BY 1 -- — работает. ' ORDER BY 2 -- — работает. ' ORDER BY 3 -- — ошибка. Значит, в запросе два столбца.

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

Находим отображаемые столбцы

Не все столбцы выводятся на страницу. Чтобы понять, какие из них видны пользователю, заменяем NULL на строки: ' UNION SELECT 'test1','test2' --. Если на странице появилось слово test2 во втором столбце — именно через него можно вывести данные из базы.

Извлекаем данные из базы

Дальше — дело техники. Сначала получаем список таблиц через системную таблицу information_schema.tables:

' UNION SELECT table_name,NULL FROM information_schema.tables WHERE table_schema=database() --

На странице появятся имена таблиц: users, products, orders. Находим интересную таблицу и запрашиваем её столбцы через information_schema.columns:

' UNION SELECT column_name,NULL FROM information_schema.columns WHERE table_name='users' --

Видим столбцы: id, username, password. Финальный шаг — дамп данных:

' UNION SELECT username,password FROM users --

На странице отобразятся логины и хеши паролей (или, в CTF-заданиях для начинающих, пароли открытым текстом). В CTF флаг обычно лежит в отдельной таблице вроде flag или secrets — принцип извлечения тот же.

Это та самая логика, которая стоит за автоматикой sqlmap. Когда вы запускаете --dump, инструмент проделывает ровно эти шаги — просто быстрее и без опечаток в синтаксисе.

Blind SQL injection: когда сервер молчит

Не каждое приложение выводит результаты запроса на страницу. Иногда вы видите только «Товар найден» или «Товар не найден» — без деталей. Это blind SQL injection, и в CTF-заданиях среднего уровня она встречается регулярно.

Boolean-based blind

Принцип: задаём базе вопросы с ответом «да/нет» и анализируем поведение приложения. ' AND SUBSTRING(username,1,1)='a' -- — если первый символ логина равен a, сервер вернёт обычную страницу. Если нет — пустую или с ошибкой. Перебираем символы один за другим, пока не соберём всю строку.

По документации sqlmap на GitHub, алгоритм бисекции (деления пополам) позволяет определить каждый ASCII-символ примерно за семь HTTP-запросов. Вместо перебора всех 128 символов алгоритм делит диапазон пополам: «символ больше 64?» — «да» — «больше 96?» — «нет» — и так далее. Для многобайтовых кодировок запросов потребуется больше, но для латиницы и цифр — стабильно семь.

Time-based blind

Самый медленный, но самый универсальный тип. Здесь нет даже разницы в контенте страницы — есть только задержка. Конструкция ' AND IF(SUBSTRING(username,1,1)='a',SLEEP(3),0) -- заставит сервер ответить с трёхсекундной задержкой, если первый символ — a. Каждый символ — минимум семь запросов по три секунды. Извлечение одного логина из 10 символов занимает около трёх с половиной минут. Дамп целой таблицы вручную — занятие для самых терпеливых (или самых упоротых).

Именно здесь ручной подход упирается в стену, и автоматизация становится не роскошью, а спасением.

Автоматизация SQL-инъекций с sqlmap

sqlmap — open-source инструмент, который автоматизирует обнаружение и эксплуатацию SQL-инъекций. Поддерживает шесть типов инъекций: boolean-based blind, time-based blind, error-based, UNION query-based, stacked queries и inline queries. Работает с MySQL, PostgreSQL, Oracle, MSSQL и кучей других СУБД. Установка — одна команда: git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev. Нужен Python 2.7 или 3.x, никаких дополнительных зависимостей.

Пошаговый дамп базы данных

Процесс эксплуатации с sqlmap — пять последовательных шагов, каждый из которых повторяет ручную логику, описанную выше. Воспроизвожу реальный workflow, который использую на CTF-заданиях и тренировочных стендах вроде DVWA.

# Шаг 1: Обнаружение инъекции и определение типа
python sqlmap.py -u "http://target.com/page?id=1" --batch

# Шаг 2: Перечисление баз данных
python sqlmap.py -u "http://target.com/page?id=1" --dbs --batch

# Шаг 3: Перечисление таблиц в целевой базе
python sqlmap.py -u "http://target.com/page?id=1" -D target_db --tables --batch

# Шаг 4: Перечисление столбцов в целевой таблице
python sqlmap.py -u "http://target.com/page?id=1" -D target_db -T users --columns --batch

# Шаг 5: Дамп данных из конкретных столбцов
python sqlmap.py -u "http://target.com/page?id=1" -D target_db -T users -C username,password --dump --batch

Флаг --batch отвечает на все интерактивные вопросы sqlmap значениями по умолчанию — удобно на CTF, где нужна скорость. Шаги можно комбинировать: --dbs можно добавить к первому запуску, чтобы сразу получить и тип инъекции, и список баз.

Работа с Burp Suite и файлами запросов

Когда инъекция не в GET-параметре URL, а в POST-теле, cookie или заголовке — удобнее перехватить запрос через Burp Suite, сохранить его в файл и передать sqlmap через флаг -r. Формат файла — стандартный HTTP-запрос. В месте инъекции можно поставить символ *, чтобы указать sqlmap точное расположение: Cookie: TrackingId=abc* говорит инструменту тестировать именно значение cookie.

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

Несколько флагов, которые экономят время на соревнованиях:

Флаг Назначение
--level Глубина тестирования (1-5). Уровень 2+ проверяет cookies, уровень 3+ — заголовки
--risk Агрессивность payload (1-3). Уровень 2 добавляет time-based, уровень 3 — OR-based
--technique Ограничение типов: B (boolean), T (time), E (error), U (union), S (stacked)
--banner Получение версии СУБД
--verbose Детализация вывода (0-6). Уровень 3 показывает отправляемые payload
--os-shell Попытка получить командную оболочку ОС через инъекцию

Типичная ситуация на CTF: sqlmap с настройками по умолчанию не находит инъекцию, хотя вручную она подтверждена. Решение — поднять --level до 3 и --risk до 2. Если инъекция в cookie, без --level 2 sqlmap просто не будет проверять этот вектор. Я на это наступал раза три, прежде чем запомнил.

Где sqlmap не справляется

По данным PortSwigger и по опыту использования на реальных проектах, автоматика плохо работает с многошаговыми сценариями (логин, затем переход, затем инъекция), second-order инъекциями (ввод сохраняется и срабатывает позже в другом контексте) и приложениями за агрессивными WAF. Для таких случаев нужен ручной подход или хотя бы гибридная стратегия: автоматика для покрытия поверхности, руки — для сложных точек.

Обход WAF при SQL-инъекции в CTF

На CTF среднего и продвинутого уровня перед приложением часто стоит WAF (Web Application Firewall), который блокирует типовые payload. Ключевые слова UNION, SELECT, OR 1=1 попадают в чёрный список, и прямолинейная инъекция не проходит. Несколько приёмов, которые работают в учебных условиях.

Tamper-скрипты sqlmap

sqlmap поставляется с набором tamper-скриптов, которые модифицируют payload перед отправкой. Флаг --tamper=space2comment заменяет пробелы на /**/, обходя фильтры по пробельным символам. Скрипт between заменяет оператор > на NOT BETWEEN 0 AND, а randomcase чередует регистр ключевых слов (sElEcT вместо SELECT). Комбинация нескольких скриптов через запятую — --tamper=space2comment,between,randomcase — повышает шансы обхода.

Ручные техники обхода

Когда WAF фильтрует конкретные слова, помогают альтернативные конструкции. Вместо UNION SELECT можно попробовать UNION/**/SELECT или UnIoN SeLeCt. Вместо пробелов — табуляцию %09 или перевод строки %0a. Inline-комментарии MySQL /*!50000SELECT*/ выполняются как код, но WAF, который ищет буквальное слово SELECT, может их пропустить.

Флаг --random-agent в sqlmap меняет заголовок User-Agent на случайный из реальных браузеров — помогает, если WAF блокирует запросы с дефолтным юзер-агентом sqlmap (а он палится моментально).

Обход WAF — это всегда гонка вооружений. Конкретная техника работает до тех пор, пока правило фильтрации не обновлено. В CTF задания проектируются с конкретным способом обхода в уму, и часть задачи — его найти. На реальном пентесте обход WAF без авторизации владельца системы — уголовное преступление в большинстве юрисдикций.

Где практиковать SQL-инъекции для начинающих

Теория без практики не стоит ничего. Четыре площадки, на которых можно безопасно отработать всё описанное.

DVWA (Damn Vulnerable Web Application) — классический тренажёр с четырьмя уровнями сложности: low, medium, high, impossible. На low — прямая инъекция через конкатенацию строк, на medium — числовой параметр без кавычек, на high — ограничение через LIMIT 1 и session-переменные. Impossible-уровень показывает правильную защиту через параметризованные запросы. Разворачивается в Docker за минуту.

PortSwigger Web Security Academy — бесплатные лаборатории с SQL-инъекциями всех типов. Каждая лаба — реалистичное веб-приложение с одной конкретной уязвимостью. Есть задания на UNION-based, blind boolean, blind time-based и second-order инъекции. После решения доступен подробный writeup.

SQLi Labs (от Audi-1 на GitHub) — набор из 65 уровней, заточенных исключительно под SQL-инъекции. Каждый уровень — отдельный сценарий: GET/POST параметры, разные типы кавычек, разные техники. Идеально для методичной прокачки навыка.

Формула на бумаге понятна, но механика ручного перебора по-настоящему ощущается только когда сам прогоняешь payload за payload. Готовый стенд с CTF-тасками по SQL-инъекциям есть на HackerLab.pro — российская CTF-платформа с категориями web, crypto, forensics и другими. Нужна регистрация, после неё доступны задачи разных уровней сложности.

Прежде чем решать таски инструментами, пройдите хотя бы пять заданий вручную — через адресную строку браузера или curl. Это формирует интуицию, которую никакой автоматический сканер не заменит.

Девять из десяти новичков, которых я вижу на CTF, делают одну и ту же ошибку: скачивают sqlmap, запускают его на таргете и ждут, что инструмент всё сделает сам. Когда sqlmap не находит инъекцию (а при дефолтных --level 1 и --risk 1 это случается часто), они сдаются и идут читать writeup. Проблема не в инструменте — проблема в том, что они не понимают, что происходит между отправкой запроса и получением ответа. Ручной этап нельзя пропустить. Он формирует понимание, почему payload ' OR 1=1 -- работает на одном стенде и ломается на другом, почему UNION SELECT требует точного количества столбцов и почему time-based blind — это семь запросов на символ, а не один. Без этого sqlmap остаётся чёрным ящиком, а ты — оператором кнопки «старт». За последние два года в CTF-задачи всё чаще встраивают нестандартные фильтры и WAF-правила, которые ломают автоматику. Побеждают те, кто умеет модифицировать payload руками, а не те, кто перебирает tamper-скрипты наугад. Если хочешь не просто «решать таски», а выстроить системное понимание ИБ от базовых концепций до первых практических задач — IB Basics на codeby.school закрывает эту базу за пару месяцев, без академической воды.

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

Поделиться

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

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

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

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

XXE-инъекции: эксплуатация XML в CTF

11 мин.

8

XXE-инъекции: эксплуатация XML в CTF

Пошаговый разбор XXE-эксплуатации для CTF: чтение файлов, blind OOB-exfiltration, обход фильтров через UTF-16 и XInclude с готовыми пейлоадами.

29 СЕНТЯБРЬ, 2026

Reverse engineering в Ghidra: crackme с нуля

12 мин.

13

Reverse engineering в Ghidra: crackme с нуля

Полный разбор crackme в Ghidra: от file и strings до XOR-декодирования пароля и патчинга JNZ. Python-скрипт, anti-reversing техники, stripped-бинарники

28 СЕНТЯБРЬ, 2026

Race Condition в CTF: эксплуатация на практике

12 мин.

9

Race Condition в CTF: эксплуатация на практике

Три типа race condition, single-packet attack за 4 мкс, Turbo Intruder скрипты и разбор реальных CVE. Практическое руководство для CTF-игроков и пентестеров.

28 СЕНТЯБРЬ, 2026