PDA

Просмотр полной версии : Тайны CSRF-уязвимостей: Глубокий аудит веб-приложений для профессионалов


Vavilen
10.07.2022, 20:25
CSRF на JSON API: от разведки до эксплуатации. Практический кейс

https://forum.antichat.xyz/attachments/4920546/img_23b4c6740f.png



ДИСКЛЕЙМЕР
Эта история — художественный вымысел. Все совпадения с реальными людьми или уязвимостями — случайность. Материал предоставлен исключительно в образовательных целях для специалистов по ИБ. Повторение подобных действий в реальной жизни приведет к серьезным проблемам с законом. Помните: вы не Вавилен.

Лирическое отступление
Вавилен был доволен своим рабочем местом в конторе. Он приносил деньги себе и своему начальству, которое давало для этого всю инфраструктуру. Но в один прекрасный день боссы, в очередной раз срубив куш на труде Вавилена, решили с ним не делиться. Так они потеряли ценного сотрудника и получили в ответ обманутого и очень мотивированного человека, жаждущего возмездия.
Постановка цели
После ухода из конторы все доступы к инфраструктуре, разумеется, были отозваны. Но это не проблема. Основные активы бизнеса крутятся в кастомном веб-приложении для обработки данных. Компрометация этого приложения — ключ ко всей нужной информации. В истории браузера, к счастью, остался IP-адрес сервера. С него и начнём.
Изучение цели
Чтобы не шуметь и не светить свой IP на боевом сервере, было принято очевидное решение — развернуть аналогичный софт у себя в лаборатории. ПО распространяется по подписке, но есть семидневный триал с предоставлением VPS. Идеально для наших тестов. Регистрируюсь через 10-минутную почту, разворачиваю софт, активирую триал. Можно приступать к вскрытию.
Первое, что нас встречает — стандартная форма логина.

https://forum.antichat.xyz/attachments/4920546/img_00cf84fdc3.png

Смотрю, как устроен механизм аутентификации. При попытке входа улетает

POST

запрос с телом в формате

application/json

. Ничего необычного.
Что там в JWT?
После успешного входа сервер в ответ ставит cookie с JWT:

content=AA.BBB.C

. Первым делом иду на

jwt.io

, чтобы посмотреть, что внутри.

https://forum.antichat.xyz/attachments/4920546/img_64e8a3a2b5.png

И тут первый "красный флаг". Внутри токена, в открытом виде (просто закодированном в Base64), лежат поля

login

и

password

. Да, пароль в виде хэша (похоже на Blowfish с солью), но сама идея хранить хэш пароля в JWT — это дикая анти-практика.

Важно понимать: JWT по умолчанию не шифруется, а подписывается. Это как паспорт: любой может прочитать ваши данные, но подделать печать (цифровую подпись) без секретного ключа, который есть только у сервера, невозможно. Так что идея подделать токен "в лоб", не зная секрета, отпадает сразу. Но сам факт такой архитектуры уже говорит о многом.
Прощупываем очевидные векторы

Стандартные креды: Приложение ставится с дефолтным логином

admin

. Пароль генерируется, 12 символов, брутить бесполезно.

Перехват трафика: Соединение по умолчанию идет по HTTP. Теоретически можно было бы провернуть MitM-атаку, взломав Wi-Fi, но сотрудники сидят через USB-модемы. Вектор отпадает.

Защита от брутфорса: Пробую перебирать пароли для

admin

. Пять неудачных попыток — и мой IP в бане. Попытка обойти бан через заголовок

X-Forwarded-For

ни к чему не приводит. Сервер на него не реагирует. Без пула прокси тут делать нечего.

Публичные уязвимости: Гуглю название софта + "vulnerability". Нахожу отчёт восьмилетней давности о классической SQL-инъекции в cookie для обхода аутентификации:

content=text' OR '1'='1

. Пробую — дыра давно закрыта. Запускаю на этот параметр Intruder в Burp'е с пачкой популярных пейлоудов для SQLi — тоже безрезультатно.
Раз с аутентификацией всё так глухо, пора посмотреть, что происходит внутри приложения. Векторов два: атаки на серверную часть (RCE, LFI) и на клиентскую (XSS, CSRF).
XSS и CSRF
Проверяю на XSS. В дашборде отображаются разные данные, приходящие в

