Современные веб-приложения имеют сейчас довольно сложную структуру. Вместе с этим, для хранения информации стали активно использоваться базы данных на основе языка 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.
Almatyn
Bind variables are the best way to prevent SQL injection.