Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions src/pentesting-web/orm-injection.md
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,64 @@ Look for flows where:
- Extended body/query parsers stay enabled and accept bracket syntax.
- Handlers forward user JSON directly into Prisma instead of mapping onto allow-listed fields/operators.

## Strapi Content API `where` smuggling (NodeJS)

Strapi's public Content API is a good example of an **ORM/query-builder injection without direct SQL injection**. In vulnerable `@strapi/strapi` versions **4.0.0 through 5.36.1**, the Content API validated/sanitized documented keys such as `filters`, `sort`, `fields`, and `populate`, but **ignored unknown top-level keys** instead of rejecting them. Later, the query transformer preserved those unknown keys via `...rest` and forwarded them into the internal database query builder.

That means a public request can smuggle a real `where` tree even though `where` is **not** a documented Content API parameter:

```http
GET /api/articles?where[updatedBy][resetPasswordToken][$startsWith]=d
```

Why this is dangerous:

- **Relation traversal**: `updatedBy` / `createdBy` joins the public collection with `admin_users`.
- **Private-field probing**: fields such as `resetPasswordToken` and `email` stay hidden in the JSON response, but they still affect the database predicate.
- **Boolean oracle**: Strapi exposes `meta.pagination.total`, so `total > 0` means the guessed prefix matched at least one related admin row.

Typical leak probes:

```http
GET /api/<collection>?where[updatedBy][email][$startsWith]=adm
GET /api/<collection>?where[updatedBy][resetPasswordToken][$startsWith]=deadbeef
```

This is enough to brute-force secrets one character at a time. In Bishop Fox's writeup, the leaked `resetPasswordToken` was then chained with the normal unauthenticated admin reset flow:

```http
POST /admin/forgot-password
{"email":"[email protected]"}

POST /admin/reset-password
{"resetPasswordToken":"<leaked-token>","password":"<new-password>"}
```

This turns the leak oracle into **administrator account takeover** and yields a valid admin JWT.

### Safe differential check

To confirm the bug without resetting any password, compare a baseline request with an always-false injected predicate:

```http
GET /api/<collection>
GET /api/<collection>?where[id][$lt]=-1
```

On a vulnerable target, both often return `200`, but the second request changes `meta.pagination.total` (commonly to `0`). On patched targets, the unknown `where` key is stripped/rejected, so the total stays equal to the baseline.

### Audit notes

Look for these conditions in Node/TypeScript APIs, not only in Strapi:

- Validators only inspect **known keys** and silently keep unknown ones.
- Query transformers merge user input with `...rest` / object spread before the ORM sink.
- Public objects have relations to internal/auth tables (`updatedBy`, `createdBy`, `owner`, `user`, `admin`).
- The response exposes an oracle such as **row presence**, **count**, **pagination metadata**, or **timing**.
- Password-reset or magic-link flows can be chained once a stored token becomes enumerable.

Strapi fixed this class in **5.37.0** by allow-listing valid Content API query keys and enabling strict top-level parameter enforcement in the framework controllers.

## Entity Framework & OData Filter Leaks

### Reflection-based text helpers leak secrets
Expand Down Expand Up @@ -431,6 +489,8 @@ Calibrating payloads to the real collation avoids wasted probes and significantl
- [https://www.elttam.com/blog/plorming-your-primsa-orm/](https://www.elttam.com/blog/plorming-your-primsa-orm/)
- [https://www.elttam.com/blog/leaking-more-than-you-joined-for/](https://www.elttam.com/blog/leaking-more-than-you-joined-for/)
- [https://positive.security/blog/ransack-data-exfiltration](https://positive.security/blog/ransack-data-exfiltration)
- [https://bishopfox.com/blog/cve-2026-27886-unauthenticated-boolean-oracle-exfiltration-of-administrator-secrets-in-strapi](https://bishopfox.com/blog/cve-2026-27886-unauthenticated-boolean-oracle-exfiltration-of-administrator-secrets-in-strapi)
- [https://github.com/advisories/GHSA-rjg2-95x7-8qmx](https://github.com/advisories/GHSA-rjg2-95x7-8qmx)

{{#include ../banners/hacktricks-training.md}}

Expand Down