Знакомство с миром инъекций ESI
Привет всем,
В этой статье я поделюсь своими выводами, связанными с ESI (включая Edge Side). Инъекция, которую я и мой друг nytr0gen нашли в частной программе вознаграждения за ошибки. Мы нашли их целую кучу, так что обязательно прочтите это до конца, так как таким образом вы не пропустите самые интересные находки
Если вы не знаете / еще не слышали о Edge Side Include Injection, я настоятельно рекомендую сначала прочитать приведенные ниже статьи, поскольку Gosecure уже проделала большую работу по объяснению этого, а также вы можете проверить твит Алекса Бирсана ниже, чтобы узнать, как их искать в последнюю очередь, но не в последнюю очередь вы можете также вы можете посмотреть выступление DEFCON:
https://www.gosecure.net/blog/2018/04/03/beyond-xss-edge-side-include-injection/ https://www.gosecure.net/blog/2019/05/02/esi-injection-part-2-abusing-specific-implementations/ https://www.youtube.com/watch?v=VUZGZnpSg8I
Начало истории
Эти иследования были сделаны в рамках программы по поиску ошибок, поэтому я не буду называть цель, буду называть её redacted.com, чтобы вы немного понимали что это за компания, я скажу лишь то, что это очень известная спортивная организация.
Я искал ошибки xss и достаточно скоро нашел одну конечную точку, где разрешались символы <> '"
, поскольку они отражались в ответе, как есть, но я не смог легко получить xss из-за установленного Akamai WAF.
Просматривая исходный код страницы, я заметил блок кода, который показался мне довольно подозрительным:
Я знал о EASY, поэтому вместо того, чтобы сосредоточиться на появлении окна предупреждения, я начал проверять, будет ли кэширующий сервер отображать мои введенные теги esi
Я использовал вышеуказанную полезную нагрузку, и она была заблокирована waf, в приложении действовали более строгие правила waf, после дальнейшего тестирования я понял, что waf ищет шаблон, подобный этому <esi: placeholderhere>
и блокирует, если он содержит какой-либо из допустимых тегов ESI, таких как include и т.д. Использование только <esi:>
вернуло 500 внутренних ошибок сервера, поэтому я знал, что сервер кэширования на самом деле пытался отобразить теги ESI, я попробовал различные символы, такие как % 00
, % 0A
и т.д., Чтобы проверить, позволяет ли какой-либо из них обойти проверку, но в итоге ничего не сработало.
В то время я использовал пробную версию Burp Pro, поэтому я отсканировал этот параметр с его помощью и получил этот вывод:
https://globe.redacted.com/?showFooter=um6k%3c!--esi--%3e8omf%3c!--esx--%3eekdi Note: This issue was generated by the Burp extension: Active Scan++. Issue detail The application appears to support Edge Side Includes: The following probe was sent: um6k<!--esi-->8omf<!--esx-->ekdi In the response, the ESI comment has been stripped: um6k8omf<!--esx-->ekdi Refer to https://gosecure.net/2018/04/03/beyond-xss-edge-side-include-injection/ for further information
Таким образом, я смог подтвердить внедрение ESI, используя тег комментария, но все равно не очень полезный.Поскольку у меня больше не было идеи попробовать, я решил двигаться дальше.
Затем я сохранил конечную точку в своих заметках и двинулся дальше, в надежде, что смогу увеличить ее в будущем.
Прошло пару месяцев, и я получил письмо от bbp компании, они запустили рекламную акцию и платили в 2 раза больше за все качественные материалы.
Увидев это письмо, я больше не мог оставлять конечную точку в своих заметках, мне нужно было как-то расширить ее, чего бы это ни стоило.
Я был почти уверен, что не смогу справиться с этим самостоятельно, поскольку у меня нет необходимого набора навыков для этого, так почему бы не обратиться к кому-нибудь еще, у кого гораздо больше опыта, чем у меня, и кто смог бы придумать решение для обхода waf.
Программа позволяла совместно работать над отчетами, поэтому я мог просто связаться с кем-нибудь из раздела "collaborator".
В этой программе было много людей, даже некоторые громкие имена тоже были там, так что было непросто решить, с кем мне следует связаться, после некоторых раздумий я связался с nytr0gen, я уже знал о нем, так как играл в ctfs. Он из команды ctf Wreck The Line, и поверьте мне, набор навыков игроков CTF находится на совершенно другом уровне.
Магия сотрудничества ⭐
Примерно через 30 минут nytr0gen выдал рабочую полезную нагрузку ESI, которую можно использовать для всплывающего окна предупреждения xss.
Еще несколько примеров, чтобы вы поняли, как теги ESI можно использовать для обхода WAF:
Позвольте мне расшифровать полезную нагрузку, чтобы вы поняли, что произошло на самом деле
um6k<!--esi $(QUERY_STRING{countryCode}) --><!--esx-->ekdi&countryCode="}};prompt.call(null,origin);//US
Я был поражен, когда впервые увидел это, так как не знал, что вы также можете использовать переменные внутри тега комментариев ESI.
Когда вышеуказанная полезная нагрузка будет отрисована сервером кэширования, он вернет только это в источнике страницы:
$(QUERY_STRING{CountryCode})
вернул значение параметра CountryCode
, теги ESI действительно очень мощные, с ними можно делать кучу вещей.
Еще одной интересной вещью было то, что файлы cookie сеанса приложения были помечены как httponly, и это было доступно для .redacted.com , поскольку файлы cookie были помечены как httponly, было бы невозможно украсть сессионные файлы cookie с помощью обычного простого xss, но с тегами ESI это возможно
Ну, я даже спросил его, как ему пришла в голову идея использовать переменные внутри комментария ESI, он сказал мне, что только что прочитал документы
Edge Side Includes (ESI) Language Tags
В отчете был представлен полномасштабный POC по захвату учетной записи, проблема была отсортирована и получила высокую оценку серьезности (благодаря 2-кратному мультипликатору).:
История на этом не заканчивается, так как оказалось, что nytr0gen нашел другой поддомен, где у него был xss при использовании тегов ESI, там также они отображались сервером кэширования, это было отличным сигналом для нас, поскольку это может означать, что все еще может быть больше ошибок внедрения ESI по всей цели.
Это приложение выполняло какое-то странное преобразование наших входных данных в верхний / нижний регистр, поэтому мы не смогли использовать какую-либо переменную, например, для HTTP_COOKIE, поскольку она преобразовывалась в верхний / нижний регистр странно http_COOKIE, тем не менее, nytr0gen смог снова прочитать документы и найти другую полезную функцию ESI, которая позволяет декодировать строки, закодированные в URL:
https://www.akamai.com/site/zh/documents/technical-publication/akamai-esi-developers-guide-technical-publication.pdf
Он в URL-адрес закодировал полезную нагрузку 2 раза, чтобы убедиться, что Akamai не блокирует ее,
Двигаясь дальше, как и сейчас, я медленно-медленно начал понимать эту инъекцию ESI, пообщавшись с nytr0gen несколько раз. Я уже нашел xss на другом поддомене, нигде не было признаков тегов ESI в исходном коде страницы, но я все равно решил попробовать aaa<!— esi --> bbb<!— esx --> ccc
, используя это в качестве полезной нагрузки, сервер фактически отобразил это, чтобы я знал - Ошибка внедрения ESI, была ли там использована та же полезная нагрузка для захвата учетной записи....
Если вас интересует изображение захвата учетной записи, вот оно (код js помещен после хэша, я просто разместил его ниже, чтобы сделать его более читаемым):
nytr0gen проделал здесь потрясающую работу, очень упростив для triager воспроизведение фактического воздействия с помощью этого PoC. Поскольку вы, ребята, еще не знаете, что делают переменные HTTP_COOKIE и QUERY_STRING{x}, вам должно быть легко понять PoC.
Кстати, оказалось, что nytr0gen уже отправлял эту ошибку xss 10 месяцев назад, мы все равно решили сообщить об этом, поскольку это оказало гораздо большее влияние по сравнению с обычным xss.
Еще одна ошибка высокой степени серьезности, у нас здесь все хорошо
Давайте продолжим, предстоящие ошибки при внедрении ESI очень интересны, так что крепко сидите на своей пятой точке, лучшее еще впереди.
Через несколько дней nytr0gen снова обнаружил еще одну новую конечную точку, которая была уязвима.
Да, вы правильно прочитали, значение заголовка типа содержимого ответа для этой конечной точки было application/json, теги ESI все еще отображались сервером кэширования.
Хотя здесь можно было включить тег ESI, но как бы вы смогли украсть файл cookie, который находится в ответе страницы, поскольку xss не будет работать с типом содержимого: application / json или это так?
Любой бы остановился тут, думая, что ничего не может сделать из-за типа контента, но вам не стоит беспокоиться, когда nytr0gen прикрывает вашу спину
Вот ответ, используя метод $add_header()
, вы можете перезаписать любой заголовок ответа.
https://www.akamai.com/site/zh/documents/technical-publication/akamai-esi-developers-guide-technical-publication.pdf
Приведенная ниже полезная нагрузка изменит тип содержимого ответа на текст /html
И вот последняя полезная нагрузка, которую мы использовали для выполнения js-кода, сначала мы использовали HTTP_COOKIE, затем add_header, а затем функцию url_decode:
Переменная HTTP_COOKIE возвращает файлы cookie в ответе, функция add_header изменяет тип содержимого ответа и, наконец, используя функцию url_decode, мы помещаем нашу полезную нагрузку xss (url, закодированный 2 раза, чтобы избежать waf), с помощью которой мы можем украсть ответ страницы.
Отличный баг заслуживает большой награды
Мы думали, что на этом все должно закончиться и ошибок ESI больше не будет, но мы ошибались..............
Мы нашли другую конечную точку, она выглядела как прокси-сервер, который принимает параметр url в качестве входных данных, а затем извлекает ответ по этому URL.
Он внес в белый список всего несколько доменов, некоторые из них были на нашей цели redacted.com и лишь немногие были на других сторонних сайтах.
Из-за того, как это работало, мы начали тестирование, чтобы увидеть, возможен ли здесь ssrf. Поскольку это позволяло делать запросы только к некоторым хостам, внесенным в белый список, мы начали искать любую открытую ошибку перенаправления на этих хостах.
Мы смогли найти один в redacted.com но это работало только тогда, когда пользователь прошел аутентификацию, кстати, этот открытый редирект был немного интересным.
При посещении этого URL-адреса: https://www.redacted.com/auth#login?url=http://evil.com
Он загрузил другой URL-адрес внутри iframe, который затем перенаправил на наш URL-адрес, я попробовал xss, но это не сработало, так как перенаправление происходило через заголовок Location.
Затем мы перешли на сторонние сайты xyz.com которые также были в белом списке, тем не менее мы не нашли ни одного открытого редиректа, который мог бы быть нам полезен, хотя мы находим некоторые xss на этих сторонних сайтах.
Ssrf выглядел здесь так близко, но все же мы не смогли делать произвольные запросы.
Играя с этой конечной точкой, я использовал уязвимую конечную точку xss стороннего сайта в параметре url, и, к удивлению, всплывающее окно xss появилось в контексте redacted.com домен.
Мы уже сообщали о внедрении ESI на этом хосте ранее, поэтому я просто попробовал использовать полезную нагрузку ESI, и это действительно сработало
Итак, используя xss на другом стороннем домене, мы смогли заставить ESI-инъекцию работать на нашем целевом домене, мы не смогли получить ssrf, так как мы не нашли ни одного открытого перенаправления, потратив пару дней, но, по крайней мере, нам удалось увеличить это с помощью ESI-инъекции, поэтому мы сообщили об этом.
Мы также попробовали еще одну вещь, о которой, я думаю, я должен упомянуть, поскольку вы уже знаете о функции add_header ESI, мы подумали, что можем создать нашу собственную ошибку открытого перенаправления с полезной нагрузкой, подобной этой:
Использование этой полезной нагрузки на любой из предыдущих конечных точек, которые мы сочли уязвимыми для внедрения ESI, даст нам приятный и простой открытый редирект.
Он работал нормально, но когда мы использовали этот открытый URL-адрес перенаправления на конечной точке прокси-сервера, он по какой-то причине не сработал, возможно, Akamai waf или что-то еще, чего мы не знали.
И вот тут-то и происходит поворот в истории, ошибка была помечена как дублирующая с помощью h1 triager, мы оба были как wtf, это невозможно.
Если бы это было на самом деле дубликатом, это означало бы, что в этой программе есть кто-то еще, кто также знает об этой магии ESI или это так?
После дальнейших манипуляций с триггером h1 мы узнали, что оригинальный репортер действительно смог увеличить его для выполнения SSRF, но, очевидно, без трюка ESI.
Мы забыли об этом отчете и двинулись дальше. Достаточно скоро акция подошла к концу, и мы решили, что тоже остановимся на данный момент, и если в будущем они проведут такую же 2-кратную акцию, мы вернемся к этому, по нашему мнению, имело очень меньшую вероятность.
Еще одно повышение
Достаточно скоро, через 4-5 месяцев, они снова запустили ту же акцию с 2-кратным множителем, и мы были готовы снова охотиться за этими ошибками внедрения ESI
Я протестировал некоторые параметры обратного вызова, поскольку они обычно отражают входные данные в ответе, и нашел некоторые, которые даже допускали символы <>, но конфигурация сервера кэширования для таких хостов была другой, поэтому теги ESI не работали.
Некоторые из старых отчетов уже были исправлены, nytr0gen обнаружил, что одна и та же конечная точка json api теперь доступна по другому пути. Чтобы исправить оригинальную ошибку json api ESI, они полностью удалили конечную точку, но та же конечная точка существовала на другом пути, поэтому ошибка все еще была там, используя тот же PoC, что и до того, как мы смогли воспроизвести ошибку.
Отправил его, поскольку конечная точка была другой, хотя у нее был тот же api, который был отсортирован программой и снова вознагражден как ВЫСОКИЙ.
На этот раз команда даже задала нам вопрос относительно смягчения последствий
Я собираюсь рассказать о последней обнаруженной нами ошибке ESI, это лучший из множества интересных способов, которые мы придумали, чтобы действительно создать работающий эксплойт.
Я нашел другую конечную точку, тип содержимого для которой был application/json
На приведенном выше скриншоте вы можете видеть, что значение параметра locale отражено в источнике, но теги ESI не были отрисованы сервером кэширования. Поскольку мы уже находили здесь ошибку с внедрением ESI ранее, это должно было сработать.
Я отправляю этот URL-адрес nytr0gen, и, по его словам, это также должно было сработать. Через некоторое время он пишет мне сообщение о том, что у него все заработало, но это бесполезно.
На этом скриншоте вы можете видеть, что теги ESI сработали, но можете ли вы определить изменения, внесенные в этот запрос?
Наметанный глаз уже заметил бы закодированный URL-адрес. в url %2e
Внутренний сервер расшифровал %2e
в URL и вернул тот же ответ, сервер кэширования не проигнорировал %2e
, поэтому он больше не рассматривал его как статический файл и, следовательно, разрешил работу тегов ESI.
Это работало, но, к сожалению, это невозможно использовать(
Если вы отправляете этот URL жертве, когда он загружает этот URL. Браузер автоматически декодирует %2e
, поэтому запрос отправляется к исходной конечной точке, а не к той, к которой мы хотели, поэтому теги ESI не отображаются.
Мы потратили на это еще некоторое время, но так и не смогли понять, как заставить это работать и в браузере.
Оставил эту конечную точку и перешел к поиску еще нескольких конечных точек для тестирования.
По прошествии целого месяца я тестировал другую программу, в которой у меня была потенциальная ошибка обхода пути на стороне клиента, например, предположим, что в приведенном ниже js-коде у вас был контроль над ключевой переменной.
На том же хосте также была конечная точка обратного вызова jsonp, поэтому я пытался загрузить ее вместо /api.js файл, который позволит мне вывести всплывающее предупреждение
Я пытался сделать что-то подобное, но значение ключевой переменной было взято из параметра запроса, поэтому я не смог использовать символ #.
Как если бы я поместил этот # в значение параметра, он будет использован браузером и не будет передан в значении ключевой переменной.
Если я декодирую %23
, то браузер при извлечении файла js не декодирует его автоматически, так что я застрял здесь. Я тоже не мог использовать? чтобы игнорировать /api.js частично из-за этой части кода.
Внутри метода getAllUrlParams заметил первую строку url.split('?')[1]
, поэтому, если я использую ? в моем значении параметра url это произошло бы
Это? будет полностью проигнорировано, из-за метода разделения.
В то время я подумал, может быть, если я загружу URL с URL-адресом, закодированным # внутри iframe, это сработает. Но, к сожалению, это не сработало.
Я думал использовать ту же идею и для отредактированной цели, помните эту конечную точку?
Что, если я загружу этот URL-адрес в iframe, это сработает? Давайте выясним
Когда я попытался загрузить тот же код в браузере Chrome, это не сработало
%2e
был автоматически декодирован Chrome
Так странно, что это работало в Firefox, но не в Chrome, позже я подтвердил, что это также работало в Safari так же, как и в Firefox.
Я был так счастлив, что это наконец сработало, что быстро связался с nytr0gen, чтобы сообщить ему хорошие новости
Теперь перейдем к части POC, когда я использовал HTTP_COOKIE, я заметил, что там отсутствовали все файлы cookie, в основном те, которые использовались для целей сеанса. Спросил об этом ntr0gen, и оказалось, что это было связано с тем, что страница загружалась внутри iframe, а родительский элемент был другого происхождения.
Поэтому вместо этого мы использовали другой xss (созданный с использованием одной из наших старых ошибок внедрения ESI) и использовали его для создания iframe этого URL-адреса .json, и вуаля, на этот раз появился файл cookie.
Когда я попытался с помощью метода add_header изменить тип содержимого application / json на text / html, waf заблокировал запрос, после сокращения я обнаружил, что waf теперь блокирует ключевое слово text / html. Это дополнительное правило, возможно, было установлено из исправления нашего предыдущего отчета, поэтому мне нужно было придумать обходной путь.
После fuzzing я обнаружил, что символ табуляции %09(\t)
был преобразован сервером в t
.Используя приведенную ниже полезную нагрузку, можно было изменить тип содержимого, а затем с помощью xss украсть сессионный файл cookie.
Это не было вознаграждено 2-кратным множителем, потому что ресурс, в котором мы обнаружили ошибку, не был частью продвижения, и мы согласились на это, поскольку они все равно заплатили за это как за высокую степень серьезности, хотя это работало только в Firefox, а не в Chrome.
Наконец, здесь произошла еще одна вещь: менеджер программы попросил нас прекратить дальнейшее тестирование на наличие ошибок EASY Injection, поскольку они собирались точно настроить свой WAF.
Наконец-то мы подошли к концу, надеюсь, вам не было скучно и вам понравилось это читать
Вот и все, большое вам спасибо за то, что дочитали его до конца. Надеюсь, вам бы это понравилось.
Наш канал : https://t.me/webhack_kakao