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

13 мин.00

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

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

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

На прошлогоднем CTF, где я менторил команду новичков, случилась показательная штука. Ребята нашли форму логина, набрали admin' в поле username, получили You have an error in your SQL syntax — и зависли на сорок минут. Ошибка базы данных прямо на экране, уязвимость веб-приложения очевидна, а что делать дальше — непонятно. Этот разрыв между «знаю, что SQL-инъекции существуют» и «могу это эксплуатировать» — главная боль начинающих. По данным OWASP, инъекции входят в категорию A03:2021 — Injection и остаются одним из самых массовых классов веб-уязвимостей. Дальше — полный путь от одинарной кавычки до извлечённого флага: механика инъекции в SQL-запрос, ручная эксплуатация, автоматизация через sqlmap и место SQLi в реальной цепочке атаки.

Как работает инъекция в SQL-запрос

Любое веб-приложение с базой данных строит SQL-запросы на основе пользовательского ввода. Вводите логин и пароль — приложение формирует что-то вроде SELECT * FROM users WHERE username = 'ваш_логин' AND password = 'ваш_пароль'. Если разработчик вставляет данные через конкатенацию строк (без подготовленных выражений) — атакующий может изменить саму структуру запроса.

Суть инъекции: вы вводите не данные, а фрагмент SQL-кода, который становится частью запроса. Одинарная кавычка ' — самый базовый тест. Набираете admin', запрос превращается в: SELECT * FROM users WHERE username = 'admin'' AND password = ''. Лишняя кавычка ломает синтаксис, база данных возвращает ошибку. Эта ошибка — первый сигнал: приложение уязвимо, пользовательский ввод попадает в запрос без обработки.

Два ключевых символа, которые нужно запомнить:

  • Двойное тире -- — комментарий в SQL. Всё после него база данных игнорирует. Позволяет «отрезать» хвост оригинального запроса.
  • Точка с запятой ; — завершает текущий запрос и позволяет начать новый (если СУБД поддерживает stacked queries — MySQL по умолчанию не поддерживает через стандартный PHP-коннектор, а вот PostgreSQL и MSSQL — да).

Русскоязычные руководства обычно на этом и останавливаются — «кавычка ломает запрос, вот список типов инъекций». Но для реальной практики нужно понимать, что именно вы контролируете. А контролируете вы всё, что идёт после точки вставки вашего ввода в SQL-запрос. Если ввод попадает в условие WHERE, можно: изменить логику условия (обход авторизации), добавить UNION SELECT (чтение чужих таблиц), вызвать ошибку с полезной информацией (error-based инъекция) или заставить базу «задуматься» на заданное время (time-based blind). Каждый вариант — отдельная техника со своими ограничениями.

Для наглядности: в руководстве PHP Manual по SQL-инъекциям приведён пример с PostgreSQL, где через поле offset злоумышленник вставляет 0; INSERT INTO pg_shadow (usename, usesysid, usesuper, usecatupd, passwd) SELECT 'crack', usesysid, 't', 't', 'crack' FROM pg_shadow WHERE usename='postgres' — и создаёт суперпользователя в базе данных. Одна строка в URL — полная компрометация СУБД.

Обход авторизации SQL: первый приём для начинающих

[Применимо: внешний пентест, CTF, bug bounty — веб-приложение с формой логина и прямой конкатенацией SQL]

Обход авторизации — самый наглядный SQL injection пример, который встречается на каждом втором CTF web задании начального уровня. Представьте форму логина, где приложение выполняет запрос: SELECT * FROM users WHERE username = '$user' AND password = '$pass'.

Если в поле username ввести administrator'--, а пароль оставить пустым, запрос превращается в: SELECT * FROM users WHERE username = 'administrator'--' AND password = ''. Всё после -- — комментарий. Проверка пароля исчезает. База возвращает запись пользователя administrator, и приложение впускает атакующего как админа. Этот пример описывает PortSwigger в своей лаборатории по SQL injection.

