29 ноября появилась первая информация о новой критической уязвимости в серверном компоненте открытой библиотеки React — React server components (RSC). А также во множестве производных проектов и фреймворков: Next.js, React Router RSC preview, Redwood SDK, Waku, RSC-плагинах Vite и Parcel.
3 декабря на сайте React опубликовали статью о выпуске новых пропатченных версий с инструкциями по обновлению.
Этой уязвимости присвоен номер — CVE-2025-55182. Уязвимости Next.js также присвоили отдельный идентификатор — CVE-2025-66478. Но из-за того, что уязвимость исходит именно от библиотеки React, которая используется в основе Next.js, CVE-2025-66478 фактически дублирует CVE-2025-55182.
Уязвимость CVE-2025-55182 получила максимальную оценку опасности: CVSS 10.0.
Как это работает и почему опасно
React представляет собой бесплатную JavaScript-библиотеку с открытым кодом, применяющуюся для создания пользовательских веб-интерфейсов.
Из-за того, что в React отсутствует проверка перед парсингом метадаты на принадлежность метадаты самому серверу, злоумышленник может заставить сервер парсить отправленную в POST-запросе строку как команду.
При успешной эксплуатации уязвимости возможно удаленное выполнение кода (RCE, Remote Code Execution): злоумышленник без какой-либо авторизации может выполнить произвольный JS-код на сервере, запускать системные команды, читать и записывать файлы, получать доступ к переменным окружения и секретам — то есть фактически захватить сервер. Учитывая, что RSC/Next.js используются во многих приложениях и что стандартная конфигурация уязвима, масштаб потенциального ущерба огромный.
Для кого актуальна уязвимость
Данной уязвимости подвержены следующие пакеты React:
«react-server-dom-webpack»;
«react-server-dom-parcel»;
«react-server-dom-turbopack».
В версиях 19.0.0, 19.1.0, 19.1.1, 19.2.0, а также следующие версии Next.js : 15.0.4, 15.1.8, 15.2.5, 15.3.5, 15.4.7, 15.5.6, 16.0.6.
Проверка эксплуатации уязвимости
Чтобы убедиться в возможности эксплуатации рассматриваемой уязвимости и наглядно продемонстрировать, к каким последствиям это может привести, был развернут тестовый сервер с React уязвимой версии 19.0.0.
При эксплуатации уязвимости использовались три модуля JS: child_process (запуск дочерних процессов), fs (операции с файловой системой) и vm (выполнение кода в отдельном контексте JS) — так как данные модули позволяют проводить операции с окружением сервера путем выполнения различных команд.
Примеры выполнения команд напрямую к серверу
Модуль child_process
Первым протестированным модулем для проверки уязвимости CVE-2025-55182 стал child_process за счет того, что данный модуль позволяет напрямую выполнять команды в оболочке уязвимого сервера, открывая широкий простор для действий злоумышленника.
Для проверки модуля в первую очередь использовалась команда execSync, возвращающая вывод выполняемого действия и блокирующая поток выполнения JS до момента завершения операции (чтобы не повторяться далее, суффикс Sync в каждой команде указывает на выполнение команды с блокированием потока выполнения). С помощью execSync была выполнена команда echo для записи строки в создаваемый файл /opt/executor.txt.
Запрос к серверу:

Ответ от сервера:

Содержимое файла на сервере:

Как видно, уязвимый сервер выполнил отправленную команду и записал нужную строку в файл.
Другая команда, spawnSync, возвращает код выполнения команды на сервере, а не сами данные. С ее помощью была выполнена команда chmod +x для предоставления прав на выполнения скрипта script.sh на уязвимом сервере.
Запрос к серверу:

Ответ от сервера. Код выполнения команды виден в поле status, «0» означает успешное выполнение команды:

Результат выполнения команды на самом сервере — script.sh стал выполняемым файлом:

Модуль fs
В отличие от модуля child_process, модуль fs позволяет совершать операции с файлами без необходимости выполнять дополнительные linux-команды.
Для начала стоит попытаться прочитать файл с помощью команды readFileSync, которая позволяет вывести содержимое файлов, находящихся на сервере. В качестве примера одного из таких файлов был выбран /etc/passwd.
Запрос к серверу:

Ответ от сервера. Содержимое файла закодировано в Unicode:

Декодирование файла проводится с помощью онлайн-декодера:

Команды writeFileSync и appendFileSync выполняют схожие операции — изменяют файл. Но если writeFileSync полностью перезаписывает содержимое файла, то appendFileSync добавляет новую строку в конец файла, не трогая его содержимое. Обе команды были успешно выполнены.
Запрос к серверу с командой writeFileSync:

Ответ от сервера:

Записанные в файл строки на уязвимом сервере:

Запрос с командой appendFileSync, которая дописывает строку в файл /opt/executor.txt:

Ответ от сервера:

Результат выполнения команды на сервере:

Модуль vm
Последним из проверяемых модулей стал vm, позволяющий запускать JS-код в различных изолированных контекстах без доступа к глобальному окружению Node.js, однако такую изоляцию можно (и, в нашем случае, нужно) обойти.
Самый простой вариант обойти изоляцию — не изолировать себя вообще. То есть запустить команду в том же контексте, в котором запущен сервер. Для этого используется команда runInThisContext, с помощью которой формируется обращение к ранее использованному модулю child_process и команде execSync.
Для демонстрации успешности эксплуатации уязвимости была использована команда hostnamectl:

В ответ от сервера была получена информация об имени сервера, его ОС, архитектуре и версии ядра:

Второй вариант обхода изоляции был осуществлен при выполнении команды runInNewContext и использовании в ней структуры вида this.constructor.constructor(‘return process’)(), что позволяет получить доступ к глобальному окружению из изолированного контекста.
Для примера была предпринята попытка получить содержимое файла /etc/apt/sources.list с помощью команды cat и обращения все к тому же модулю child_process и команде execSync:

В ответ сервер возвращает содержимое всего файла:

Краткие выводы по результатам
Подводя небольшой итог, на незащищенном сервере с библиотекой React злоумышленник может провести широкий набор действий — от вывода имени сервера до выполнения собственных скриптов через оболочку сервера или даже полностью вывести сервер из строя.
Проверка эксплуатации уязвимости с использованием WAF
Теперь, когда удалось полностью убедиться в работоспособности уязвимости, необходимо проработать варианты того, как себя обезопасить от эксплуатации этой уязвимости на серверах, использующих библиотеку React. Самым правильным решением будет установка версии React/Next.js, в которой уже нет уязвимости. Однако возможны случаи, когда существует необходимость использования конкретной версии React на сервере, например, потому что на нее жестко завязана работа еще каких-либо модулей и обновление может привести к отказу сервера. В таком случае требуется вариант временного закрытия уязвимости, например, используя класс решений Web-Application Firewall или WAF, который создан для защиты веб-приложений. Для проверки того, как решение класса WAF позволяет бороться с эксплуатацией уязвимости, выбрано хорошо известное решение от производителя Positive Technologies — PT AF Pro версии 4.1.7
Сначала для проверок тестовый WAF использовался с правилами «из коробки», то есть без дополнительной настройки пользовательских правил. Описание процесса добавления веб-приложения на WAF в данной статье не рассматривается, так как не имеет отношения к эксплуатации уязвимости, однако стоит отметить, что у PT AF Pro сразу имеется шаблон политик безопасности для веб-приложений, использующих Node.js, который и был использован:

Примеры выполнения команд через WAF с настройками «из коробки»
Модуль child_process
Первая же проверка выполнения команды execSync показала неутешительный результат — PT AF Pro с правилами «из коробки» пропустил выполнение команды echo на сервере:

Ответ от сервера:

Результат выполнения команды на сервере. Строка успешно добавлена в файл /opt/executor.txt:

А вот проверка выполнения команды spawnSync показала уже хороший результат — выполнение команды было заблокировано:

Ответ от сервера. WAF вернул статус код 403 Forbidden и ID события в веб-консоли:

Модуль fs
Ни одна из выполняемых команд не дошла до сервера, все они были заблокированы правилом попытки выхода за пределы каталога WAF.
Первой была проверена команда readFileSync с попыткой прочитать файл /etc/passwd:

Ответ от сервера, статус-код 403 и ID события в веб-консоли WAF:

Описание блокировки запроса:

С командами writeFileSync и appendFileSync ситуация аналогичная: запросы заблокированы правилом попытки выхода за пределы каталога.
Запрос с командой writeFileSync:

Ответ от WAF:

Запрос с командой appendFileSync:

Ответ от WAF:

Модуль vm
Результат проверки команд модуля vm оказался не таким однозначным, как с модулем fs: одна из команд была заблокирована, а вот вторая была успешно выполнена, но с небольшой особенностью, о которой расскажем далее.
Команда runInThisContext сразу заблокирована как попытка выполнения межсайтового выполнения сценария, что не является корректным определением нашей попытки выполнить атаку, но тут скорее важна сама блокировка, а не корректное определение атаки.
Запрос с командой runInThisContext и попыткой выполнения команды hostnamectl:

Ответ от WAF:

Описание заблокированной атаки в веб-консоли:

Выполнение команды runInNewContext дало несколько неожиданный результат. Попытка доступа к файлу /etc/passwd была заблокирована, но не потому, что обнаружена атака в запросе, а из-за того, что в ответе присутствуют чувствительные данные:

Такое поведение WAF позволило нам сделать вывод, что команда runInNewContext в целом может быть успешно выполнена, если немного модифицировать вывод файла /etc/passwd. В модификации вывода нам поможет команда xxd с аргументом -p, позволяющая вывести содержимое файла в шестнадцатеричной кодировке, которая не должна корректно обрабатываться правилами PT AF Pro.
Запрос с командой runInNewContext с выводом файла /etc/passwd в шестнадцатеричной кодировке:

