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 |