zypher.co / reports / getconnects-admin-endpoint-exposure signed in · client link
Penetration test report

GetConnects

F-05: Admin endpoint accessible to non-admin users
April 2026 Delivered 2026-04-23
Risk index
91
/ 100
Severity distribution
Critical
1
High
0
Medium
0
Low
0
Info
0
Executive summary

The /api/shift/adminGetAll endpoint returns administrative shift data when called with a non-admin user token. No role-based check is performed at the handler, and no tenant-membership check either, so the response spans every tenant on the platform. Combined with F-03 this gives a low-privilege attacker a full read of who is running what and when across every tenant, including admin flags on assignees.

Finding 5 of 5 from the GetConnects multi-tenant API assessment. Delivered , published .

Engagement
Client
GetConnects
Endpoint
POST /api/shift/adminGetAll
Class
BFLA (OWASP API5)
Auth required
Any valid non-admin session
CVSS vector
AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N
Status
Reported, remediation and retest cycle in progress
Redaction notice. Tenant names, assignee identities, and campaign names have been genericized in the response. Endpoint path, request shape, response shape, and severity are unmodified.

Findings

1 total
01 Critical
F-05: Admin endpoint accessible to non-admin users
BFLA (OWASP API5) · POST /api/shift/adminGetAll
9.1 reported +
Description

Two failures on one handler. First, there is no role check: an admin-prefixed endpoint is reachable with a non-admin session. Second, there is no tenant scoping: the results are unfiltered across every tenant on the platform. Both look like the same class of "check must happen somewhere else" reasoning that produced F-01 and F-03.

Impact

Access to administrative-level operational data across organisations, including campaign scheduling, assignee identities, and admin-flag exposure. The isAdmin field in the response is the specific detail that turns this from an information disclosure into a targeting primitive: an attacker who has already gained low-privilege access on any tenant can now enumerate which named accounts on other tenants carry admin privileges, and use that list as a target set for phishing or credential stuffing.

Combined with F-03, the same low-privilege session pulls a full read of who is running what campaign, when, and under whose admin oversight, for every tenant on the system. That is a materially better position than a full-tenant BOLA on its own, because it maps identities to authority.

Remediation
  1. Add a role guard as part of the request middleware chain, keyed to the specific action rather than a generic isAdmin flag on the session. Endpoints whose URLs advertise administrative intent should fail closed by default and require an explicit opt-in for non-admin access.
  2. Centralise tenant scoping in the same middleware. This handler needs both the role check and the tenant check; running them at different layers is what let both fail here.
  3. Do not expose the isAdmin field on non-admin responses. Even if the endpoint were legitimately scoped to a single tenant, echoing role flags on user objects widens the blast radius of any future BOLA on the same endpoint.
  4. Add a CI check that runs every admin*-prefixed endpoint under a non-admin session and fails the build if any returns a 2xx.
Evidence

Proof-of-concept request:

POST /api/shift/adminGetAll HTTP/2
Host: [REDACTED]
Content-Type: application/json
Cookie: token=<non_admin_user>

{
  "json": {
    "startFrom": "2026-04-19T14:00:00.000Z",
    "endTo": "2026-04-26T13:59:59.999Z"
  }
}

Response, truncated and redacted:

{
  "json": [
    {
      "organisation": { "name": "[Tenant name redacted]" },
      "campaign": { "name": "test" },
      "assignee": { "name": "[Redacted]", "isAdmin": true },
      "status": "scheduled"
    }
  ]
}

Root cause

  • No role check on the handler. The admin prefix in the URL is treated as a naming convention rather than an enforcement point.
  • No tenant scoping either, so the leak is universal rather than confined to the caller's tenant.
  • The check was assumed to happen at some other layer (middleware, route guard, or gateway) that does not exist for this endpoint.

Timeline

DateEvent
Engagement window; finding delivered on .
Following weeksRemediation and retest cycle.
Public release of redacted writeup.
Remediation in progress Delivered 2026-04-23 Published 2026-07-12
More reports →