GET

-параметрах. Загоняю туда всевозможные пейлоуды из популярных списков — приложение всё добросовестно экранирует. Украсть cookie админа через XSS не выйдет.

А что насчёт CSRF (Cross-Site Request Forgery)? По мне так крайне скучная уязвимость, до боли банальная. Но именно на таких банальностях часто и прокалываются. Идея проста: заставить залогиненного пользователя перейти по нашей ссылке, и его браузер сам, от его имени, выполнит нужное нам действие.
Смотрю на критически важное действие — создание нового администратора. И что я вижу? А точнее, чего я не вижу? Никакой защиты от CSRF. Ни случайного токена, ни проверки заголовков. Ничего.

Запрос на создание админа выглядит так:

POST /admin/?object=account.create

с телом

application/json

:

https://forum.antichat.xyz/attachments/4920546/img_118f9d7840.png

Тут и кроется главная загвоздка. Обычная HTML-форма не может отправить запрос с

Content-Type: application/json

. Но есть старый трюк. С ним пришлось повозиться, но в итоге родился рабочий PoC.

HTML:




// Просто для удобства, можно и на чистом JS
// но jQuery гарантирует срабатывание на большинстве браузеров

$(document).ready(function() {
// Вся магия здесь. Мы меняем атрибут name на строку, которая является телом JSON.
// enctype="text/plain" заставит браузер отправить это как: {"type":...="me"}
// Нестрогий парсер на сервере съест это и обработает как валидный JSON.
$("#json_payload").attr("name", '{"type":"ADMIN","preferences":{"timezone":"EST","language":"ca"},"login":"hacked_admin","new_password":"P@ssw0rd123","new_password_confirmation":"P@ssw0rd123"}{"comment":"');

// Отправляем форму без ведома пользователя
$("#csrf_form").submit();

});



Вот такой костыль. Выглядит странно, но именно эта вариация стабильно работала после всех тестов. Серверный парсер оказался достаточно "гибким", чтобы проглотить такой запрос.
Доставка
Эксплойт готов и протестирован в лабе. Осталось доставить его жертве. Подозрительные ссылки никто открывать не будет. Поэтому покупаем домен, созвучный с рабочей тематикой конторы, накидываем простенький сайт-визитку, внедряем наш скрипт.

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

hacked_admin

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

Но если серьезно, вот конкретные шаги для защиты от подобной CSRF-атаки на JSON API:

SameSite Cookies: Самый эффективный современный метод. Установить для сессионной cookie атрибут

SameSite=Strict

или

SameSite=Lax

. В режиме

Lax

(дефолт во многих браузерах) cookie не будут отправляться при межсайтовых POST-запросах. Это убило бы нашу атаку на корню.

Anti-CSRF токены: Классика. Генерировать случайный токен для каждой сессии, встраивать его в защищаемые формы/запросы и проверять на сервере.

Проверка заголовка

Content-Type

: Сервер должен строго проверять, что

Content-Type

входящего запроса — именно

application/json

. Наш трюк с

text/plain

бы не прошел.

Проверка заголовков

Origin

или

Referer

: Дополнительный рубеж обороны. Сервер может проверять, что запрос пришел с доверенного домена, а не с

evil.com

.
Я остался доволен своей работой. А бывшее начальство получило хороший (и дорогой) урок по кибербезопасности.

f22
11.07.2022, 06:01
Vavilen сказал(а):

Чтобы не привлекать внимания жертвы, было принято решение развернуть аналогичный софт в лаборатории.





Vavilen сказал(а):

Взлом Wi-Fi для перехвата трафика
Примечательно, что по умолчанию подключение происходит по HTTP. Теоретически возможно взломать Wi-Fi роутер и перехватить трафик в plain text, но не наш случай, т.к. пользователи используют USB мобильные модемы.


Взломать роутер в собственной лаборатории?
Зачем нужно ломать роутер, если можно установить Wireshark или написать собственный сниффер?

Да, и что можно было найти в своём трафике, если требуются кукисы или учетка админа?



Vavilen сказал(а):

Слабая защита от грубой силы
Проверяю защиту от брутфорса admin/admin admin/password ... — через 5 попыток блокировка по IP. Попробовал трюк с обходом блокировки по IP. Добавил в Head заголовок типа X-Forwarded-For, сервер не реагирует. Такое обходить будет проблематично, без использования пакета прокси или написание софта.