Другой классический payload: ' OR 1=1--. Он делает условие WHERE всегда истинным — запрос возвращает все записи таблицы. Но тут есть подвох, о котором PortSwigger предупреждает отдельно: с OR 1=1 нужна осторожность. Если ваш ввод попадает не только в SELECT, но и в UPDATE или DELETE, можно случайно удалить или изменить данные. На CTF это обычно не критично, а вот в bug bounty или на реальном пентесте — катастрофа.

Ограничения техники: обход авторизации через SQLi не работает, если приложение использует параметризованные запросы (prepared statements), хеширует пароль на стороне приложения до формирования запроса, или применяет ORM с параметрами. На современных фреймворках (Django, Laravel, Spring) прямая конкатенация SQL — редкость, но в legacy-системах и самописных CMS встречается постоянно.

UNION-based SQL-инъекция: взлом базы данных через SQL шаг за шагом

UNION-based инъекция — основной рабочий инструмент для извлечения данных на CTF и при пентесте баз данных. Она позволяет «приклеить» к оригинальному запросу свой SELECT и вытащить содержимое произвольных таблиц. Этот тип инъекции чаще всего приводит к захвату флага CTF на соревнованиях.

[Применимо: внешний пентест, CTF — MySQL, PostgreSQL, MSSQL с выводом результатов запроса на страницу]

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

  • ОС: Kali Linux 2024+ или Ubuntu 22.04+ (Windows тоже подходит, но удобнее на Linux)
  • RAM: минимум 2 ГБ для DVWA в Docker, рекомендуется 4 ГБ
  • Софт: Docker и docker-compose — установка через sudo apt install docker.io docker-compose
  • Браузер: Firefox или Chromium с DevTools (F12, вкладка Network)
  • Сеть: всё работает локально, интернет не нужен

Запуск DVWA (Damn Vulnerable Web Application): docker run --rm -it -p 80:80 vulnerables/web-dvwa. Открываете http://localhost, логин admin, пароль password. Security Level ставите на Low в разделе DVWA Security. DVWA — стандартная учебная платформа для SQL-инъекции обучение, её же использует pentest-tools.com в своих руководствах по эксплуатации.

Пошаговая эксплуатация ручной SQL-инъекции

Открываете раздел SQL Injection в DVWA. Перед вами поле User ID и кнопка Submit.

Шаг 1: Подтверждение уязвимости. Вводите 1'. Видите ошибку SQL-синтаксиса — точка инъекции найдена. Вводите 1' OR '1'='1 — если вернулись все пользователи, инъекция подтверждена. На этом этапе вы уже знаете: пользовательский ввод попадает в SQL-запрос без фильтрации.

Шаг 2: Определение количества колонок. Для UNION нужно совпадение числа колонок в оригинальном и добавляемом запросе. Используем ORDER BY: вводите 1' ORDER BY 1--, затем 1' ORDER BY 2--, затем 1' ORDER BY 3--. Когда номер превысит количество колонок — появится ошибка. Если ORDER BY 2 работает, а ORDER BY 3 выдаёт ошибку — в запросе 2 колонки. Этот метод подробно описан в руководстве Acunetix по эксплуатации SQL-инъекций.

Шаг 3: Поиск видимой колонки. Вводите 1' UNION SELECT 1,2--. На странице вместо обычных данных появятся числа 1 и 2 — это позиции, куда можно подставить свои подзапросы. Подставляете version() во вторую позицию: 1' UNION SELECT 1,version()-- — видите версию СУБД (например, 10.1.36-MariaDB).

Шаг 4-6: Извлечение данных. Три payload'а, которые ведут к флагу:

-- Список таблиц в текущей базе
1' UNION SELECT 1,group_concat(table_name) FROM information_schema.tables WHERE table_schema=database()--

-- Колонки таблицы users
1' UNION SELECT 1,group_concat(column_name) FROM information_schema.columns WHERE table_name='users'--

