Apr 16, 2026·6 min read·4 visits
Unauthenticated attackers can bypass API projections in ApostropheCMS <= 4.28.0 using the 'choices' and 'counts' parameters to exfiltrate restricted field data.
ApostropheCMS versions 4.28.0 and prior contain an authorization bypass vulnerability in the REST API's 'choices' and 'counts' query builders. These parameters execute MongoDB aggregation operations that bypass configured public API projections, permitting unauthenticated attackers to extract distinct values for restricted schema fields.
ApostropheCMS versions 4.28.0 and prior suffer from an authorization bypass vulnerability within the core REST API implementation. The flaw resides specifically in the handling of the choices and counts query parameters. These parameters are designed to provide aggregated metadata about available documents, supporting frontend features like dynamic filtering.
The vulnerability allows unauthenticated attackers to extract distinct data values for database fields that are otherwise protected by access controls. ApostropheCMS relies on a publicApiProjection mechanism to strip sensitive fields from standard API responses. The choices and counts endpoints fail to respect these projection constraints.
By exploiting this oversight, an external entity can enumerate all unique entries for any defined schema field. The exposed data types encompass strings, integers, floats, booleans, dates, slugs, and relationship identifiers. Fields protected by specific viewPermission directives are similarly vulnerable to this extraction method.
The risk is categorized as Medium severity (CVSS 5.3) due to its remote, unauthenticated nature, though the impact is restricted entirely to data confidentiality. The maintainers resolved the issue in version 4.29.0 by introducing explicit projection validation within the aggregation pipeline.
The standard API response lifecycle in ApostropheCMS applies a publicApiProjection before returning JSON output to unauthenticated clients. This projection dictates exactly which MongoDB fields are permissible for public transmission. Standard document retrieval queries inherently enforce this rule by limiting the cursor output.
The core issue stems from the divergent execution path used by the choices and counts query builders, located in the @apostrophecms/doc-type module. To fulfill requests for unique field values, the application invokes MongoDB's distinct() and aggregation framework commands. These commands compute unique value sets directly on the database server.
MongoDB's distinct operation operates independently of query projections. It scans the targeted collection and returns an array of unique values for a given field key. The ApostropheCMS application code passed the user-supplied field name directly to this aggregation layer without first validating it against the publicApiProjection map.
Furthermore, the authorization checks responsible for evaluating viewPermission at the document level were entirely omitted from the aggregation execution flow. The application implicitly trusted the parameters passed to the metadata endpoints, resulting in the authorization bypass.
Prior to version 4.29.0, the query builder directly processed the choices and counts requests without intercepting the requested schema fields for access validation. The aggregation methods executed without any context regarding the current user's role or the public visibility state of the endpoint.
The fix implemented in commit 6c2b548dec2e3f7a82e8e16736603f4cd17525aa introduces necessary validation layers immediately before database execution. The patch adds the choicesFieldAllowedByProjection method to verify authorization dynamically.
// packages/apostrophe/modules/@apostrophecms/doc-type/index.js
choicesFieldAllowedByProjection(filter, projection) {
if (!projection || !Object.keys(projection).length) {
return true;
}
const field = self.schema.find(f => f.name === filter);
if (!field) {
return true;
}
const topLevel = filter.split('.')[0];
const values = Object.values(projection);
const hasInclusion = values.some(v => v && v !== 0);
const hasExclusion = values.some(v => v === 0 || v === false);
if (hasInclusion) {
return Boolean(projection[topLevel]);
}
if (hasExclusion) {
return projection[topLevel] !== 0 && projection[topLevel] !== false;
}
return true;
}This method parses the projection object to support MongoDB's mixed inclusion and exclusion syntax. If the projection explicitly defines fields to include, it verifies the requested field is present. If the projection defines fields to exclude, it ensures the requested field is not explicitly forbidden. A parallel method, choicesFieldAllowedByViewPermission, was added to validate granular field-level permissions.
Exploitation of CVE-2026-39857 requires network connectivity to a publicly accessible REST API endpoint managed by an unpatched ApostropheCMS instance. The attacker does not require authentication or specific user roles. The attack vector targets both the piece-type and page REST APIs.
The exploit methodology relies on appending malicious query parameters to standard API GET requests. An attacker identifies a target schema and constructs a URL such as https://target.local/api/v1/users?choices=internalRole. The backend application processes the choices parameter and initiates the flawed aggregation logic.
The database engine processes the distinct() command against the requested field and returns the results to the application layer. The application then wraps these results in a JSON array and delivers them to the attacker. The response contains all unique values for internalRole present across all documents in the collection, including unpublished or archived records.
Attackers can extend this technique using the counts parameter. Appending ?counts=internalRole returns the distinct values alongside an integer count representing the number of documents containing each value. This enables an attacker to map the internal state and distribution of sensitive database entries efficiently.
The primary impact of this vulnerability is the unauthorized disclosure of confidential data stored within the ApostropheCMS database. The flaw permits the extraction of data across numerous field types, including text strings, numerical values, and boolean flags.
The severity of the disclosure depends on the specific data schemas implemented by the deployment. Exposure of internal operational statuses, draft titles, or hidden administrative boolean flags can compromise business logic and provide a foundation for subsequent attacks. Relationship fields are also vulnerable, meaning attackers can map internal document relationships and discover unique object identifiers.
The vulnerability is constrained by the nature of the MongoDB distinct operation. Attackers can only extract unordered lists of unique values for a specific field. They cannot retrieve complete, correlated documents or modify any data on the server.
The CVSS v3.1 base score is calculated at 5.3. This reflects the low attack complexity and lack of required authentication, mitigated by the limitation to confidentiality impact. The EPSS score is 0.00037, indicating low expected exploitation volume in the wild.
The official remediation requires upgrading ApostropheCMS to version 4.29.0 or later. This release incorporates the explicit projection validation checks within the choices and counts query execution flow. Upgrading ensures that all database aggregation commands respect the configured publicApiProjection and viewPermission rules.
Administrators should conduct an audit of their publicApiProjection configurations after upgrading. Review all piece and page type definitions to guarantee that sensitive internal fields are explicitly excluded from public API responses. Accurate projection definitions are necessary to ensure the patch provides complete protection.
Deployments unable to apply the software update immediately can mitigate the vulnerability using Web Application Firewalls (WAF). Organizations should deploy traffic filtering rules to drop or sanitize incoming HTTP requests targeting API endpoints that contain choices= or counts= query parameters.
Implementing the WAF mitigation strategy requires operational testing. Blocking these parameters globally will disable frontend components that legitimately rely on dynamic choice enumeration. This workaround is recommended only as a temporary measure until the core application can be updated.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
ApostropheCMS Apostrophe Technologies | <= 4.28.0 | 4.29.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-863 |
| Attack Vector | Network |
| CVSS v3.1 | 5.3 |
| EPSS Score | 0.00037 |
| Impact | Confidentiality (Low) |
| Exploitation Status | PoC Available |
| CISA KEV | No |
Incorrect Authorization