CVE-2026-63087 is an unauthenticated compromise of self-hosted Grafana OnCall. The install endpoint InstallV2View, mounted at POST /api/internal/v1/plugin/v2/install/, sets both authentication_classes and permission_classes to empty. Its own parent view enforces plugin authentication. The install subclass removes it.
The endpoint turns off its own parent's auth
SyncV2View is authenticated. InstallV2View extends it and overrides the auth away to nothing.
So a match against two known constants is the only gate, and provision_plugin() then returns a fresh plaintext token to a caller who proved nothing.
The second flaw: a header that assigns your own role
The minted token authenticates through PluginAuthentication. When it resolves a user that does not exist yet, it falls back to a request header and trusts the role written there.
get_or_create_user maps that string to a database role with no check. Supply "role": "Admin" and you are Admin.
The exploit
StepWhat happens1Anonymous POST /api/internal/v1/plugin/v2/install/ with a body carrying the public defaults stack_id: 5, org_id: 100.2InstallV2View has no authentication_classes, so the request is never authenticated.3do_sync confirms only that the body matches the two default IDs. The supplied grafana_token is never verified.4provision_plugin() mints a plaintext onCallToken and returns it.5The caller presents that token plus X-Oncall-User-Context: {"role":"Admin", ...} to any PluginAuthentication endpoint.6On User.DoesNotExist, the header's role is trusted and an Admin user is created.7The caller is Admin: create integrations, revoke the legitimate plugin token, repoint OnCall's Grafana calls at an attacker-controlled host.
No token, no credential, and no prior access is required at any step.
The admin-check fix is a trap
The tempting fix is to require that the caller be a Grafana Org Admin before provisioning. We built that and ran it against the real view. It does not work, and the reason is structural: there is no authenticated principal on this endpoint to check. The only grafana_token in the request is the attacker's own body value, pointing at a grafana_url the attacker also controls. An admin check asks that attacker-controlled Grafana whether the attacker is an admin, and it happily says yes.
An authority check only means something once there is an authenticated identity. Here there is none, so the check is decoration.
The fix that actually closes it
Two changes, both required. Neither alone is enough.
1. Put real authentication on the install endpoint. Not the plugin token this endpoint mints, which would lock out first install, but an unforgeable, operator-provisioned install secret the attacker cannot supply.
Emphere Diff
2. Stop trusting the client-supplied role in the user-context bootstrap. Derive the role server-side instead of reading it from the attacker-controlled header.
Emphere Diff
Applying this is a contract change. The install endpoint now requires a header no client sends today, so every legitimate install path, the Helm chart, provisioning scripts, and OnCall's own install test, has to be updated to send the install secret in lockstep.
How we verified it
Assurance stood up the real OnCall engine for this: real Django 4.2.27 and DRF 3.15.2, a real database with all migrations applied, and every request driven through the project's own URLconf, middleware, and DRF dispatch. We confirmed each result by inspecting the database directly after every request. Fidelity is full; the only thing not real was the external Grafana the endpoint calls, and the vendor's own client handles an unreachable Grafana exactly as the harness saw it.
The fix nobody upstream is going to ship
OnCall is archived at 1.16.11 and moved to cold storage, so no vendor patch is coming. The change above is the one we verified, on the real view under real DRF, actually stops the exploit: the empty-auth endpoint stops minting tokens to anonymous callers, and the trusted-role header stops handing out Admin. It is here for operators holding the line while they migrate off, who need to build from source in the meantime.
An install endpoint that switches off its own parent's authentication is not a smaller version of a secured endpoint. It is an unauthenticated one, and no check you add after the token is minted can put the authentication back.
