← All articles

CVE-2026-19072: From Velociraptor Investigator to SuperUser

How caller-controlled compiled hunt arguments and EffectivePrincipal produced server-side VQL execution as VelociraptorServer.

CVE
CVE-2026-19072
Product
Rapid7 Velociraptor
Affected versions
Linux releases before 0.77.2
Fixed versions
0.77.2
Status
Patched
9.9/ 10

CVSS 3.1 Base score

Critical

Rapid7 (CNA) score
Scoring details

Published vector

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

CVSS is owned by FIRST and used by permission.

The short version

Velociraptor 0.77.1 accepted caller-controlled compiled VQL inside a hunt and preserved an attacker-selected EffectivePrincipal. A stock investigator could relaunch that hunt against the special server client, causing the VQL to execute as the synthetic VelociraptorServer SuperUser.

The lab demonstrated server-host command execution and a persistent self-grant to the administrator role. The issue was fixed before 0.77.2 and is tracked as CVE-2026-19072.

At a glance

Field Detail
Tested version Velociraptor 0.77.1, commit 3137c7f71
Fixed version 0.77.2
Vulnerability class Improper privilege management / internal special element injection, CWE-269 and CWE-164
Vendor affected range Velociraptor on Linux before 0.77.2
Required access Stock investigator
Demonstrated impact Server command execution and persistent administrator grant
Researcher estimate Critical; proposed CVSS 3.1 score 9.9
Credits Yuval Miller and Leon Kayaliev

The boundary that failed

Investigators may create hunts, but they are not supposed to submit already compiled queries that bypass artifact compilation and its permission checks. Nor should they be able to nominate the internal SuperUser as the effective identity for server-side execution.

This bug combined both conditions. The API accepted internal-looking fields, the hunt compiler appended its output instead of replacing the caller’s data, and the server runner honored the surviving identity.

Why these fields were interesting

Fields named compiled_collector_args and effective_principal look like internal execution state, not user intent. I treated their presence in an externally reachable request as a question: does the server ignore and recompute them, validate them, or trust them? The answer was visible in the stored hunt and, more importantly, in the server flow log.

I kept the hunt paused while building the test. That separated object creation from execution and made it possible to inspect the stored request before triggering a single controlled server flow. Direct execve and user_grant denials from the same account were recorded first so the final result could not be mistaken for an overprivileged test user.

Lab reproduction

The actor was a stock investigator without COLLECT_SERVER, EXECVE, SERVER_ADMIN, ORG_ADMIN, ARTIFACT_WRITER, or IMPERSONATION.

Using the normal authenticated GUI session, the actor created a paused hunt whose request contained a precompiled query and selected the synthetic server principal:

{
  "state": "PAUSED",
  "hunt_description": "effective principal test",
  "start_request": {
    "artifacts": ["Generic.Client.Info"],
    "compiled_collector_args": [{
      "effective_principal": "VelociraptorServer",
      "Query": [{
        "Name": "Injected",
        "VQL": "SELECT whoami() AS EffectiveUser, user_grant(user='ATTACKER', roles=['administrator']) AS Granted FROM scope()"
      }]
    }]
  }
}

The investigator then launched the stored hunt against the special server client:

SELECT hunt_add(
  client_id='server',
  hunt_id='HUNT_ID',
  relaunch=TRUE) AS FlowId
FROM scope()

The server flow log showed execution on behalf of the investigator with effective permissions for VelociraptorServer. The injected query ran and the investigator’s stored role became administrator.

A second isolated variant replaced the role grant with a benign execve marker. The marker contained the real server-host identity. Direct user_grant and execve controls from the same investigator notebook were denied.

Root cause

CreateHunt accepted caller-controlled StartRequest.CompiledCollectorArgs. The hunt dispatcher appended server-compiled arguments to that list rather than replacing it, allowing the injected query to bypass the normal compiler and required-permission checks.

The relaunch path permitted the stored request to target client server. Its ordinary principal was the hunt creator, satisfying the nonempty-principal guard, while the attacker-controlled effective principal survived. The ACL manager mapped the public name VelociraptorServer directly to SuperUser.

In short, several individually dangerous internal fields crossed the external API boundary without being discarded or recomputed.

Source-to-sink analysis

The first failure occurs in services/hunt_dispatcher/hunt_dispatcher.go. CreateHunt() accepted StartRequest.CompiledCollectorArgs from the external request and later appended the server’s compilation result to that same slice. Appending was security-relevant: the trusted compiler did not erase the precompiled query already supplied by the caller.

The stored collector arguments were eventually consumed by services/server_artifacts/collection_context.go. That path treats EffectivePrincipal as an instruction to construct a new server ACL manager. When the value is the configured VelociraptorServer identity, services/acl_manager/acl_manager.go resolves it to the internal SuperUser. The authorization decision therefore uses an identity embedded in the attacker-supplied compiled object, not the investigator who created the hunt.

The final condition was target selection. The hunt API permitted the special server pseudo-client to be added to the hunt. The demonstrated exploit therefore crossed three boundaries:

external request controls compiled collector arguments
        -> trusted compilation appends instead of replaces
        -> server runner honors attacker-selected EffectivePrincipal
        -> hunt is relaunched against the server pseudo-client

Removing any one of those edges breaks the chain. Rejecting EffectivePrincipal alone is useful hardening, but internal compiled structures should not be accepted from an external request in the first place.

Detection notes

Review hunt creation and modification events for caller-supplied compiled_collector_args, unexpected effective_principal values, or compiled VQL that does not correspond to the named artifacts. A hunt relaunched against the special server client deserves particular scrutiny.

Post-exploitation evidence may include role grants, server flows whose effective permissions are VelociraptorServer, or server-side process creation near the hunt execution time. Logging the ordinary principal and the effective principal together is essential; recording only the latter erases the actor who created the path.

Remediation

Upgrade to Velociraptor 0.77.2 or later. The fix in commit b64d9154 clears caller-supplied compiled arguments, assigns compiler output instead of appending it, and prevents hunts from targeting the server pseudo-client.

Additional hardening should:

  • Reject caller-supplied compiled collector arguments.
  • Replace, rather than append to, internal compilation output.
  • Strip or explicitly authorize EffectivePrincipal.
  • Keep internal SuperUser identities unnameable from request data.

Disclosure timeline

Date Event
2026-08-03 Vulnerability reported to Rapid7
2026-09-24 Rapid7 closed tracking ticket 139936 after remediation

References