Ответ от сервера, в котором видно успешное выполнение передан��ой команды и вывод файла /etc/passwd в шестнадцатеричной кодировке:

Декодирование полученного вывода можно провести с помощью все тех же онлайн-декодеров:

Краткие выводы по результатам использования PT AF Pro с правилами «из коробки»
Как показали проверки, PT AF Pro без настроенных правил блокирует большую часть вариантов эксплуатации уязвимости, однако оставляет довольно ощутимую «дыру» в безопасности. А именно возможность выполнения команд execSync и runInNewContext, которые все еще позволяют злоумышленнику реализовать широкий набор действий с уязвимым сервером.
Примеры выполнения команд через PT AF Pro с пользовательским правилом
Для проведения следующих проверок на сервер WAF было загружено пользовательское правило, выпущенное производителем Positive Technologies 4 декабря специально для предотвращения эксплуатации уязвимости CVE-2025-55182.
Правило направлено на блокировку запросов с типом данных multipart/form-data, и специфичными для уязвимости конструкциями, такими как vm, fs, child_process, module или даже переменными из заголовка name $ACTION_REF_0 или $ACTION_0:0.
Добавление правила привело к закономерному результату: оставшиеся незаблокированными варианты эксплуатации уязвимости через выполнение команд execSync и runInNewContext теперь блокировались новым пользовательским правилом. В обходе правила не помогла ни кодировка части символов в Unicode (WAF их успешно расшифровал и заблокировал запрос), ни разбиение запроса на части.
Запрос с командой execSync:

Ответ от WAF:

Запрос с командой runInNewContext:

Ответ от WAF:

Событие в веб-консоли:

Попытка разделения запроса на части с помощью скрипта также привела к блокировке по добавленному правилу:

Краткие выводы по результатам использования PT AF Pro с использованием специализированного правила от производителя
C использованием пользовательских правил можно исключить возможность эксплуатации уязвимости и даже препятствовать наиболее распространенным вариантам обхода блокировок со стороны WAF путем замены символов на их кодировки в Unicode или разделению запроса на несколько частей. Однако такая мера по нейтрализации уязвимости может оказаться выполненной слишком «широкими мазками» и начать блокировать уже легитимные запросы.
Использование WAF от других производителей
Тестирование решений класса WAF от других производителей показало, что некоторые системы не способны обнаружить попытки эксплуатации уязвимости «из коробки» и требуют серьезных доработок правил безопасности. При этом даже при тонкой настройке таких WAF существует вероятность обхода правил блокировки с помощью кодирования символов, либо разделения запроса на несколько частей.
Тестирование проводилось только для on-prem вариантов WAF. В случае противодействия новейшим уязвимостям можно предположить, что поставщики WAF как сервиса будут разрабатывать компенсирующие правила оперативнее и точнее в силу более короткого времени получения обратной связи, но, по организационным причинам эту версию проверить не удалось.
Подведем итоги
Чтобы обезопасить себя от возможной эксплуатации рассматриваемой уязвимости, следует выполнить следующие действия:
Провести аудит и убедиться, что опубликованные наружу веб-приложения используют React/Next.js уязвимых версий.
Перейти на безопасные версии RSC-пакетов: 19.0.1, 19.1.2, 19.2.1.
Обновить Next.js до безопасных версий: 15.0.5, 15.1.9, 15.2.6, 15.3.6, 15.4.8, 15.5.7 или 16.0.7.
В случае если обновиться здесь и сейчас нет возможности, следует использовать решения класса Web Application Firewall (WAF) для блокировки попыток эксплуатации уязвимости злоумышленниками. Важно проводить тонкую настройку решения и использовать пользовательские правила, а не только правила «из коробки».
Пользовательские правила можно составить по совокупности следующих критериев:
+ тип запроса POST;
+ значение заголовка Content-type — multipart/form-data;
+ в теле запроса встречаются следующе конструкции: vm#, fs#, child_process, constructor, runInThisContext, runInNewContext, execSync, execFileSync, spawnSync, module#_load, module#createRequire, readFileSync, writeFileSync, appendFileSync.
Также важно помнить, что, несмотря на доказанную эффективность WAF для закрытия уязвимости, использование решений данного класса не является панацеей. Правила, которые производители WAF выпускают для оперативного закрытия, призваны отсечь широкий спектр возможных тактик злоумышленников, поэтому могут быть написаны «широкими мазками» и частично блокировать и легитимные запросы.
Самым правильным вариантом всегда будет обновление до версий ПО, в которых эта уязвимость уже устранена. Однако WAF защитит вас здесь и сейчас и даст дополнительное время на ожидание пропатченных версий и само обновление.
Автор:
Вадим Гаврилов, руководитель отдела систем обеспечения ИБ
