Современные веб-приложения имеют сейчас довольно сложную структуру. Вместе с этим, для хранения информации стали активно использоваться базы данных на основе языка SQL. При обращении к какой-либо странице, веб-приложение формирует специальный SQL-запрос, который запрашивает в базе данных соответствующую запись. Результат запроса возвращается в веб-приложение, которое, в свою очередь, отображает его на странице браузера для пользователя.

Использовать подобную схему взаимодействия удобно, но вместе с этим, возрастает риск появления SQL-инъекций. Суть их работы заключается во внедрении собственного кода в SQL-запрос к базе данных с целью получить дополнительную информацию от нее.

Пожалуй, наиболее часто встречаются SQLi в SELECT запросах, так как данный тип запросов является самым распространенным, но, все же не ограничивается только им.

SQL-injection встречается:

  • в строковом параметре;

  • в числовом параметре.

Для числового параметра характерна обработка числовых данных, например, в параметре ID:

http://example.com/index.php?id=1

Если параметр не будет иметь фильтрацию, а код выглядит примерно так:

$id = $_GET['id'];

$query = "SELECT * FROM some_table_name WHERE id=$id";

то при эксплуатации SQL-инъекции веб-приложение направит некорректный запрос в базу данных, например:

SELECT * FROM some_table_name WHERE id='1''

и в ответе от веб-приложения пользователь получит ошибку синтаксиса. Но если этого не произойдет, то здесь 2 варианта:

  • SQL-инъекции здесь нет — фильтруются кавычки, или просто стоит преобразование в целочисленный тип int;

  • отключен вывод ошибок.

В строковом параметре ситуация аналогичная, только вместо числового параметра будет строка:

http://example.com/index.php?user=admin'

Статья носит информационный характер. Не нарушайте законодательство.

Основные методы эксплуатации SQL-injection

Union Based SQL-injection — применяется, если SQL-injection возникает в SELECT запросе. Благодаря данному методу можно объединить два SELECT запроса в один набор результатов. Особенность этого метода заключается в том, что он будет работать только в том случае, если количество столбцов, возвращаемых первым запросом, будет равно количеству столбцов, возвращаемых во втором запросе.

Для определения количества столбцов можно воспользоваться 3 методами:

  • добавление нового столбца при каждой итерации проверки, что не совсем удобно, так как столбцов может быть 20,30,50 и т.д.:

?id=1' union select null --

?id=1' union select null,null --

?id=1' union select null,null,null --

и т.д.

Почему используется значение NULL? Все просто, типы данных в каждом столбце должны быть совместимы между исходным и внедренным запросами. Поскольку NULL может быть преобразован во все часто используемые типы данных, его использование увеличивает вероятность успешного выполнения полезной нагрузки при правильном подсчете столбцов.

  • использование команды order by для определение количества столбцов. В этом случае нужно опираться на появление ошибки о несоответствии количества столбцов в запросе. Стоит начать с большого количества столбцов и уменьшать их вдвое при каждой итерации проверки. Если количество столбцов не будет соответствовать фактическому значению, то вернется ошибка

?id=1' order by 20 --

?id=1' order by 10 --

?id=1' order by 5 --

и т.д.

  • использование команды group by, который основывается на обратном методе проверки, в отличие от order by.

?id=1' group by 5 --

?id=1' group by 10 --

?id=1' group by 20 --

Boolean Based SQL-injection — метод эксплуатации слепых инъекций. Информация извлекается исходя из реакции на условные выражения. Инъекция называется слепой в тех случаях, если нет никакой видимой реакции от веб-приложения. Например, при подстановке кавычек в потенциально уязвимый параметр, ошибка, связанная с нарушением логики SQL-запроса, не появляется, а страница отображается без изменений. Но тут, как уже говорилось ранее, несколько вариантов: либо входные параметры фильтруются и уязвимости нет, либо на сервере отключен вывод ошибок на странице.

Пример:

?id=1' AND substring(@@version,1,1)=5 --

Вместе с запросом указывается некоторое условие. Условие может быть как истинным, так и ложным. Если оба условия выполняются одновременно, то, запрос отработает корректно, в противном случае запрос не будет выполнен.

Error Based SQL-injection — данный метод позволяет получить информацию из базы данных в тексте ошибки.

Для поиска такого рода инъекций можно воспользоваться следующими примерами:

?id=0X01

?id=9999999999999999999999999999999999999999999

В первом варианте 0X01 будет являться действительным числом, но в языке PHP, а не MySQL, что вызовет ошибку последнего. Во втором варианте число будет преобразовано в INF, что также будет являться действительным числом для PHP, но не для MySQL.

Time Based SQL-injection — используются команды СУБД (например, sleep) , вызывающие задержку ответа от базы данных. Метод также используется для эксплуатации слепых инъекций, когда отсутствует какой-либо вывод информации, в том числе в случаях, описанных в Boolean Based SQL-injection.

Пример:

?id=1' and sleep(20) --

