
Обсуждение анализа уязвимости React Server Components RCE
В последние два дня React уязвимость RCE из-за десериализации нашумела, официальный CVSS сразу выставил 10.0 баллов, наравне с Log4j в своё время. Сразу же появились слухи, что это современный Log4j для фронтенда, что вызвало панику у многих разработчиков компаний, и все, проснувшись, начали штудировать документацию и ставить патчи... В то же время в сети появилось немало сомнений, некоторые после тестирования обнаружили, что уязвимость не так серьезна, как рекламируется, наоборот, для её эксплуатации требуются определённые условия. Поэтому я решил выделить время и глубоко изучить эту уязвимость.
react-server-dom-webpack < 19.2.0, react-server-dom-turbopack < 19.2.0Причина возникновения данной уязвимости следующая: в [email protected] ключевая функция для парсинга Server Action на сервере — requireModule (псевдокод):
function requireModule(metadata) {
var moduleExports = __webpack_require__(metadata[0]);
// ...
return "*" === metadata[2]
? moduleExports
: "" === metadata[2]
? moduleExports.__esModule
? moduleExports.default
: moduleExports
: moduleExports[metadata[2]]; // ← Точка уязвимости
}
Основная проблема заключается в части moduleExports[metadata[2]]: отсутствует проверка metadata[2], что позволяет атакующему получать доступ не только к собственным экспортируемым свойствам модуля, но и к свойствам цепочки прототипов (например, constructor, __proto__ и т.д.). Когда атакующий конструирует metadata[0] (например, указывая на vm), он может снова сконструировать metadata[2], тем самым экспортировать опасные методы из указанного модуля, например, vm.runInThisContext, что приводит к эксплуатации уязвимости.
При анализе я обращался к тестовой среде и эксплуатации, предоставленным ejpir, и в качестве примера использовал gadget vm_runInThisContext для Code Execution. Процесс описан ниже (обратите внимание: в реальной среде процесс эксплуатации может отличаться!).
Сначала после отправки запроса с полезной нагрузкой ставим точку останова на месте получения запроса:


Затем программа доходит до const formData = parseMultipart(buffer, boundaryMatch[1]);, заходим в parseMultipart

parseMultipart извлекает данные тела запроса и возвращает их в formData

Заходим в const actionFn = await decodeAction(formData, serverManifest); Место возникновения уязвимости

Заходим в loadServerReference


Теперь дошли до ключевого места кода уязвимости requireModule, заходим


Возвращает значение id, разделённое символом #, как метод модуля, и значение параметра bound как аргумент метода




Заходим в actionFn, выполняем финальную полезную нагрузку



На этом эксплуатация уязвимости завершена!
Эта уязвимость, по сути, также вызвана недостаточно строгой проверкой входных данных — так же, как и в случае с Log4j и fastjson. Я тестировал vm_runInThisContext, но на самом деле у этой уязвимости есть несколько доступных гаджетов, например:
vm#runInThisContextvm#runInNewContextchild_process#execSyncchild_process#execFileSyncchild_process#spawnSyncfs#readFileSyncfs#writeFileSync#constructor#__proto__#prototypeАтакующий может использовать эту уязвимость для:
vm#runInThisContext или child_process#execSync выполнять произвольные системные командыfs#readFileSync, fs#writeFileSync читать/записывать произвольные файлы.bashrc, перезапись файлов приложения и т.д..env, закрытые ключи, учетные данные базы данных и т.д.)Исходя из этого, можно дать следующие меры защиты:
В качестве временной защиты можно настроить на WAF правила блокировки опасных полей для своевременного перехвата вредоносных атак. Также можно настроить блокировку на nginx, как показано ниже:
# Пример конфигурации Nginx
location /formaction {
# Блокируем запросы, содержащие ссылки на опасные модули
if ($request_body ~* "(vm#|child_process#|fs#|module#)") {
return 403;
}
# Блокируем попытки загрязнения прототипа
if ($request_body ~* "(#constructor|#__proto__|#prototype)") {
return 403;
}
}
Официально выпущены обновления безопасности, немедленно обновитесь до безопасной версии!:
# Обновление react-server-dom-webpack
npm install react-server-dom-webpack@>=19.2.0
# Обновление react-server-dom-turbopack
npm install react-server-dom-turbopack@>=19.2.0
# Для пользователей Next.js
npm install next@>=15.0.5
Исправленные версии:
react-server-dom-webpack: >= 19.2.0react-server-dom-turbopack: >= 19.2.0next.js: >= 15.0.5При анализе вышеуказанной уязвимости я обращался к соответствующему эксплойту от whiteov3rflow, дополнительно написал инструмент для обнаружения уязвимости в тестовой среде и разместил его в репозитории на GitHub.
Желающие могут проверить себя, перейдя по ссылке (обратите внимание: из-за особенностей тестовой среды автора, в настоящее время инструмент может подходить только для оригинальной тестовой среды; будет доработан позже, при необходимости можно взять и доработать самостоятельно...). Пожалуйста, используйте только при наличии законного разрешения, запрещено несанкционированное разрушительное использование!
На момент написания (5 декабря 2025 года) я вижу в интернете, что вокруг этой уязвимости "шумиха" то поднимается, то спадает: то "ядерная бомба", то "незначительная дыра", через некоторое время снова "ядерная бомба"... Способы эксплуатации уязвимости множатся. По имеющимся данным, "ядерная бомба" может подтвердиться, но сфера влияния меньше, чем у Log4j. Тем не менее, всем, кого это касается, следует как можно скорее обновиться, чтобы устранить угрозу!!!
Также даю совет по безопасности разработчикам: никогда не доверяйте пользовательскому вводу. Log4j, fastjson и текущий ReactRCE пострадали именно из-за этого. Поэтому при реальной разработке бизнес-функций для опасных мест обязательно использовать песочницы или белые списки для строгой проверки, чтобы избежать трагедии!!!
Теория — это бледно, а практика требует осторожности.