Раньше я не имел привычки писать о своих находках, но теперь решил время от времени делиться наиболее интересными. Это мой первый разбор одной из моих последних находок, поэтому любые предложения или вопросы более чем приветствуются!
В этом разборе я покажу вам, как ошибка в конфигурации CORS привела к полному захвату аккаунта (ATO) и обходу двухфакторной аутентификации. Без лишних слов, перейдем к истории.
Я скрою название цели (довольно популярной), но отмечу, что один из ее сервисов связан с механизмом безопасности, предназначенным для защиты веб-сайтов и приложений при сохранении конфиденциальности пользователей. Этот механизм разработан для предотвращения автоматизированных злоупотреблений и атак, при этом обеспечивает легитимное взаимодействие с веб-сайтом без чрезмерного отслеживания или сбора данных.
Методология
Я не проводил никакой разведки, вместо этого я сразу сосредоточился на основном домене. Я начал с посещения страницы, чтобы создать новый аккаунт и завершить процесс регистрации.
Цель предлагает различные тарифные планы, от бесплатных услуг до корпоративных решений. Я выбрал версию «Pro» и активировал бесплатный пробный период.
После завершения регистрации я вошел в систему с использованием своих учетных данных и был перенаправлен на страницу. Там я начал изучать различные функции. В настройках профиля я активировал двухфакторную аутентификацию (2FA) и добавил ключ безопасности как дополнительный метод аутентификации, что позволяет входить в систему без ввода пароля каждый раз.
Настроив аккаунт, я продолжил исследовать приложение как обычный пользователь, направляя трафик через Burp Suite.
После некоторого времени, проведенного в панели управления, в истории Burp я наткнулся на GET-запрос. Ответ содержал JSON с конфиденциальной информацией, включая тип пользователя, адрес электронной почты, зашифрованный email, параметры «избранное» и «забаненные».
Однако самая критическая проблема заключалась в том, что ответ также включал действительный токен сессии в заголовке ответа Set-Cookie — именно здесь начался настоящий хаос.
До этого момента все казалось довольно неплохим. Однако наличие Access-Control-Allow-Origin: https://accounts.target.com вместе с Access-Control-Allow-Credentials: true в ответе сервера сразу привлекло мое внимание. С этого момента я отложил все остальные идеи и начал проверять на наличие потенциальной проблемы с CORS.
Понимание неправильных конфигураций CORS и их влияние на безопасность
Описание
Cross-Origin Resource Sharing (CORS) — это механизм безопасности, применяемый веб-браузерами для контроля доступа к ресурсам из различных источников, который дополняет Same-Origin Policy (SOP).
Неправильные конфигурации возникают, когда сервер неправильно допускает кросс-доменные запросы, что потенциально может привести к раскрытию конфиденциальных данных при переходе на неизвестные сайты. Обычно это происходит, когда универсальные источники (*), доверенные поддомены или неправильно настроенные заголовки Access-Control-Allow-Origin и Access-Control-Allow-Credentials предоставляют непреднамеренный доступ. Злоумышленники могут использовать это, чтобы похитить данные пользователей, захватить сессии или выполнять несанкционированные действия от имени жертвы, обходя таким образом SOP защиту.
Исходный запрос и ответ выглядели так:

Я многократно изменял заголовок Origin, пытаясь проэксплуатировать неправильную конфигурацию CORS, но все мои попытки были отклонены — постоянный отказ в доступе неавторизованным источникам. Однако, когда я заменил заголовок Origin на домен, которому цель явно доверяет (перебрал список доверенных источников), я заметил, что он был отражен в ответе. Интересно, что это был единственный принятый субдомен.
Затем я повторил все свои предыдущие техники для изменения заголовка Origin, но ничего не изменилось. В каждом случае, когда источник был неверным, заголовки Access-Control-Allow-Origin и Access-Control-Allow-Credentials просто отсутствовали в ответе.
Прорыв произошел, когда я обнаружил, что добавление (конкатенация) любой строки к доверенному субдомену все равно приводит к тому, что запрос принимается. Это работало как с https, так и с http, например:
Origin: https://blablasub.target.com
Origin: http://blablasub.target.com
Этого было достаточно, чтобы подготовить доказательство концепции (proof of concept). Я создал XMLHttpRequest к уязвимому эндпоинту, включив учетные данные в запрос, и успешно прочитал конфиденциальный ответ с моего произвольного источника. Чтобы продемонстрировать влияние (impact), я разместил вредоносный скрипт на своем сервере, который отправляет запрос (https://blasub.target.com) для получения данных и извлечения ответа.
Скрипт выглядел следующим образом:
<!DOCTYPE html>
<html>
<body>
<center>
<h1>CORS PoC Exploit</h1>
<div id="demo">
<button type="button" onclick="cors()">Click Me</button>
</div>
</center>
<script>
function cors() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("demo").innerHTML = alert(this.responseText);
} else if (this.readyState == 4) {
document.getElementById("demo").innerHTML = document.write("Error: " + this.status);
}
};
var url = "
https://accounts.target.com/auth/pro/allowed_accounts
";
xhttp.open
("GET", url, true);
xhttp.withCredentials = true;
var requestBody = JSON.stringify({
});
xhttp.send(requestBody);
}
</script>
</body>
</html>
PoC с моего источника

