Hacked Me
Главная
Регистрация
 Administrator ICQ 399114574
По умолчанию Новая альтернатива Benchmark'y или эффективный blind SQL-injection


Новая альтернатива Benchmark'y или эффективный blind SQL-injection


В статье Вам будет показан новый и универсальный способ обходиться без использования benchmark в blind sql-inj
в так называемых в народе "неюзабельных" бажных запросах как UPDATE, DELETE, REPLACE, UPDATE и прочих.

Найденный Альтернативный способ позволяет именно ПОЛУЧАТЬ требуемую информацию из базы данных, а не банально модифицировать записи.

Будут описаны все особенности известных способов атак SQL-inj, применимых к перечисленным выше операторам.

А также мы подробно рассмотрим аспекты практического использования бенчмарка при написании эксплойтов.

Примечание: в статье приведены примеры для mysql, однако опиcанное сработает практичеcки на всех субд(с поправкой на синтаксис запросов, разумеется).



[1] INTRO

Программисты умнеют, багов в селект-запросах становится всё меньше, а проблема инъекций в
INSERT,UPDATE,REPLACE,DELETE и прочих остаётся актуальной.
Часто вижу вопросы на форумах: "..подскажите, что мне делать, если - 'mysql error INSERT INTO table_name VALUES(...'"
Видя такую ошибку, новичок в хаке закроет браузер, любитель постит сотый вопрос на форумах...
В ответ как правило тишина, либо советы которыми он не в силах воспользоваться,
поскольку раскрутка blind sql, ввиду объёма запросов, требует применения автоматизации, то есть умения писать эксплоит.

Способы давно известны и подробно расписаны 1dt.w0lf ещё за 14 ноября 2004, не читал её только ленивый:
http://www.securitylab.ru/contest/212099.php
Данную статью можно считать её продолжением.



[2] INSERT и другие
описаны известные атаки на insert\update и тд, кто знает - пропускаем

Что можно из этого выжать?

1) (случай общий)Модификация данных без возможности просмотра результата
Идеальный случай - sql-inj в имени таблицы. Здеcь возможно всё:
Код:
INSERT INTO [SQL] VALUES ('stat','stat2');