-- Дамп логинов и хешей паролей
1' UNION SELECT 1,group_concat(user,0x3a,password) FROM users--

Функция group_concat() склеивает все результаты в одну строку. information_schema — служебная база данных, которая хранит метаинформацию обо всех таблицах и колонках. 0x3a — hex-код двоеточия, разделитель между полями в выводе. На экране вы увидите дамп вида admin:5f4dcc3b5aa765d61d8327deb882cf99. На CTF в одной из записей будет флаг, а в реальном пентесте — хеши для дальнейшего взлома.

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

Ограничения UNION-based: техника работает только когда результат SQL-запроса отображается на странице. Если приложение использует данные запроса без вывода (например, только проверяет существование записи) — UNION-based бесполезна. Также не пробивает WAF (ModSecurity, Cloudflare, PT Application Firewall), который блокирует ключевые слова UNION и SELECT в параметрах.

Error-based и blind SQL-инъекция

Не всегда результат запроса выводится на страницу. Тут начинаются другие подходы к эксплуатации веб-уязвимостей.

Error-based SQL-инъекция: данные в сообщении об ошибке

Если приложение показывает ошибки базы данных, можно заставить СУБД включить полезные данные прямо в текст ошибки. Классический трюк для MySQL, описанный в руководстве pentest-tools.com: payload с FLOOR(RAND(0)*2) и GROUP BY вызывает ошибку дубликата ключа, а в тексте ошибки оказывается результат подзапроса. Ошибка вида Duplicate entry '10.1.36-MariaDB#0' for key 'group_key' содержит версию СУБД прямо в сообщении.

Метод работает, когда UNION-based инъекция невозможна (данные не отображаются), но ошибки базы всё ещё выводятся пользователю. На практике встречается реже, чем UNION — грамотно настроенные production-серверы показывают пользователю только generic-ответ 500 Internal Server Error, а технические детали пишут в серверный лог. Но самописные CMS и legacy-приложения на PHP 5.x — другая история. Там display_errors = On стоит в продакшене чаще, чем хотелось бы.

Blind SQL-инъекция: бит за битом

[Применимо: внешний пентест, CTF уровня medium+, bug bounty — когда нет прямого вывода данных]

Blind-инъекция — самый муторный тип для начинающих. Приложение не показывает ни данных, ни ошибок. Остаётся наблюдать за косвенными признаками.

Boolean-based blind: вы задаёте базе данных вопросы с ответом «да/нет». Payload 1' AND SUBSTRING(version(),1,1)='5'-- — если страница отображается нормально, первый символ версии равен 5. Если страница пустая или визуально отличается — не 5. Перебираете символ за символом. На извлечение одной строки из 32 символов уходят десятки запросов.

Time-based blind: если даже разницу в содержимом страницы не отследить, используют задержки. Payload 1' AND IF(SUBSTRING(version(),1,1)='5', SLEEP(3), 0)-- — при совпадении сервер ответит с задержкой 3 секунды. Замеряете время ответа и получаете информацию по одному биту за запрос. На CTF начального уровня blind-инъекции встречаются редко, но на PortSwigger Web Security Academy есть отдельные лаборатории по обоим типам — рекомендую пройти после освоения UNION-based.

Ограничения blind: крайне медленная эксплуатация. Извлечение хеша пароля из 32 символов через time-based blind с задержкой 3 секунды займёт минимум 5-10 минут при идеальных условиях. На нестабильном соединении — значительно дольше. Поэтому для blind-инъекций почти всегда используют автоматизацию.

Использование sqlmap для автоматизации SQL-инъекций

Ручная эксплуатация — это понимание механики. На реальном пентесте баз данных пентестер использует sqlmap — открытый инструмент автоматизации SQL-инъекций. Python 2.7/3.x, лицензия GPLv2, репозиторий github.com/sqlmapproject/sqlmap активно поддерживается (CI/CD через GitHub Actions, обновления выходят регулярно).

