diff --git a/src/pentesting-web/orm-injection.md b/src/pentesting-web/orm-injection.md index 70b9a6c8c12..3638308deae 100644 --- a/src/pentesting-web/orm-injection.md +++ b/src/pentesting-web/orm-injection.md @@ -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/?where[updatedBy][email][$startsWith]=adm +GET /api/?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":"admin@example.com"} + +POST /admin/reset-password +{"resetPasswordToken":"","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/ +GET /api/?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 @@ -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}}