Учитывая, что пароль оказался "PASSWORD", брутфорс тут не такая уже и плохая идея.
Поднять tor, proxychains и написать банальный bash script, отправляющий по 5 запросов и меняющий IP не составит большого труда.
Как минимум можно было поставить такое задание в фон



Vavilen сказал(а):

перехватить трафик в plain text,





Vavilen сказал(а):

Есть вариант с семидневным пробным периодом с предоставлением VPS. Очень удобно для наших тестов.


какой же тут будет plain text, если подключение идёт через VPS?



Vavilen сказал(а):

"new_password":"123=","new_password_confirmation":"123'


Как же будет создана админка, если переданный пароль и его подтверждение разные?
Да и кавычки должны быть парными... такой запрос, похоже, кроме ошибки ничего не создаст



Vavilen сказал(а):

При создании запроса на создание админки идёт

POST

запрос

/admin/?object=account.create

с телом:



А почему адреса-то разные?



Vavilen сказал(а):

Формируется POST запрос на сервер жертвы, т. к. жертва залогинена, подставляются легитимные куки.





Vavilen сказал(а):

При создании запроса на создание админки идёт

POST

запрос

/admin/?object=account.create

с телом:


Что-то не совсем понятно, а как пользователь, не являющийся админом, запросит создание админки и мало того, ещё и создаст её?

Vavilen
11.07.2022, 10:43
Привет!
У нас есть: реальная цель, где пользователь сидит за свои пк и отправляет запросы на VPS, а так же лабаратория, где можно затестить софт.


f22 сказал(а):

Взломать роутер в собственной лаборатории?


Нет, не в собственной. Заметив что в собственной HTTP, разумно предположить, что у реального пользователя тоже HTTP.


f22 сказал(а):

какой же тут будет plain text, если подключение идёт через VPS?


Я представляю это так: формируется запрос в локальной сети, который уходит на VPS. В момент передвижения HTTP пакета по сети, его можно перехватить. В открытом виде можно перехватить логин/пароль/куку.


f22 сказал(а):

Учитывая, что пароль оказался "PASSWORD", брутфорс тут не такая уже и плохая идея.


бро, это пример. Пароль выдаётся вида q1z8JvsQ9xjyC7FL. На скрине с бурпа - скрин где я отправляю запрос на создание админки я передаю NAME PASSWORD в качестве кредов.


f22 сказал(а):

Поднять tor, proxychains и написать банальный bash script, отправляющий по 5 запросов и меняющий IP не составит большого труда.
Как минимум можно было поставить такое задание в фон


Да, именно над таким решением я работал. Но посчитал его нецелесообразным, т.к. стандартный пароль вида q1z8JvsQ9xjyC7FL мне не подобрать.


f22 сказал(а):

Как же будет создана админка, если переданный пароль и его подтверждение разные?
Да и кавычки должны быть парными... такой запрос, похоже, кроме ошибки ничего не создаст


Повторюсь: Такой воn получился PoC. Выглядит битым, но именно такая вариация работала после всех тестов. Пароль один 123. Проверяйте в работе.


f22 сказал(а):

А почему адреса-то разные?


Потому что был найден баг на проекте, сдан разрабам. Была предпринята попытка изменить код, чтобы его нельзя было идентифицировать. Банальная опечатка, сути не меняет.


f22 сказал(а):

Что-то не совсем понятно, а как пользователь, не являющийся админом, запросит создание админки и мало того, ещё и создаст её?


Вот это более менее дельный вопрос. Т.к. атака точечная, мы кидаем ссылку непосредственно админу. На этапе доставке мы пишем на корпоративную почту цели. Конечно, цель должна быть залогинена в браузере + у неё должны быть привилегии админа.

f22
11.07.2022, 11:52
Привет!)


Vavilen сказал(а):

У нас есть: реальная цель, где пользователь сидит за свои пк и отправляет запросы на VPS, а так же лабаратория, где можно затестить софт.





Vavilen сказал(а):

Я представляю это так: формируется запрос в локальной сети, который уходит на VPS. В момент передвижения HTTP пакета по сети, его можно перехватить. В открытом виде можно перехватить логин/пароль/куку.