Установка: git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git sqlmap-dev. Готов к работе без дополнительных зависимостей — нужен только Python.

Базовый сценарий использования sqlmap для DVWA:

python sqlmap.py -u "http://localhost/vulnerabilities/sqli/?id=1&Submit=Submit" \
  --cookie="PHPSESSID=abc123; security=low" \
  --dbs --batch

Разберём флаги: -u — URL с параметром для тестирования, --cookie — куки сессии (без них sqlmap не пройдёт авторизацию в DVWA), --dbs — перечислить все базы данных, --batch — автоматически отвечать на вопросы. После обнаружения баз: флаг --tables -D dvwa покажет таблицы, а --dump -T users -D dvwa выгрузит содержимое. Для усиления поиска: --level 3 --risk 2 расширяет набор проверяемых payload'ов и точек инъекции (включая Cookie и User-Agent).

sqlmap сам определяет тип инъекции (UNION, error-based, boolean blind, time-based blind), подбирает payload, определяет СУБД и извлекает данные. За пять минут он делает то, что вручную занимает тридцать-сорок.

Когда sqlmap не справляется

sqlmap — не волшебная палочка. Конкретные ситуации, где он буксует:

  • WAF. ModSecurity, Cloudflare, AWS WAF — все фильтруют типичные payload'ы sqlmap. Флаг --tamper с модулями обфускации (например, space2comment, charencode) помогает не всегда. На bug bounty с серьёзным WAF sqlmap часто бесполезен без кастомных tamper-скриптов.
  • Нестандартные точки инъекции. JSON в теле POST-запроса, XML-параметры, HTTP-заголовки (X-Forwarded-For, Referer) — sqlmap умеет работать с ними через -p и --data, но автоопределение часто промахивается. Тут нужен Burp Suite: перехватить запрос, сохранить в файл через --request, и натравить sqlmap на конкретный параметр.
  • Кастомная логика приложения. Если параметр проходит через base64-кодирование, шифрование или несколько слоёв обработки до попадания в SQL — sqlmap может не распознать уязвимость.
  • Rate limiting. Агрессивное сканирование sqlmap на реальной цели приводит к бану по IP за считанные минуты. Флаг --delay=1 добавляет задержку между запросами, но замедляет работу в разы.

Золотое правило: сначала руками подтвердите инъекцию (кавычка, ORDER BY), потом запускайте sqlmap с конкретным параметром через -p. Так инструмент работает точнее и значительно быстрее.

SQL-инъекция в цепочке атаки

SQL-инъекция — не изолированный трюк. В реальных атаках она занимает конкретное место в kill chain, и понимание этого контекста отличает CTF-решателя от пентестера.

По классификации MITRE ATT&CK, эксплуатация SQL-инъекции в публичном веб-приложении — техника Exploit Public-Facing Application (T1190, Initial Access). Атакующий получает начальный доступ к данным через публичный интерфейс. Дальше цепочка разворачивается:

  1. Initial Access (T1190): атакующий находит SQLi в форме поиска или параметре URL.
  2. Collection — Databases (T1213.006): через UNION SELECT или sqlmap выгружает таблицы с учётными данными.
  3. Credential Access — Credentials In Files (T1552.001): из дампа извлекает логины и хеши паролей.
  4. Privilege Escalation — Valid Accounts (T1078): взломав хеш через hashcat или john, использует пароль для входа в админку, SSH или другой сервис. По данным Acunetix, в их демонстрационном пентесте WordPress-сайта хеш пароля администратора был взломан через hashcat -m 400 -a 0 (тип хеша phpass/WordPress, dictionary attack) за несколько минут.
  5. Persistence — SQL Stored Procedures (T1505.001): на MSSQL атакующий может активировать xp_cmdshell и получить выполнение команд на уровне ОС. Пример из руководства PHP Manual: через инъекцию на MSSQL выполняется exec master..xp_cmdshell 'net user test testpass /ADD' — создание нового пользователя на сервере.
  6. Impact — Stored Data Manipulation (T1565.001): модификация данных в базе — подмена цен, изменение контента, удаление записей.