На этом этапе у меня была неправильная конфигурация CORS, приводящая к раскрытию конфиденциальных данных произвольным источникам, что, скорее всего, будет принято как проблема средней или высокой степени серьезности. Однако вместо того чтобы немедленно сообщить об этом, я решил копнуть глубже, надеясь развить ситуацию в полный захват учетной записи (ATO).
Забавная заметка: Как видно на скриншоте выше, запрос требует отправки X-Csrf-Token, чтобы он прошел успешно. Однако я обнаружил, что полное исключение этого токена из запроса все равно позволяло мне получить корректный ответ — без какой-либо проверки аутентификации или авторизации! Это само по себе является отдельной уязвимостью, потому что мне не нужно было получать этот токен или пытаться его угадать.
Важная заметка: Всякий раз, когда вы сталкиваетесь с проблемой CORS, особенно если Origin динамически отображается в ответе, не торопитесь с отчетом — изучите проблему внимательнее. Возможно, вы обнаружите еще что-то более значимое.
Механизм входа в систему
Напомню, после активации 2fa (двухфакторной аутентификации) вход в приложение будет выполняться следующим образом:
1. Пользователь посещает https://target.com/login.
2. Приложение предлагает пользователю выбрать метод входа:
2.1. Аутентификация с помощью ключа доступа
-
Пользователь вводит ключ доступа (предопределенное слово, биометрическую верификацию и т.д.).
-
В случае успешного выполнения он входит в систему.
2.2. Двухфакторная аутентификация (2FA)
-
Пользователь вводит свой адрес электронной почты.
-
Пользователь вводит свой пароль.
-
Пользователь вводит код 2FA.
-
Если все шаги пройдены успешно, он входит в систему.
Предзахват учетной записи
Как упоминалось ранее, ответ содержит действительный session cookie, который помечен как HttpOnly, чтобы предотвратить доступ к нему с помощью JavaScript.
Обычно это защищает cookie от кражи через клиентские скрипты. Однако в моем случае сам ответ предоставлял сессионный токен в заголовке. Хотя cookie автоматически отправлялся с каждым запросом, сервер генерировал его заново в ответе, фактически раскрывая его. Это означало, что мне не нужно было использовать какой-либо язык серверной разработки для его извлечения — он уже был записан в логах моего сервера.
Захват учетной записи
Теперь я был всего в одном шаге от полного захвата учетной записи (ATO), так как при аутентификации пользователя, приложение полностью полагалось на этот сессионный cookie.
Получив его, я открыл новую вкладку в режиме инкогнито (используя другой браузер) и вручную внедрил cookie в хранилище браузера. После обновления страницы я успешно вошел в систему как жертва — получив полный контроль над учетной записью, полностью обойдя процесс аутентификации, включая 2FA и Passkey.
Вы, возможно, думаете о том же, о чем и я — может ли это быть ложноположительным срабатыванием? Чтобы проверить, я переключился на другую сеть с новым IP-адресом и повторил попытку, используя сессионный cookie.

Удивительно, но это всё ещё работало. Сессия оставалась действительной, что позволило мне снова войти в систему как жертва. Это было неожиданно, потому что, согласно политике безопасности приложения, сессионные токены должны были быть привязаны к IP-адресу и непригодны для повторного использования после входа. Очевидно, это было не так.
На этом этапе я задокументировал все детали и составил подробный отчет, в котором описал уязвимости и их потенциальное воздействие. Затем я сообщил компании о проблемах, чтобы они были в курсе рисков и могли предпринять соответствующие меры для их смягчения.
Хронология
-
Дата подачи: Среда, 19 февраля 2025 года — 10:26
-
Первый ответ: Среда, 19 февраля 2025 года — 13:51
-
Проблема обсуждена: Среда, 19 февраля 2025 года — 22:03 (Частичное исправление, устранена проблема с CORS)
-
Исправления: Воскресенье, 9 марта 2025 года — 9:13
-
Выплачено вознаграждение: $$$$ — Суббота, 14 марта 2025 года — 13:57
-
Статус:
Исправлено

ссылка на оригинал статьи https://habr.com/ru/articles/896684/
Добавить комментарий