CVE-2026-87803 Overview
CVE-2026-87803 is an authorization bypass vulnerability in the Countly Server DBViewer component. The flaw resides in the aggregation stage sanitizer used by the /o/db endpoint. The sanitizer incorrectly classifies nested arrays containing unknown stage operators as generic arrays, causing it to skip stage-level filtering for sibling stages. Attackers with non-admin DBViewer read permission can abuse this to inject forbidden operators such as $lookup inside $facet sub-pipelines. The result is unauthorized cross-collection reads into restricted collections, exposing sensitive data such as password-reset tokens (prid) and enabling account takeover. The weakness is categorized as CWE-863: Incorrect Authorization.
Critical Impact
Authenticated low-privileged users can exfiltrate password-reset tokens from restricted collections and take over accounts, including administrative ones.
Affected Products
- Countly Server (DBViewer plugin)
- Deployments exposing the /o/db aggregation endpoint to non-admin users
- Instances relying on the aggregation stage sanitizer with the hardcoded KNOWN_STAGE_OPERATORS allow-list
Discovery Timeline
- 2026-09-10 - CVE-2026-87803 published to NVD
- 2026-09-10 - Last updated in NVD database
Technical Details for CVE-2026-87803
Vulnerability Analysis
Countly Server exposes the /o/db endpoint to allow users with DBViewer permissions to run MongoDB aggregation pipelines. To prevent misuse, user-controlled aggregation JSON passes through a stage sanitizer that strips forbidden operators from each pipeline stage. The sanitizer distinguishes real sub-pipelines from generic nested arrays by checking whether every element contains a key present in the hardcoded KNOWN_STAGE_OPERATORS set.
This all-or-nothing check is the root of the bypass. When any single element uses a stage key outside the allow-list, the sanitizer treats the entire array as opaque data and skips per-stage sanitization for its siblings. Attackers exploit this by mixing a benign but unrecognized stage operator with a forbidden operator inside the same array.
The most reliable trigger is the undocumented MongoDB-internal $_internalInhibitOptimization operator. Because it is not in the allow-list, its presence causes the sanitizer to abandon inspection of the surrounding stages, allowing operators such as $lookup to survive inside a $facet sub-pipeline. $lookup then performs a join against a collection the caller has no permission to read.
Root Cause
The root cause is a logic flaw in sub-pipeline detection. The sanitizer conflates classification with enforcement: an unrecognized element short-circuits both. Combined with an incomplete allow-list that omits internal MongoDB operators, this converts a defense-in-depth filter into a bypass primitive.
Attack Vector
Exploitation requires network access to the Countly Server and a valid account with DBViewer read permission. The attacker sends a crafted aggregation payload to /o/db containing a $facet stage whose sub-pipeline includes both $_internalInhibitOptimization and a $lookup targeting a restricted collection such as the users or password-reset collection. The response returns the joined documents, including prid values that the attacker replays to reset arbitrary account passwords.
No synthetic proof-of-concept is reproduced here. Technical details are available in Countly Pull Request #7868.
Detection Methods for CVE-2026-87803
Indicators of Compromise
- Requests to /o/db containing $facet combined with $lookup in the aggregation JSON.
- Aggregation payloads referencing $_internalInhibitOptimization or other undocumented MongoDB-internal operators.
- Non-admin user sessions returning documents from collections outside the DBViewer scope, such as user or password-reset collections.
- Unexpected password-reset events immediately following DBViewer API activity from the same account.
Detection Strategies
- Parse /o/db request bodies at the reverse proxy or WAF and alert on $lookup, $graphLookup, $merge, $out, and $_internalInhibitOptimization tokens.
- Correlate DBViewer API calls with subsequent /i/users/reset_password or equivalent password-reset endpoint activity across short time windows.
- Baseline aggregation query shapes per user role and flag deviations, particularly nested $facet pipelines from non-admin roles.
Monitoring Recommendations
- Enable MongoDB profiling for slow or unusual aggregation stages and forward logs to a centralized analytics platform.
- Monitor Countly application logs for DBViewer permission checks that succeed while accessing sensitive collections.
- Track outbound response sizes on /o/db and alert on responses containing token-like fields such as prid, token, or reset.
How to Mitigate CVE-2026-87803
Immediate Actions Required
- Upgrade Countly Server to the release containing the fix from Pull Request #7868.
- Revoke DBViewer read permission from any account that does not strictly require it, especially non-administrative roles.
- Rotate all outstanding password-reset tokens and force password resets for privileged accounts if exploitation is suspected.
- Audit recent /o/db traffic for the indicators listed above and treat any matches as suspected data exfiltration.
Patch Information
The vendor fix is delivered in Countly PR #7868. The patch reworks the aggregation stage sanitizer so unknown stage operators no longer disable sanitization for sibling stages, and it enforces stage-level stripping recursively inside $facet sub-pipelines. Apply the corresponding tagged release rather than cherry-picking the diff to ensure related sanitizer changes are included.
Workarounds
- Block requests to /o/db at a reverse proxy when the JSON body contains $lookup, $graphLookup, $merge, $out, or $_internalInhibitOptimization.
- Restrict the DBViewer plugin to administrator accounts only until the patched release is deployed.
- Place the Countly management interface behind an authenticated VPN or IP allow-list to reduce the attack surface for authenticated abuse.
- Configure MongoDB role-based access control so the Countly service account cannot read password-reset or credential collections beyond the minimum required.
# Example reverse proxy rule (NGINX) to block risky aggregation operators on /o/db
location /o/db {
if ($request_body ~* "\$lookup|\$graphLookup|\$merge|\$out|\$_internalInhibitOptimization") {
return 403;
}
proxy_pass http://countly_upstream;
}
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