Масштаб последствий: по данным Have I Been Pwned, утечка Exploit.In (2016) содержала 593 миллиона уникальных email-адресов с паролями. Значительная часть крупных утечек начинается с SQL-инъекции в публичном веб-приложении. Согласно разбору pentest-tools.com, взлом Sony Pictures и утечка данных Marriott International (500 миллионов записей гостей) тоже включали эксплуатацию SQL-инъекций как один из векторов.

В контексте российского законодательства: если через SQLi утекают персональные данные пользователей — это прямое нарушение ст. 7 ФЗ-152 о конфиденциальности персональных данных, со штрафами и репутационными потерями для оператора.

Защита от SQL-инъекций: чеклист для разработчика

Защита от SQL-инъекций для начинающих разработчиков сводится к одному принципу: пользовательский ввод никогда не должен становиться частью SQL-кода.

Параметризованные запросы (prepared statements) — основной и самый надёжный метод. Вместо конкатенации "SELECT * FROM users WHERE id = " + userInput используется подготовленное выражение с плейсхолдером. СУБД обрабатывает параметр исключительно как данные, а не как код — инъекция невозможна на уровне архитектуры. В PHP это PDO с prepare() и execute(), в Python — DB-API с параметрами, в Java — PreparedStatement. Все современные ORM (SQLAlchemy, Django ORM, Hibernate) используют параметризованные запросы по умолчанию.

Минимальные привилегии учётной записи БД. Если веб-приложение подключается к базе с правами суперпользователя — SQLi приводит к полному контролю. Если у учётной записи только SELECT на нужные таблицы — ущерб ограничен чтением конкретных данных. Разница колоссальная.

Сокрытие ошибок. Ошибки SQL не должны показываться пользователю. Логируйте в серверный лог, а клиенту — generic-ответ. Это отрезает error-based инъекцию полностью.

WAF работает как дополнительный барьер, но не заменяет исправление кода. Опытный атакующий обходит WAF через обфускацию, нестандартные кодировки и альтернативный SQL-синтаксис. Я видел обходы ModSecurity через банальную замену пробелов на /**/ — правило не ловило.

Готовый чеклист:

  1. Все SQL-запросы используют prepared statements с параметрами — никаких конкатенаций.
  2. Учётная запись БД имеет минимальные привилегии (только SELECT/INSERT на конкретные таблицы).
  3. Ошибки SQL не выводятся пользователю — только в серверные логи.
  4. Входные данные валидируются по типу и формату (is_numeric(), регулярные выражения).
  5. WAF настроен и правила обновляются.
  6. Регулярный пентест — ищите прямую конкатенацию пользовательского ввода в SQL-запросах.

За полтора года менторства на CTF я вижу одну и ту же закономерность: новичок, который освоил UNION-based инъекцию руками, потом открывает sqlmap и понимает каждую строчку вывода. А тот, кто начал сразу с sqlmap, застревает на первом нестандартном случае — когда payload нужно подправить вручную, а ты не понимаешь, что он делает. Большинство русскоязычных руководств дают теорию типов инъекций и список мер защиты — между ними зияет дыра. Ни один список «видов SQL-инъекций» не научит считать колонки через ORDER BY или собирать вывод через group_concat(). Это навык, который нарабатывается только повторением на учебных целях: DVWA, PortSwigger Web Security Academy, CTF-площадки. Тренируйтесь руками — автоматизация подождёт. Если хочется не собирать фундамент из разрозненных writeups, а пройти путь от нуля до первых практических задач системно — на IB Basics показывают, что делать в первый месяц после «хочу в ИБ».

Поделиться

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

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

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