Особенности Safari в client-side атаках

By | Blog | 2 комментария

Что делает браузер, чтобы открыть страницу? Для начала, он отправляет запрос DNS серверу, чтобы узнать, с каким IP ему иметь дело.  Как известно, DNS-сервер может отвечать на произвольный запрос, если такая настройка (wildcard) присутствует.

Например, воспользовавшись утилитой dig попробуй отправить такой запрос:

dig "x^x`<\">'\!x=x.yandex.ru" A

DNS сервер вернет IP адрес.

Но что будет, если перенаправить пользователя на такой домен в браузере? Будет ли браузер вообще пытаться открыть такой URL? Конечно нет! Браузер перед отправкой запроса проверяет,  является ли строка доменным именем!

Только если это не Safari…

Потому что может

Особое свойство Safari — он посылает DNS запрос, даже если у доменного имени присутствуют спецсимволы. Правда и CURL не имеет механизма проверки подлинности домена, но этой утилите можно простить. Подобное поведение порождает целый класс client-side уязвимостей, о которых мы сейчас и поговорим.
Чтобы убедиться, наличие такой суперсилы можно проверить на домене hsts.pro. Safari будет пытаться открывать ресурс, если помимо букв и цифр содержатся следующие спецсимволы:

Вот попробуй —
http://!"$&'()*+,-;<=>^_`{|}~.hsts.pro
(разумеется, только для Safari и если DNS сервер, который ты используешь дает ответ, например 8.8.8.8 отвечает корректно).

Более того, туда можно записать и непечатаемые символы:
%01-%08, %0b-%0c, %0e-1f, %7f.

XSS via Host header

Если на страницу попадает текущий домен, к примеру, с помощью $_SERVER[‘HTTP_HOST’], то можно попробовать провести внедрение произвольного javascript сценария. Разумеется, уязвимое веб-приложение должно отвечать на такой поддомен. Из других минусов — как выяснилось при тестах, некоторые DNS сервера не позволяют отправлять символы “(“ и “)”. Если не пускает скобки, используем фичу js — шаблонные строки. Чтобы вызвать функцию, например, alert() — достаточно использовать вместо скобок символ бэктика — alert``.
А такие символы как “/”, символ пробела или табуляции и вовсе не могут быть частью доменного имени. Однако, так как мы имеем доступ и к управляющим символам, пробел может заменить символ смены страницы — %0c. Символ перевода курсора на новую страницу, в нашем случае, будет равнозначным с пробелом.

Таким образом, ссылка вида
http://a">'><img%0csrc%0conerror=alert``>a.hsts.pro
будет корректно воспринята браузером и в качестве домена, и в качестве тега при попадании в html, а значит, если не будет мешать XSS Auditor — функция alert выполнится.

Дальше — проще, если понадобится указать “/” внутри атрибута — можно использовать, например, его мнемоник — &sol;

Cookie Injection

Другой, очевидный вариант использования, это инъекция в Cookie. Если в домене будет содержаться знак «;» — это даст возможность отрезать часть заголовка и внедрить свою логику, если заголовок создания cookie также зависит от заголовка HOST. Использование подобного когда во многих проектах — норма:

Даже если XSS отсутствует и ты видишь, что приходит заголовок Set-Cookie с атрибутом domain, попробуй изменить домен таким образом, чтобы выполнить инъекцию.
Более того, (как уже показывал Black2Fan) — Safari имеет еще одну забавную фичу — перечисление нескольких Cookie в одном заголовке Set-Cookie через запятую. Поэтому, используя специально сформированную ссылку можно устанавливать произвольные Cookie в браузер жертвы, закрыв предыдущую печеньку символом “;”, а через запятую перечислить куки, которые ты хочешь установить.

XSS via Referer

Всем известно, что браузеры преобразуют спецсимволы в их URLEncode представления, поэтому даже если содержимое Referer попадает на страницу, перенаправить пользователя с страницы
site/<script>alert()</script>.html
и выполнить XSS не получится, так как до веб-приложения этот заголовок дойдет как
Referer: http://site/%3Cscript%3Ealert()%3C%2Fscript%3E.html
На сайте я оставил r.php с параметром u, который можно использовать для перенаправлений. Попробуй перейти сюда:
http://hsts.pro/r.php?u=//hsts.pro/referer.php&xss=<script>alert()</script>