А что в вашем случае подразумевается под VPS?
Это просто какая-то машина, на которой может работать пользователь?
Или речь идёт о поднятом VPN на VPS?



Vavilen сказал(а):

Нет, не в собственной. Заметив что в собственной HTTP, разумно предположить, что у реального пользователя тоже HTTP.





Vavilen сказал(а):

Чтобы не привлекать внимания жертвы, было принято решение развернуть аналогичный софт в лаборатории.


Ну если лаборатория ваша, то роутер тоже ваш, поэтому повторюсь: зачем нужно ломать роутер, если все данные от лабораторной машины передаются к роутеру по вашей сети (если опять же это не VPN)



Vavilen сказал(а):

бро, это пример. Пароль выдаётся вида q1z8JvsQ9xjyC7FL. На скрине с бурпа - скрин где я отправляю запрос на создание админки я передаю NAME PASSWORD в качестве кредов.


Так надо было и скрин делать с другим паролем )



Vavilen сказал(а):

Если пользователь с легитимной сессией перейдет по ссылке, то при срабатывании скрипта на

evil.com

отправится

POST

запрос с подставкой куки пользователя.


Куда логичнее перехватить эти самые кукисы и самому создать пользователя, а ещё проще выполнить нужные действия, подставляя эти самые кукисы.



Vavilen сказал(а):

Повторюсь: Такой воn получился PoC. Выглядит битым, но именно такая вариация работала после всех тестов. Пароль один 123. Проверяйте в работе.


Подобным объяснением можно объяснить всё, что угодно, пруфов-то нет )

Вот только почему же у вас в Burp'e тогда запрос не битый?

Как вы смогли догадаться, что он сработал? Вы этот момент опишите

А если этот ваш запрос сработал, зачем нужно было атаковать пользователя, у вас же уже есть админка?

Vavilen
11.07.2022, 13:10
f22 сказал(а):

А что в вашем случае подразумевается под VPS?
Это просто какая-то машина, на которой может работать пользователь?
Или речь идёт о поднятом VPN на VPS?


Под VPS я понимаю: Virtual Private Server, услуга, предоставления вычислительной мощности с публичным айпи. Сервер. На этом сервере поднято веб-приложение. Никакого VPN.


f22 сказал(а):

Ну если лаборатория ваша, то роутер тоже ваш, поэтому повторюсь: зачем нужно ломать роутер, если все данные от лабораторной машины передаются к роутеру по вашей сети (если опять же это не VPN)


В данном примере, я обсуждал теоретическую атаку на локальную сеть конторы. От локальной сети жертвы до их "VPS".


f22 сказал(а):

Так надо было и скрин делать с другим паролем )


Так это скриншот тестового запроса. Я создал запрос, чтобы перехватить пост запрос на создание акка.


f22 сказал(а):

Куда логичнее перехватить эти самые кукисы и самому создать пользователя, а ещё проще выполнить нужные действия, подставляя эти самые кукисы.


Очень интересно. Расскажи в теории, как в данном случае перехватить куки? Вариант с перехватом трафика отпадает. Да, у нас два варианта для достижения цели. Перехват куки для доступа к его админке или создание нового акка.



f22 сказал(а):

Подобным объяснением можно объяснить всё, что угодно, пруфов-то нет )


PoC - proof of concept, ну русском доказательство.
Вы можете протестировать мой PoC, увидеть что генерируется правильный запрос. Обратите внимание на закрытие скобки




Только что поднял HTTP, и сгенерировал пост запрос. Пароль по итогу 123=.
Да я видел, как и вы гору однообразных CSRF PoC. Но я вас уверяю, попробовав своими руками вы убедитесь, что например там прилетает откуда не возьмись символ равно, и PoC не работает. Я вам предлагаею рабочий варинт, который подтверждает мою находку. Ей богу как при сдаче на hackerOne нахожусь

https://forum.antichat.xyz/attachments/4920567/1657530367284.png



f22 сказал(а):

Вот только почему же у вас в Burp'e тогда запрос не битый?
Как вы смогли догадаться, что он сработал? Вы этот момент опишите
А если этот ваш запрос сработал, зачем нужно было атаковать пользователя, у вас же уже есть админка?


Я догадался что он сработал потому что... я его тестировал...

У меня есть админка в моей тестовой лаборатории, а настоящая цель использует свою личную инфраструктуру.