Добавление нового пользователя:
Код:
INSERT INTO mysql.user (user, host, password) VALUES ('newadmin', 'localhost', PASSWORD('passwd'))/* VALUES ('stat');


2) DoS-атака
Но подобные sql-inj в 99% присутствуют в имени столбца, что не позволяют изменить модифицируемую таблицу на полезную нам.
Код:
INSERT INTO table VALUES ('stat','[SQL]');

А раз так, то кроме порчи\засорения\dosa бд мы модификацией данных ничего не добьемся.
SQL:
Код:
INSERT INTO table VALUES ('stat','bla'),('test', 'demo');

DoS:
Код:
INSERT INTO table VALUES ('stat','bla' and BENCHMARK(10000000,BENCHMARK(10000000,md5(now()))) ) /*');


3) Разделение запросов в MSSQL, PostgreSql, Oracle
Что же объединяет эти три популярные субд?
Такая замечательная вещь, как поддержка разделения запросов, используя точку с запятой, перевод каретки и прочее.
Таким образом мы можем добавить абсолютно ЛЮБОЙ sql-запрос, отделив его ";" от основного.
Что можно полезного вставить - ищем в гугле под конкретную бд.

4) Модификация данных с возможностью просмотра результата
Здесь уже не важно, где инъекция - в имени таблицы или столбца.
Мы можем увидеть результат выполнения запроса, пускай и косвенно.
А значит провести атаку не проблема - чисто технические особенности выяснения результата.
Пример: пусть веб-приложение ПУБЛИЧНО ведёт статистику посещений и есть уязвимость в столбце с User-Agent.
Тогда, результат выполнения sql-inj мы можем наблюдать на паге вывода статистики посещаемости.

5) Использование BENCHMARK
Мы используем BENCHMARK, что позволяет по времени ответа сервера определить результат выполнения запроса.
Этот запрос будет для if положителен и ответ от сервера придёт сразу:
Код:
INSERT INTO table VALUES ('stat','1' and 1=if(ascii(lower(substring((select users from mysql.user limit 1),1,1)))>=1,1,benchmark(999999,md5(now()))) )/* ,'stat2');

А этот - отрицателен и на исполнение бенчмарка уйдёт время, потому ответ от сервера придёт сразу:
Код:
INSERT INTO table VALUES ('stat','1' and 1=if(ascii(lower(substring((select users from mysql.user limit 1),1,1)))>=254,1,benchmark(999999,md5(now()))) )/* ,'stat2');


Разумеется, вместо INSERT может стоять любой другой оператор, в том числе и SELECT, ведь с ним тоже бывают cложные случаи инъекций.



[3] Bencmark для хакера
особенности эксплуатации benchmark, кто знает - пропускаем

Применение бенчмарка это своего рода переход ко второму измерению.
От измерения разности получаемой информации к измерению разности времени на исполнение запроса.

Если кто-то сомневается, что под бенчмарк вообще пишут эксплойты - пробегитесь по багтракам и уверьтесь в обратном - они есть, правда не много.

В реализации сплойт на бенчмарке чуть-чуть сложнее чем для слепого посимвольного брута.

Особенности benchmark:

- пожалуй самая важная деталь - бенчмарк создаёт серьёзную нагрузку на процессор сервера.
Причём эта нагрузка длиться практически постоянно во время работы эксплойта.
Администратор может не смотреть логи обращений\ошибок, но вполне может поинтересоваться, почему сервер притормаживает и в top->P "mysqld" занимает первое место...

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

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

- параметр измерения производительности, то есть количество итераций, в нашем примере - 999999
benchmark(999999,md5(user())
По личному опыту, с момента написания статьи 1dt.w0lf'ом это число изменилось почти на порядок вместе с ростом производительности современных серверов.
В эксплойте желательно делать автоподстройку этого параметра для универсальности, добиваясь приемлемого времени ответа....

- и соответственно, подобрав число в benchmark'e нужно задать таймаут ответа - timeout
Это будет среднее арифметическое от времени выполнения true\false запроса.

- Замечу: при написании эксплойта учитывайте, что по статистике общее число "неверных" запросов превышает общее число верных,
а значит нужно поставить бенчмарк-задержку так, чтобы она срабатывала при ВЕРНОМ ответе. Вот так:
Код:
if( ?, true, false ) 1 = if( 1=1 , benchmark(999999,md5(user()), 1 )

Тем самым мы сэкономим время и снизим нагрузку на сервер.

- не забывайте, что серверу необходимо дать отдых после каждого бенчмарка, дать восстановиться так сказать.
Иначе следующий запрос может иметь непредвиденное время выполнения и даст вам ошибочные данные.
Об этой маленькой детали часто забывают, поскольку тестят двиг локально, либо непонятно о чём думают, релизя свой код без предварительного тестирования.
Сам лично долго удивлялся, непонимая, почему первый символ брутится правильно, но дальше идёт мусор - сервер просто давился бенчмарком.
Желательно выбирать время задержки в расчете на 1-1,5 больше, чем время исполнения бенчмарка.
Да, это существенно замедлит брут, но обеспечит качество результата.

Если обратится непосредственно к коду, то символьный брутер 1dt.w0lf'а без труда модифицируется с учетом бенчмарка:
необходимо

- изменить sql под бенчмарк
Код:
$http_query = $path."?Cat=&page=1&like=".$username."' AND 1=if(ascii(substring(CONCAT(U_LoginName,CHAR(58),U _Password),".$s_num.",1))".$ccheck.",benchmark(999999,md5(user()),1)/*";


- timeout - таймаут.
Но вот возвращать sub check($) теперь должна результат в зависимости от того, уложился ли сервер в таймаут, или нет.
Код:
$mcb_reguest = LWP::UserAgent->new(timeout=>$timeout) or die;


- sleep()
После каждого ложного запроса, а значит подвергнутого пытке бенчмарком, дадим серверу отдохнуть N секунд. пусть восстановится.

вот и всё.



[4] Альтернатива benchmark'y
ну вот, собственно, самое сладкое

Копаясь с бенчмарком меня как и любого из Вас одолевало желание найти замену этому геморойному, но единственному способу.
И способ был найден!

На самом деле разность выводимой инфы и времени - не единственные факторы.

Давайте вернёмся к началу и вспомним, с чего начинается любая sql-inj?
Конечно с ковычки в запросе! ...и выводе сообщения ошибки соответственно.
Но подойдем к этому логически - мы ставим ковычку, mysql проверяет запрос, находит в нём ошибку - сообщает нам.
Следите за мыслью: мускул сначала проверяет на корректность, затем либо проводит запрос и выводит результат, либо выводит еррор.
А теперь зададим себе вопрос - может ли быть иначе? Ну, разумеется может, в том то всё и дело.
Может возникнуть ситуация, когда проверку на корректность запрос пройдет, выполнится,.. но результат даст нам сообщение об ошибке.
Тогда наша задача сводится к простейшей ПРОВАКАЦИИ такого запроса + необходимо связать параметры в запросе таким образом, чтобы
мы могли манипулировать результатом запроса и соответственно получать полезные данные.

Ставим задачу:
Срыв запроса неявным условием и как следствие - намеренный вызов ошибки, которая и поможет отличить true\false query.

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

Сначала попытался используя IF сорвать запрос путем подстановки неверных имён таблиц\столбцов\типов данных:
Код:
select if(1=1,null,blaaaah);

Однако mysql, сцуко, умный и проверяет тип данных перед выполнением на корректность, что меня обломало.
You have an error in your SQL syntax
Попытка изменять имя не столбца, а таблицы также не увенчалась успехом.
Код:
select null from if(1=1,users,blaaaah);

Кстати, видимо алгоритм парсинга запроса в мускуле на корректность предполагает имя таблицы как константы, а здесь она не задана явно.
Он проверяет существование столбца в заведомо известной таблице и если таблица не определена, например я через if её задаю, то мускул шлёт меня подальше.
Принцип проверки в других базах данных я не проверял, так что возможно такие простые, а главное, универсальные трюки там прокатят.

Какие ещё ошибки могут возникнуть при выполнении запроса?
Самой популярной после "You have an error in your SQL syntax" является пожалуй "The used SELECT statements have a different number of columns"
Но здесь проверка так же происходит до выполнения.

"Operand should contain 1 column(s)" - опять же нельзя по той же причине.

"warning: mysql_fetch_array..." - можно, но php-ошибка специфическая, под конкретный двиг.


..а следующая по популярности в моём рейтинге идёт "Subquery returns more than 1 row".... Да, да, он самый, вездесущий лимит.

Вот то, что нам нужно.
Мускул проверяет корректность данных, но не может проверить число возвращаемых ответов, не сделав самого запроса.

Встаёт вопрос, как запрос надо задать, чтобы вероятно вызвать такую ошибку?

Требуется спровоцировать два условия:
когда ошибка 100% выведется и 100% не выведется.

Предположим, что нам заведомо известны имена таблиц и столбцов в уязвимом приложении.
Пусть пароль хешируется md5. Тогда длинна любого пароля = 32 символа.
А длина id или логина наверняка будет меньше, не правда ли?...
length(id)=(1-5..) и length(password)=(32)
Тогда наш эксплоит будет выглядеть следующим образом:

false:
Код:
...123' and 1=(select null from users where length(if(ascii(substring((select password from users where uid=1),1,1))>254,password,uid))>5)/*

Первый символ пароля наверняка меньше чем ascii(254).
Значит if возвратит length(id)>5, а так как столько пользователей наверняка нет(ну можно циферку и побольше брать), то селект возвратит NULL.
В результате мы ничего не увидим, будто и нет никакой инъекции.

true:
Код:
...123' and 1=(select null from users where length(if(ascii(substring((select password from users where uid=1),1,1))>1,password,uid))>5)/*

Поскольку первый символ пароля наверняка больше ascii(1), то условие истинно
Значит if возвратит length(password)>5, что приведёт к выводу N результатов в селект запросе.
Но в условие может быть сравнен только один результат 1=(*),... что спровоцирует ошибку "Subquery returns more than 1 row" !!!

Как всё просто, не правда ли?

Уверен, что поискав найдутся и другие подобные ошибки, проявляющиеся именно после выполнения query.


Особенности "more than 1 row":

1) важнейшее: стандартная скорость посимвольного брута при полной заменяемости бенчмарка, экономим время и не грузим сервер.

2) цена этой производительности - масса ерроров в логах mysql, что может привлечь внимание администратора.

3) единственно, требуется знать некоторые данные, как то - имена столбцов и таблиц, чтобы провоцировать ошибку "more than 1 row"



Возможно, вы найдете более совершенный и универсальный способ, в отличии от моего. Удачи!



[5] Links

Ссылки. Публичные эксплойты, использующие benchmark blind SQL-inj :


08 марта, 2007 г. PHP-Nuke <= 8.0 Final (INSERT) Blind SQL Injection Exploit (mysql)

08 марта, 2007 г. PHP-Nuke <= 8.0 Final (HTTP Referers) Remote SQL Injection Exploit

08 марта, 2007 г. PHP-Nuke <= 8.0 Final (INSERT) Remote SQL Injection Exploit

21 февраля 2007 г. Blind sql injection attack in INSERT syntax on PHP-nuke <=8.0 Final

1 декабря 2006 г. Invision Gallery 2.0.7 SQL Injection Vulnerability

8 сентября 2006 г. PHPFusion <= 6.01.4 extract()/_SERVER[REMOTE_ADDR] sql injection exploit

25 июля 2006 г. SQL-Injection in Shop-Script PRO & Shop-Script Premium all version

3 мая 2006 г. sBlog SQL Injection and Path Disclosure Vulnerability

24 марта 2004 г. MS Analysis v2.0 module for PhpNuke MS Analysis


10 февраля 2004 г. SQL injection in Php-Nuke 7.1.0