Но все меняется, если жертва использует Safari. Ведь все спецсимволы, которые содержатся в доменном имени будут проходить функцию URLDecode, и придут в заголовке на уязвимую страницу в подходящем для нас виде.

А чтобы убедиться, что все работает — вот тебе ссылка:
http://a<img%0csrc=x%0conerror=alert``>.hsts.pro/r.php?u=//hsts.pro/referer.php

Что-то еще?

А как поведет себя веб-сервер, получив в качестве заголовка Origin спецсимволы, которых в принципе в домене быть не может (ну мы-то знаем)?
Как уже выяснил один из охотников на ошибки, символ бэктика (%60), мог обойти проверку заголовка Origin и смог выполнить XHR запрос в обход логики работы веб-приложения на одном из серверов Yahoo — вжух.

Поэтому, кто знает, как поведет себя тот или иной сайт, если в заголовке Origin будут присутствовать посторонние символы. Рекомендую в первую очередь проверить:
victim.com&.evil.domain
victim.com`.evil.domain
victim.com%01.evil.domain

А напоследок

Забавно, что Safari все больше и больше превращается в Internet Explorer. До этого Host Header XSS и обход URLEncode были только в нем, но теперь вектор атаки расширился на пользователей MacOS. Если ты придумал, как еще использовать подобное поведение браузера — обязательно напиши об этом в комментариях.

Обязательно попробуй подобные атаки на BugBounty, авось 😉

Комикс о UXSS в Safari и Chrome

By | Blog | 4 комментария

Привет! Помимо уязвимостей клиентской части веб-приложений опасность представляют и само клиентское ПО. Нет, речь даже не о java или flash, а о самих браузерах.

Хочу показать тебе примеры двух браузеров-конкурентов с уязвимостью UXSS (Universal Cross-site Scripting), один из которых с закрытым исходным кодом, а другой с открытым. UXSS — это ошибка в логике работы браузера, благодаря которой злоумышленник может выполнить javascript сценарий в рамках произвольного сайта. Грубо говоря — сделать XSS там, где ее нет.

 

Safari

Что я могу сказать хорошего о Safari? Это простой и легковесный браузер, в котором нет ничего лишнего. Это правда самый быстрый браузер который мне довелось использовать. Что плохого? Ну… Он странный.

Возможно, ты уже читал мою статью о чтении локальных файлов с помощью браузера. Если коротко — открыв следующий html файл в Safari он прочитает локальные файлы и попытается слить личные документы, в рамках PoC — на свою же локальную машину (в консоли разработчика ты увидишь ошибки).

А еще функции в консоли выполняются в момент ее написания, жуть!

 

А вот что ты слышал о псевдорасширении parent-tab://? Да ничего, лишь пара упоминаний. Однако в Safari он присутствует.

Примечательно, что до 11 версии (уже) parent-tab имеет все те же привилегии для доступа к объектам домена, например к cookie.

А еще круче — в него можно писать. Вот создаешь локальный html, пишешь <iframe> с parent-tab и js’ом записываешь произвольное содержимое! Так рождается первый эксплойт, где в parent-tab.html — редирект на parent-tab://+domain

Однако, его нельзя вызвать с другого сайта, что немного огорчает. Локальные эксплойты не так весело (ну чтение файлов веселее).

Тут на помощь пришел Frans Rosén, который выяснил, что записать свою полезную нагрузку можно также с помощью window.open с аргументом _top. Вжух — и мы получаем доступ к данным сайта.

Но оказалось, эта бага в WebKit, поэтому, возможно, применима и к Chrome. Примеры эксплойта на CVE-2017-7089 ждет тебя на github.

Chrome

Хром более защищенный браузер. Наверное. Все таки это браузер с открытым исходным кодом. Между тем, несмотря на современность — в нем до сих пор поддерживаются старые вещи, например поддержка древнегреческого формата MHTML (MIME-HTML).

Что из себя представляет данный формат? Это текстовый документ, в котором прописан заголовок, тип контента (Content-Type: multipart/related) и разделитель контента (boundary). Да-да, multipart в файле. Дальше следуют дополнительные параметры, типа кодировки (может быть base64).

Ну проще один раз увидеть.

Так вот, к файлу можно написать атрибут Content-location, а потом обратиться к нему в самом html.

Например, пишем Content-location: /abc, содержимое файла. И вызываем <img src=/abc>. А можно написать Content-location: https://example.com/abc, и загрузить <img src=https://example.com/abc>

Если это изображение — то оно появится на странице при попытке открыть данный файл.

Все бы ничего, но javascript в нем запрещен. Вообще.

Но и тут находится одно «но».  Везде, кроме XSLT.

Объявляем content-type: application/xml, вставляем XSLT, получаем alert():

MIME-Version: 1.0
Content-Type: multipart/related;
type="text/html";
boundary="----MultipartBoundary--"
------MultipartBoundary--
Content-Type: application/xml;
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xml" href="#stylesheet"?>
<!DOCTYPE catalog [
<!ATTLIST xsl:stylesheet
id ID #REQUIRED>
]>
<xsl:stylesheet id="stylesheet" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="*">
<html><script>alert()</script></html>
</xsl:template>
</xsl:stylesheet>
------MultipartBoundary----

Ну и как ты уже догадался, в таком документе мы можем написать Content-location: https://example.com и вызвать javascript оттуда, обойдя песочницу вопреки всем законам SOP. Добавляем к предыдущему файлу следующий файл и вместо alert вызываем его в фрейме.

------MultipartBoundary--
Content-Type: text/html
Content-Location: https://google.com
<script>alert('Location origin: '+location.origin)</script>

Для открытия MHTML нужно вернуть в ответе от сервера тип контента multipart/related, в итоге построится следующее DOM-дерево:

В хроме внесен соответствующий патч (благодаря которой был найден эксплойт). Плюсы — открытый исходный код позволяет наблюдать за всеми внесенными изменениями. Минусы точно такие же.

Демонстрация эксплойта по следующей ссылке (Chrome < 62), исходники CVE-2017-5124 на github.

Вывод

Какой-такой вывод? Ну будь аккуратнее, обновляй ПО для минимизации рисков, но ты это слышал уже тысячу раз. Что-то нового я тебе сказать не могу 🙂

Способы обхода проверки домена и IP адреса

By | Blog | 3 комментария

И снова здравствуй!

Допустим, существует приложение, которое возвращает тело страницы, если ему отправить домен или IP адрес. Но наша цель — отправить пакет на адрес внутренней инфраструктуры и проэксплуатировать такую атаку, как SSRF.

Некорректная логика работы проверки адреса

Проверка вхождения строки

Порой, обход проверки домена бывает совсем простым. Если приложение ожидает, что будет указан адрес сайта — vulnsite.com, то иногда достаточно указать его в поддомене. Вот попробуй указать vulnsite.com.evil.com — и если веб-приложение проверяет вхождение строки в домене — мы проходим эту проверку.

Ошибка в регулярном выражении

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

(.*)mail.vulnsite.com$

А что тут не так? А то, что точка в регулярном выражении — это любой символ, поэтому отправив ссылку на mail-vulnsite.com (который можно зарегистрировать) — проверка будет пройдена.

Отсутствие проверки перенаправления

Другие способы — это перенаправление, когда указанная ссылка перенаправляет на внутренний адрес (обычный 302 редирект). Проверку, что домен принадлежит внутренней сети — прошли, но и редирект обработали. Забавно, что редирект может быть и на file:///etc/passwd 🙂

Отсутствие проверки адреса во время запроса

Другая ошибка логики — это разделение проверки на соответствие внешнему адресу и на непосредственно сам запрос.

Приложение делает резолв доменного имени и убеждается, что ip адрес evil.com не принадлежит адресу внутренней инфраструктуры.

Потом происходит сам запрос, и утилита (или библиотека) берет адрес не из кэша, а заново получает IP адрес домена.

Для эксплуатации уязвимости необходимо настроить свой DNS сервер таким образом, что на первый запрос DNS вернул внешний IP адрес, а на последующие — внутренний.

«Отрезание» части домена

В ссылке может быть передан логин и пароль (до символа @), данные которого в HTTP-запросе уйдут в заголовок, такая ссылка имеет подобный вид:

http://google.com:80@evil.com

Для некоторых механизмов проверки — доменное имя будет соответствовать google.com, а по факту — домену, указанному через символ «@».

Некоторые символы позволяют «отрезать» хвост у домена, и если парсер пропускает их:

  • Символ решетки (hash — #).

http://evil.com#vulnsite.com

Символ используется для hash-навигации или передачи информации без участия сервера. Открыв эту ссылку в браузере, браузер отправит GET запрос только на evil.com, а обработка информации после символа происходит на стороне клиента.

  • Знак вопроса (?)

http://evil.com?vulnsite.com

Аналогичная ситуация, только строка после знака вопроса уйдет в параметр, точно так же, если открыть страницу evil.com/?vulnsite.com

  • Нулевой байт (%00)

http://evil.com%00vulnsite.com

По большей части это касается сайтов на PHP, ибо работал он с nullbyte не всегда хорошо.

  • Отдельно стоит отметить символ 💩 (U+1F4A9)

http://evil.com💩vulnsite.com

Веселый emoji «PILE OF POO» — альтернатива nullbyte для таких СУБД как MySQL с кодировкой utf8. И эта уязвимость встречается на многих проектах.

Варианты представления IP адреса

IP адрес может быть представлен различными способами. Думаю, удобнее показывать на живом сайте, а не на локальном. Берем IP адрес google.com — у меня это — 173.194.44.70.

Если мы сделаем urlencode — ссылка будет оставаться валидной:

http://%31%37%33%2E%31%39%34%2E%34%34%2E%37%30

Это применимо и к обычным доменным именам.

Каждое число в IP адресе можно представить в восьмеричной или шестнадцатеричной системах счисления:

http://0xad.0xc2.0x2c.0x46

http://0255.0302.0054.0106

Более того, эти варианты можно смешивать:

http://173.0302.0x2c.70

http://0xad.0302.0x2c.0106

Не забываем про целочисленное представление IP адреса — Long IP, например, можно воспользоваться этим сервисом. В результате мы можем получить такую ссылку:

http://2915183686

Аналогично, Long IP можно представить в восьмеричной и шестнадцатеричной системах счисления:

http://025560426106

http://0xADC22C46

Помимо этого, внутренний адрес, такой как localhost (127.0.0.1) может быть представлен как 0.0.0.0, что является исходным адресом для этого хоста в сети.

Еще один забавный лайфхак — символы нуля можно опускать.

Таким образом, появляются еще варианты:

  • 0
  • 0.0
  • 0.0.0

Это же правило применимо для других ip адресов:

  • 127.1
  • 127.0.1

А теперь применяя предыдущие методы, тот же 0x7f.0.0×01 — точно такой же адрес, как и 127.0.0.1 (попробуй пингануть, например, 0x7f.1).

Точка, точка, запятая

А закончу материал примером, который произошел совсем недавно. Все началось с того, что я увидел @_Abr1k0s_ в ленте твиттера:

 

О, так у PortSwigger есть багбаунти? Я в последнее время отстал от bb-движухи, но было интересно посмотреть, что у них находится на периметре. Пара минут — и был найден поддомен surveys.portswigger.net, который ведет на surveygizmo.com

dig surveys.portswigger.net

Как оказалось, это популярный сервис, а пользуются им многие. После регистрации в приложении я создал свою форму и попытался добавить домен PortSwigger. Естественно вылезла ошибка — «This domain name is already in use», однако, тут я вспомнил об одной фиче с доменами.

Существует такая штука, как домен нулевого уровня. В конце домена должна стоять точка, но она упускается.

Например, сайт википедии нормально работает с точкой

https://en.wikipedia.org./wiki/Main_Page

https://en.wikipedia.org/wiki/Main_Page

но как будет вести себя логика приложения — еще непонятно.

В зависимости от настроек сервера — сайт может реагировать по-разному. Кто-то будет показывать 404, кто-то invalid hostname, кто-то bad request. Но в любом случае, браузеры считают такой запрос вполне валидным.

И как ты уже догадался, я добавил домен surveys.portswigger.net. в личный кабинет. А так как сервис достаточно популярен, нашел еще десяток-другой компаний, который попали ко мне в кабинет:

surveys

В настройках была создана собственная форма с директорией /s3/, с дальнейшим редиректом.

В итоге домены имели такой вид

surveys.portswigger.net — неконтролируемый мной

surveys.portswigger.net./s3/ — домен, которым могу управлять, вел на мой контент

Ну и аналогичный конфиг на сайты microsoft, jetbrains, и ряде других.

Так как настоящий хозяин домена уже прикрепил домен к сервису, мы можем свободно использовать его домен, но уже с точкой. Если мы можем управлять контентом — можем эксплуатировать такие штуки как фиксация сессии, да и раздолье для строительства фейков.

Что имеем?

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

Когда символ пробела — атака

By | Blog | 5 комментариев
Ты не пройдешь

Привет!

Сервера могут по-разному воспринимать присланную пользователем информацию. Общепринято, что %20 или знак + в параметрах принимается за пробел. Запрос на директорию /folder/test/%2e%2e/page — нормализуется до /folder/page, ибо %2e — точка в urlencode. Например, знаменитый (в узких кругах) open redirect работает за счет некорректной нормализации запроса.

Но я тебе расскажу о том, как пробел становится атакой.

Уязвимость в Xbox

В качестве вступления — забавная бага, не моё, но в тему. Пацану 5 лет и он понял, что если ввести пробел вместо пароля — он попадет в аккаунт отца. Как это работало я до сих пор смутно представляю, но вот тебе ссылка ознакомиться поближе.

— А причём тут ты?

А я просто мимо проходил, и оказывается, находился на одной стене почёта с этим мальчиком, и даже попал на TV:

Видел?

Как нет?

Ну ты чего, смотри внимательнее!

Вот же:

Правда всего на треть секунды, быстрым перематыванием страницы. Ну да ладно 😀

Уязвимость на поддомене Yandex

Было замечено, что в стандартной ситуации, один из поддоменов яндекса при обращении к директории /admin/ просит логин и пароль:

http://sqtest.yandex.net/admin/ — 401

Однако, если добавить после admin знак пробела, то в админку пускало.

http://sqtest.yandex.net/admin%20/ — 200

Внутренние ссылки заново просили логин и пароль, однако, подменив вхождение строки «/admin/» в ответе сервера на»/admin%20/», админкой можно было пользоваться. К сожалению, скриншотов у меня не осталось, так что придется поверить мне наслово. Уязвимость давно сдана, деньги получены и потрачены, остались лишь воспоминания.

Уязвимость в Bitbucket Server

Шло время, уязвимость в рамках bugbounty я уже давно забыл, но после очередного брута директорий нарвался на странную аномалию, где опять же, директория /admin с пробелом на конце вернула статус 200, в то время как должен быть редирект на /login

Взглянув на домен, я понял, что там находится bitbucket server, да и версия вроде не древняя. Открыв руками — был удивлён, и правда открывается админка, правда без многих ссылок.

Дальше изучив bitbucket server на локальной машине, опытным путем понял, что сервер отдает содержимое страниц для следующих ссылок:

/admin%20/mail-server
/admin%20/db
/admin%20/db/edit
/admin%20/license
/admin%20/logging
/admin%20/server-settings
/admin%20/authentication
/admin%20/avatars

Выглядит забавно, правда?

Уязвимость позволяет увидеть лишь некоторые директории, например, директория /admin/users/ — недоступна. Также, вместо пробела могут быть символы x01-x20.

Помимо директорий выше — видны установленные плагины, которые тоже могут иметь свои уязвимости.

Бага была исправлена и работает на Bitbucket Server < 4.8. Bitbucket разрабатывает команда atlassian, в свою очередь они делают такие продукты как jira, confluence, hipchat, и может ты, %username%, попробуй изучить подробнее, вдруг я что-то пропустил. И возможно, уязвимость будет в других продуктах компании.

UPD: А тем временем Алексей Писаренко попал в админку одного из сайтов студии Лебедева. А казалось бы — пробел…