В этом примере добавляется задержка 20 секунд в виде команды sleep(20) для базы данных, которая создаст задержку перед ответом, что соответственно отразится и на времени ответа веб-приложения. Также для этих целей можно использовать и другие команды, например, benchmark или waitfor:

BENCHMARK(5000000,ENCODE('MSG','by 5 seconds'));
id=1' waitfor delay '00:00:10'  (MS-SQL);
pg_sleep() (PostgreSQL).

Пример, где по длительному ответу от базы данных можно определить, что первый символ значения user_password для пользователя с id=1 будет равняться «2» т.к. char(50) является цифрой 2:

?id=1' UNION SELECT IF(SUBSTRING(user_password,1,1) = CHAR(50),BENCHMARK(5000000,ENCODE('MSG','by 5 seconds')),null) FROM users WHERE user_id = 1;

Команда benchmark, в случае верного условия, выполнит функцию ENCODE 50000 раз, что вызовет задержку ответа, по которой и определяется наличие SQL-инъекции.

Как искать и эксплуатировать SQLi

1. Ручной поиск. Для начала во все параметры подставляем спецсимволы: кавычки, двойные кавычки и т.д. Если на каком-то варианте вернулась ошибка, то инъекция найдена и можно продолжать. Как правило, используется оператор union select, чтобы объединить сразу 2 запроса, а как мы уже знаем, этот метод требует соответствия в количестве столбцов. С помощью order by определяем количество столбцов. После этого можно формировать запрос для получения информации о самой базе данных. Допустим у нас столбцов будет 4, то команда будет выглядеть так:

?id=1' union select null,null,null,null --

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

?id=1' union select version(),user(),@@port,null --

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

?id=1' union select null,null,table_name,null from information_schema.tables --

Указав table_name и добавив from information_schema.tables мы получим список всех таблиц базы данных, включая системные. Это стало возможным благодаря добавлению таблицы information_schema в MySQL 5.0+ и по ее наличию также можно косвенно определить версию используемой БД. Получив список таблиц ищем среди них нужную и получаем из нее информацию. Например, у нас есть таблица users, где хранятся пользовательские данные — имя пользователя, хэш пароля и прочая информация.

?id=1' union select null,null,concat(user(),0x3a,password('user()'),null --

В результате запроса в одном поле будет выведена информация о пользователях БД (логин) и их пароли в виде хеша, используя функцию concat.

2. Автоматизация. Безусловно, навык ручного поиска и эксплуатации SQL-инъекций очень важен и порой незаменим в тестировании на проникновение, однако и время тоже является крайне ценным ресурсом. Для его экономии, особенно при поиске и эксплуатации слепых инъекций, разработаны вспомогательные  инструменты.

Один из них довольно популярен и называется SQLmap. Рассказывать про него особо нечего, так как это, думаю, первая ассоциация при слове SQL-инъекция. Но если вкратце - мощный кроссплатформенный консольный инструмент для автоматизации поиска и эксплуатации SQL-инъекций любого вида и сложности, написанный на языке Python. В основной функционал которого входит:

  • полная поддержка для таких систем управления базами данных, как:  MySQL, Oracle, PostgreSQL, Microsoft SQL Server, Microsoft Access, IBM DB2, SQLite, Firebird, Sybase, SAP MaxDB и HSQLDB;

  • поддержка прямого подключения к базе данных без использования SQL инъекции, посредством введения учётных данных СУБД, IP адреса, порта и имени БД;

  • поддержка перечисления пользователей, хешей паролей, привилегий, ролей, БД, таблиц и колонок;

  • автоматическое распознавание форматов хешей паролей и предложение их взлома с использованием атаки по словарю;

  • поддержка выполнения произвольных команд на ОС сервера БД и получение их стандартного вывода при использовании MySQL, PostgreSQL или Microsoft SQL Server и многое другое.

Основные ключи, которые используются при работе с инструментом:

  • --dbs - поиск и вывод списка баз данных;

  • -D,T,C - указание конкретной базы данных, таблицы и колонок в таблице для дампа информации из них;

  • --level, --risk - параметры для более углубленного тестирования (параметры генерируют больше трафика и могут помочь системам защиты выявить атаку);

  • --random-agent - использование произвольного User-Agent.

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

JSQL injection

Домашняя страница

Инструмент для автоматизации поиска и эксплуатации SQL-уязвимостей. Написан на Java, поэтому является кроссплатформенным, нужно только установить нужную версию Java. Скачать jar-файл можно по ссылке.

В отличие от популярного SQLmap здесь есть графический интерфейс и инструмент в целом может выполнять большинство всех основных действий, необходимых для поиска и эксплуатации SQL-инъекций.

Основные возможности:

поддержка работы с 33 базами данных, например: Access, Altibase, Firebird, MemSQL, MySQL, Oracle, PostgreSQL, Presto, SQLite, SQL Server и т.д.;

  • поддержка различных видов инъекций: нормальные, error-based, stacked-based, blind и time-based;

  • список для введения нескольких целей;

  • чтение и запись файла с помощью инъекции;

  • создание и внедрение веб-шелла и SQL-шелла;

  • полный перебор паролей по хешу;

  • поиск страниц администратора;

  • кодирование и декодирование текста;

  • аутентификация с использованием Basic, Digest, NTLM и Kerberos;

  • прокси-соединение по HTTP, SOCKS4 и SOCKS5.

Судя по набору функций, инструмент в себе собрал не только средство поиска и эксплуатации SQLi, но и функционал Dirb и Hashcat. Лучше их использовать отдельно, но если такой возможности нет, то приходится использовать то, что есть, но об этом чуть позже.

Интерфейс

Интерфейс легкий, незамысловатый и интуитивно понятный. В главном меню указывается URL для проверки  параметров в GET-  или POST-запросах, cookie и прочих заголовках. После указания необходимых параметров автоматически начнется поиск уязвимостей. Если нашлись, то они будут проэксплуатированы, а во вкладке Database отобразится содержимое всей базы данных, где можно посмотреть информацию по любой из предоставленных таблиц.

Просмотр базы данных

Кстати, и SQLmap и JSQL injection поддерживают поиск уязвимостей в достаточно экзотических параметрах как, например, cookie. Но из-за того, что в SQLmap база пейлоадов намного больше, то и результата он, вероятнее, будет добиваться чаще. При тестировании инструментов на одном и том же приложении SQLmap смог подобрать нужный пейлоад командой:

# sqlmap -u http://test.site --cookie='wp_sap=[«*»]' --dbs

а у JSQL injection с этим возникли небольшие сложности, вероятно, из-за не такой обширной базы пейлоадов.

Если удалось получить хэш пароля пользователя из БД, то в инструменте предусмотрен встроенный функционал перебора во вкладке Brute force. Это именно чистый bruteforce т.к. перебор по словарю недоступен и нужно указывать набор символов и их количество. Если сравнивать с SQLmap, то в последнем представлен перебор только по словарю, который может и не так эффективен, как полный перебор, но и не требует таких затрат по ресурсам компьютера и времени.

Перебор пароля по хешу

Есть возможность поиска панели администратора по списку. Разумеется, есть готовый список страниц для поиска, но его можно редактировать и дополнять.

Поиск панели администратора

Следующий набор функций позволяет читать файлы, загружать их, создавать и записывать web- и sql-шеллы на сервере. Все эти действия производятся на основе списка директорий, который необходимо либо прочитать, либо в который записать. Список можно редактировать. Но тут небольшая оговорка — чтобы этот функционал работал, у пользователя, от имени которого веб-приложение обращается к базе данных, должны быть соответствующие привилегии на чтение или запись, используя СУБД. То есть, должны быть привилегии на выполнение команд, например, load_file() или into outfile(). Но чаще всего подобные привилегии пользователю не предоставляются.

Чтение, запись и загрузка файлов

Вкладка Encode позволяет в реальном времени закодировать текст в base16, base32, base58, base64, Url-Encode, Hex,Html-Encode и т.д.

Кодирование

Последняя вкладка Scan позволяет установить список URL-адресов для автоматизации поиска SQL-инъекций. Предоставляемый изначально список можно удалить или отредактировать, добавляя собственные URL-адреса.

Используем WAF

И еще немного про взаимодействие инструментов с WAF. Изначально не заявлено, что оба инструмента позволяют обойти средство защиты, поэтому и тестирование было довольно коротким. Для тестирования использовался только Nemesida WAF Free и анализ запросов на основе сигнатур. Атаки обоих инструментов были успешно заблокированы и отображены в личном кабинете.

Фиксирование атак в личном кабинете

SQLmap

jSQL Injection

Вывод

Риск SQL-инъекций возникает всякий раз, когда программист создает динамический запрос к базе, содержащий введённые пользователем данные. Это значит, что способов предотвращения SQL-инъекций два:

  • не использовать динамические запросы к базе;

  • не использовать пользовательские данные.

Поскольку отказаться от этих двух условий довольно проблематично, то SQL-инъекции были и будут оставаться одними из самых распространенных и опасных уязвимостей веб-приложений. И вот небольшой список рекомендаций для защиты веб-приложений от них:

  • тщательная проверка данных от пользователя перед использованием их в запросе;

  • отключение вывода ошибок на сервере. От SQL-инъекций это не защитит, но затруднит ее поиск и эксплуатацию;

  • периодическое проведение аудита веб-приложения как вручную, так и с помощью инструментов автоматизации (SQLmap, jSQL Injection и т.д.). Также можно доверить этот процесс профессионалам с достаточным опытом и уровнем подготовки;

  • удаление неиспользуемого функционала. Та или иная функция, которая не была вовремя удалена может сыграть на руку злоумышленникам;

  • регулярная установка обновлений;

  • использование средств защиты веб-приложений, например WAF.