Velociraptor DFIR and hunting platform. VQL queries, built-in artifacts, hunts, collections, server/client deployment, and custom artifact creation.
Velociraptor is an open-source DFIR and endpoint visibility tool developed by Rapid7. It uses the Velociraptor Query Language (VQL) to query endpoint state in real time - processes, files, registry, network, event logs, and more.
┌──────────────────────────────────────┐
│ Velociraptor Server │
│ - Web UI (port 8889) │
│ - gRPC frontend (port 8000) │
│ - Datastore (file-based / EFS) │
│ - Hunt dispatcher │
│ - Artifact repository │
└──────────────┬───────────────────────┘
│ gRPC + TLS (mutual auth)
┌──────────┼──────────┐
▼ ▼ ▼
┌──────┐ ┌──────┐ ┌──────┐
│Client│ │Client│ │Client│
│(agent)│ │(agent)│ │(agent)│
└──────┘ └──────┘ └──────┘
The single binary acts as both server and client. Generate a server config, then deploy.
# Download latest release
wget https://github.com/Velocidex/velociraptor/releases/latest/download/velociraptor-v0.73.3-linux-amd64
chmod +x velociraptor-v0.73.3-linux-amd64
mv velociraptor-v0.73.3-linux-amd64 /usr/local/bin/velociraptor
# Interactive config generation (creates server.config.yaml + client.config.yaml)
velociraptor config generate -i
# Start server as a service
velociraptor --config server.config.yaml frontend -v
# Or install as a systemd service
velociraptor --config server.config.yaml service install --user velociraptor
systemctl enable --now velociraptor_server
The client config is generated during server setup. It contains the server URL and TLS certs. Package and deploy it to endpoints.
# Repack the binary with embedded client config
velociraptor --config server.config.yaml config repack \
--exe velociraptor-v0.73.3-linux-amd64 \
client.config.yaml velociraptor-client-repacked
# Deploy on Windows (as a service)
velociraptor-client-repacked.exe service install
# Deploy on Linux
sudo cp velociraptor-client-repacked /usr/local/bin/velociraptor-client
velociraptor-client --config client.config.yaml service install
systemctl enable --now velociraptor_client
# Generate MSI installer for mass deployment
velociraptor --config server.config.yaml config repack \
--msi velociraptor-v0.73.3-amd64.msi \
client.config.yaml velociraptor-client.msi
# Verify client connectivity (server-side)
velociraptor --config server.config.yaml query \
"SELECT * FROM clients() LIMIT 5"
# Add a GUI admin user
velociraptor --config server.config.yaml \
user add admin --role administrator
# Roles: administrator, reader, analyst, investigator, api
velociraptor --config server.config.yaml \
user add analyst1 --role analyst
VQL (Velociraptor Query Language) looks like SQL but queries live endpoint state. Each plugin acts as a virtual "table" that pulls data from the system in real time - processes, files, network, registry, etc.
-- Basic structure
SELECT Column1, Column2
FROM plugin(arg1=value1, arg2=value2)
WHERE condition
ORDER BY Column1
LIMIT 100
-- Every VQL query has a FROM clause with a plugin
-- Plugins generate rows, like SQL tables but dynamic
-- Example: list processes
SELECT Pid, Name, Exe, CommandLine
FROM pslist()
-- Filter with WHERE
SELECT Pid, Name, Exe
FROM pslist()
WHERE Name =~ "powershell|cmd"
-- Subqueries (foreach)
SELECT * FROM foreach(
row={SELECT Pid, Name FROM pslist()},
query={SELECT Pid, Name, FamilyName
FROM netstat() WHERE Pid = Pid}
)
| Operator | Purpose | Example |
|---|---|---|
=~ |
Regex match | Name =~ "svc.*host" |
= |
Exact match | Pid = 4 |
<> |
Not equal | Status <> "Running" |
IN |
Membership | Name IN ("cmd.exe", "powershell.exe") |
AND / OR |
Logic | Pid > 100 AND Name =~ "svc" |
NOT |
Negation | NOT Name =~ "system" |
format() |
String formatting | format(format="%s:%d", args=[IP, Port]) |
timestamp() |
Parse timestamps | timestamp(epoch=CreateTime) |
basename() |
Filename from path | basename(path=Exe) |
dirname() |
Directory from path | dirname(path=Exe) |
hash() |
File hash | hash(path=Exe, hashselect="SHA256") |
upload() |
Upload file to server | upload(file=Exe) |
if() |
Conditional | if(condition=Pid=0, then="Kernel", else=Name) |
count() |
Aggregate count | SELECT Name, count() FROM pslist() GROUP BY Name |
len() |
Length | len(list=CommandLine) |
Plugins are the data sources in VQL. Each one queries a specific aspect of the endpoint.
-- Process listing
SELECT * FROM pslist()
-- Network connections
SELECT * FROM netstat()
-- File search (glob patterns)
SELECT * FROM glob(globs="C:/Users/*/Downloads/*.exe")
-- Registry key reading
SELECT * FROM read_reg_key(globs="HKLM/SOFTWARE/Microsoft/Windows/CurrentVersion/Run/*")
-- WMI queries
SELECT * FROM wmi(query="SELECT * FROM Win32_Service", namespace="ROOT/CIMV2")
-- Execute command and capture output
SELECT * FROM execve(argv=["cmd.exe", "/c", "ipconfig", "/all"])
-- Parse PE headers
SELECT * FROM parse_pe(file="C:/Windows/System32/cmd.exe")
-- Read file content
SELECT * FROM read_file(filenames="C:/Windows/System32/drivers/etc/hosts")
-- Parse event logs
SELECT * FROM parse_evtx(filename="C:/Windows/System32/winevt/Logs/Security.evtx")
-- Yara scanning
SELECT * FROM yara(rules=MyYaraRule, files="C:/Users/*/Downloads/*.exe")
VQL supports variable declarations with LET - useful for reusing subqueries, defining constants, and building complex pipelines.
-- Define a constant
LET suspicious_names = ("mimikatz", "rubeus", "seatbelt", "sharphound")
-- Store a subquery for reuse
LET running_procs = SELECT Pid, Name, Exe, CommandLine
FROM pslist()
WHERE Exe
-- Use it in another query
SELECT * FROM running_procs
WHERE basename(path=Exe) IN suspicious_names
-- Parameterized lambda (lazy evaluation)
LET hash_file(path) = SELECT
hash(path=path, hashselect="SHA256") AS SHA256
FROM scope()
-- Chain queries
LET executables = SELECT FullPath
FROM glob(globs="C:/Temp/**/*.exe")
SELECT FullPath,
hash(path=FullPath, hashselect="SHA256") AS SHA256
FROM executables
The pslist() plugin retrieves all running processes with metadata. This is the starting point for most investigations - get a process listing, then drill down into suspicious entries.
-- Full process listing
SELECT Pid, Ppid, Name, Exe,
CommandLine, Username,
CreateTime, TokenIsElevated
FROM pslist()
-- With hash of the binary
SELECT Pid, Name, Exe, CommandLine,
hash(path=Exe, hashselect="SHA256") AS SHA256
FROM pslist()
WHERE Exe
-- Process with loaded DLLs (Windows)
SELECT * FROM modules(pid=1234)
-- Process memory map
SELECT * FROM vad(pid=1234)
Search for specific processes matching patterns. Useful when you know the tool name, a suspicious path, or a command-line argument associated with malware or attacker tools.
-- Find by name (regex)
SELECT Pid, Name, Exe, CommandLine
FROM pslist()
WHERE Name =~ "(?i)powershell|pwsh|cmd"
-- Find by suspicious paths (not in expected locations)
SELECT Pid, Name, Exe, CommandLine
FROM pslist()
WHERE Exe AND NOT Exe =~ "(?i)^C:\\\\(Windows|Program Files)"
-- Find by command-line keywords
SELECT Pid, Name, CommandLine
FROM pslist()
WHERE CommandLine =~ "(?i)(encoded|bypass|hidden|invoke-|iex|downloadstring)"
-- Unsigned binaries running
SELECT Pid, Name, Exe,
authenticode(filename=Exe).Trusted AS Signed
FROM pslist()
WHERE Exe
AND NOT authenticode(filename=Exe).Trusted = "trusted"
Reconstruct parent-child relationships to understand how a process was spawned. Critical for identifying LOLBin abuse, lateral movement artifacts, and initial access vectors.
-- Build process tree
SELECT Pid, Ppid, Name, Exe, CommandLine,
{SELECT Name FROM pslist() WHERE Pid = Ppid} AS ParentName
FROM pslist()
-- Children of a specific process
SELECT Pid, Name, Exe, CommandLine
FROM pslist()
WHERE Ppid = 1234
-- Full ancestry chain (recursive)
LET get_parent(pid) = SELECT Pid, Ppid, Name, Exe
FROM pslist() WHERE Pid = pid
SELECT * FROM foreach(
row={SELECT Pid, Ppid, Name FROM pslist() WHERE Pid = 5678},
query={SELECT * FROM chain(
a={SELECT * FROM get_parent(pid=Ppid)},
b={SELECT * FROM get_parent(pid=Ppid)},
c={SELECT * FROM get_parent(pid=Ppid)}
)}
)
Flag process relationships that deviate from normal Windows behavior. For example, svchost.exe should only be spawned by services.exe, and cmd.exe from explorer.exe via a user-initiated action.
-- Suspicious parent-child pairs
LET suspicious_combos = SELECT * FROM parse_csv(accessor="data", filename='''
ParentPattern,ChildPattern
winword|excel|powerpoint,cmd\.exe|powershell|wscript|cscript|mshta
svchost,cmd\.exe|powershell
wmiprvse,cmd\.exe|powershell
services\.exe,cmd\.exe|powershell
''')
LET procs = SELECT Pid, Ppid, Name, Exe, CommandLine,
{SELECT Name FROM pslist() WHERE Pid = Ppid} AS ParentName
FROM pslist()
SELECT Pid, Name, ParentName, CommandLine
FROM procs
WHERE ParentName AND
filter(list=suspicious_combos,
condition= ParentName[0] =~ ParentPattern
AND Name =~ ChildPattern)
-- svchost.exe not spawned by services.exe
SELECT Pid, Ppid, Name, Exe, CommandLine,
{SELECT Name FROM pslist() WHERE Pid = Ppid} AS ParentName
FROM pslist()
WHERE Name =~ "svchost"
AND NOT {SELECT Name FROM pslist() WHERE Pid = Ppid}[0] = "services.exe"
Examine process memory for injected code, suspicious memory regions, or strings. Use VAD (Virtual Address Descriptor) analysis to find executable memory regions that aren't backed by files on disk - a common indicator of process injection.
-- Find RWX (read-write-execute) memory regions - injection indicator
SELECT Pid, Name, Address, Size, Protection, FileName
FROM vad(pid=1234)
WHERE Protection =~ "xrw"
-- Scan process memory with Yara
LET yara_rule = '''
rule Mimikatz {
strings:
$s1 = "mimikatz" ascii wide
$s2 = "sekurlsa" ascii wide
condition:
any of them
}
'''
SELECT * FROM proc_yara(rules=yara_rule, pid=1234)
-- Dump suspicious process memory
SELECT upload(file=format(format="/proc/%d/mem", args=[Pid]),
accessor="sparse",
name=format(format="%s_%d.dmp", args=[Name, Pid]))
FROM pslist()
WHERE Pid = 1234
The netstat() plugin shows active network connections enriched with process info. Use this to identify beaconing behavior, lateral movement, and data exfiltration channels.
-- All connections with process info
SELECT Pid, Name, FamilyName AS Proto,
Laddr.IP AS LocalIP, Laddr.Port AS LocalPort,
Raddr.IP AS RemoteIP, Raddr.Port AS RemotePort,
Status, Timestamp
FROM netstat()
-- Only established outbound connections
SELECT Pid, Name,
Raddr.IP AS RemoteIP, Raddr.Port AS RemotePort
FROM netstat()
WHERE Status = "ESTABLISHED"
AND NOT Raddr.IP =~ "^(127\.|10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.)"
-- Top talkers (most connections per process)
SELECT Name, Pid, count() AS ConnCount
FROM netstat()
WHERE Status = "ESTABLISHED"
GROUP BY Name, Pid
ORDER BY ConnCount DESC
LIMIT 20
Query the local DNS resolver cache to identify recently resolved domains. Useful for detecting C2 domains, DGA patterns, and data exfiltration over DNS.
-- Windows DNS cache
SELECT * FROM dns_cache()
-- Look for suspicious TLDs or long domain names (DGA detection)
SELECT Name, Record, TTL
FROM dns_cache()
WHERE Name =~ "\.(xyz|top|buzz|tk|ml|ga|cf)$"
OR len(list=split(string=Name, sep=".")[0]) > 20
-- Search for specific domains
SELECT Name, Record
FROM dns_cache()
WHERE Name =~ "(?i)(pastebin|ngrok|duckdns|no-ip|dyndns)"
Identify all services listening for incoming connections. Compare against known baselines to find backdoors, reverse shells, or unauthorized services.
-- All listening ports
SELECT Pid, Name,
Laddr.IP AS ListenIP,
Laddr.Port AS ListenPort,
FamilyName AS Proto
FROM netstat()
WHERE Status = "LISTEN"
ORDER BY ListenPort
-- Listening on non-standard ports (not well-known services)
SELECT Pid, Name, Laddr.Port AS Port, Exe
FROM netstat()
WHERE Status = "LISTEN"
AND NOT Laddr.Port IN (22, 53, 80, 135, 139, 443, 445, 3389, 5985, 5986)
ORDER BY Port
-- High ports with no recognizable service
SELECT Pid, Name, Exe, CommandLine,
Laddr.Port AS Port
FROM netstat()
WHERE Status = "LISTEN"
AND Laddr.Port > 49000
Cross-reference active connections against a threat intel list. Replace the IP list with your own IOC feed or a local file of indicators.
-- Check against a list of IOC IPs
LET bad_ips = ("203.0.113.50", "198.51.100.23", "192.0.2.100")
SELECT Pid, Name, Exe,
Raddr.IP AS RemoteIP,
Raddr.Port AS RemotePort
FROM netstat()
WHERE Raddr.IP IN bad_ips
-- Check against IOC file (one IP per line)
LET ioc_ips = SELECT Content
FROM read_file(filenames="C:/iocs/bad_ips.txt")
SELECT Pid, Name, Raddr.IP AS RemoteIP
FROM netstat()
WHERE Raddr.IP IN ioc_ips.Content
-- GeoIP-based anomaly detection (if MaxMind DB available)
SELECT Pid, Name, Raddr.IP AS RemoteIP, Raddr.Port AS Port,
geoip(ip=Raddr.IP) AS Geo
FROM netstat()
WHERE Status = "ESTABLISHED"
AND geoip(ip=Raddr.IP).Country.ISOCode
IN ("RU", "CN", "KP", "IR")
Build a timeline of network activity by correlating event logs and connection data. Helpful for establishing when C2 communication started or when lateral movement occurred.
-- Firewall log events (Windows)
SELECT timestamp(epoch=TimeCreated) AS Time,
EventData.SourceAddress AS SrcIP,
EventData.SourcePort AS SrcPort,
EventData.DestAddress AS DstIP,
EventData.DestPort AS DstPort,
EventData.Application AS App,
EventData.Direction AS Direction
FROM parse_evtx(
filename="C:/Windows/System32/winevt/Logs/Microsoft-Windows-Windows Firewall With Advanced Security%4Firewall.evtx")
ORDER BY Time DESC
LIMIT 500
-- Sysmon network connections (Event ID 3)
SELECT timestamp(epoch=TimeCreated) AS Time,
EventData.Image AS Process,
EventData.DestinationIp AS DstIP,
EventData.DestinationPort AS DstPort,
EventData.User AS User
FROM parse_evtx(
filename="C:/Windows/System32/winevt/Logs/Microsoft-Windows-Sysmon%4Operational.evtx")
WHERE System.EventID.Value = 3
ORDER BY Time DESC
Use glob() to recursively search the file system. This is one of the most frequently used operations - finding malware droppers, tools, or modified system files.
-- Search by filename pattern
SELECT FullPath, Size, Mtime, Atime
FROM glob(globs="C:/Users/**/mimikatz*")
-- Search by extension in temp directories
SELECT FullPath, Size, Mtime
FROM glob(globs="C:/{Temp,Users/*/AppData/Local/Temp}/**/*.{exe,dll,ps1,bat,vbs}")
-- Find files matching a known-bad SHA256
LET target_hash = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
SELECT FullPath, Size, Mtime,
hash(path=FullPath, hashselect="SHA256") AS SHA256
FROM glob(globs="C:/Users/**/*.exe")
WHERE hash(path=FullPath, hashselect="SHA256").SHA256 = target_hash
-- Files modified in the last 24 hours
SELECT FullPath, Size, Mtime, Atime, Ctime
FROM glob(globs="C:/Windows/System32/**")
WHERE Mtime > now() - 86400
ORDER BY Mtime DESC
Identify recently created or modified files that may indicate attacker activity. Focus on sensitive directories and executable file types.
-- Recently modified executables across common attack paths
SELECT FullPath, Size,
timestamp(epoch=Mtime) AS Modified,
timestamp(epoch=Ctime) AS Created,
hash(path=FullPath, hashselect="SHA256") AS SHA256
FROM glob(globs=[
"C:/Windows/Temp/**/*.{exe,dll,ps1,bat,vbs}",
"C:/Users/*/AppData/**/*.{exe,dll,ps1,bat,vbs}",
"C:/ProgramData/**/*.{exe,dll,ps1,bat,vbs}"
])
WHERE Mtime > now() - 86400 * 7
ORDER BY Mtime DESC
-- New executables in System32 (should rarely change)
SELECT FullPath, Size,
timestamp(epoch=Ctime) AS Created
FROM glob(globs="C:/Windows/System32/*.{exe,dll}")
WHERE Ctime > now() - 86400 * 7
ORDER BY Ctime DESC
NTFS Alternate Data Streams can hide data within files. Attackers use ADS to stash payloads or tools that won't appear in normal directory listings.
-- Find files with alternate data streams
SELECT FullPath, Name, Size, Data
FROM glob(globs="C:/Users/**",
accessor="ntfs")
WHERE Data.type =~ "ADS"
-- Scan for ADS in common attacker directories
SELECT *
FROM Artifact.Windows.NTFS.ADS(
SearchPath="C:/Users/",
FileRegex=".")
-- Zone.Identifier streams (Mark of the Web)
SELECT FullPath, Size, Mtime,
read_file(filename=FullPath + ":Zone.Identifier") AS ZoneInfo
FROM glob(globs="C:/Users/*/Downloads/*")
WHERE IsDir = false
Windows Prefetch files record evidence of program execution - even after the binary is deleted. Each .pf file contains the executable name, run count, timestamps, and loaded files.
-- Parse all prefetch files
SELECT Name, Executable, FileSize,
RunCount, LastRunTimes,
FilesAccessed
FROM Artifact.Windows.Forensics.Prefetch()
ORDER BY LastRunTimes DESC
-- Search prefetch for specific tool execution
SELECT Executable, RunCount,
LastRunTimes, FilesAccessed
FROM Artifact.Windows.Forensics.Prefetch()
WHERE Executable =~ "(?i)(mimikatz|psexec|wmic|certutil|bitsadmin)"
-- Recently executed programs (last 7 days)
SELECT Executable, RunCount, LastRunTimes
FROM Artifact.Windows.Forensics.Prefetch()
WHERE LastRunTimes[-1] > now() - 86400 * 7
ORDER BY LastRunTimes DESC
The Master File Table (MFT) is the NTFS index of every file and directory. Parsing the MFT provides a complete file system timeline - including deleted files that haven't been overwritten.
-- Parse MFT for file timeline
SELECT FullPath, FileName, FileSize,
Created0x10 AS Created,
LastModified0x10 AS Modified,
InUse, IsDir
FROM parse_mft(accessor="ntfs", filename="\\\\.\\C:")
WHERE FullPath =~ "Users"
ORDER BY Created DESC
LIMIT 1000
-- Find timestomped files (SI vs FN timestamp mismatch)
SELECT FullPath, FileName,
Created0x10 AS SI_Created,
Created0x30 AS FN_Created,
LastModified0x10 AS SI_Modified,
LastModified0x30 AS FN_Modified
FROM parse_mft(accessor="ntfs", filename="\\\\.\\C:")
WHERE Created0x10 <> Created0x30
AND InUse = true
AND NOT IsDir
-- Deleted files recovery (MFT records not in use)
SELECT FullPath, FileName, FileSize,
Created0x10, LastModified0x10
FROM parse_mft(accessor="ntfs", filename="\\\\.\\C:")
WHERE InUse = false
AND FileName =~ "(?i)\.(exe|dll|ps1|bat|zip|rar)$"
ORDER BY LastModified0x10 DESC
LIMIT 500
Combine multiple file system data sources into a unified timeline. This helps correlate attacker actions across different evidence types during an investigation.
-- USN Journal timeline (tracks file system changes)
SELECT
timestamp(epoch=Timestamp) AS Time,
FullPath,
Reason,
FileAttributes
FROM parse_usn(device="\\\\.\\C:")
WHERE Timestamp > now() - 86400 * 3
ORDER BY Timestamp DESC
LIMIT 5000
-- Combined timeline: MFT + Prefetch + Event Logs
-- Use the built-in super timeline artifact
SELECT * FROM Artifact.Windows.Forensics.Timeline()
WHERE Time > "2026-03-28"
ORDER BY Time
Run and RunOnce registry keys execute programs at user logon. One of the oldest and most common persistence mechanisms, easy to set and frequently abused by malware.
-- All Run key entries (HKLM + HKCU)
SELECT Key.FullPath, Name, Data.value AS Value
FROM read_reg_key(
globs=[
"HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows/CurrentVersion/Run/*",
"HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows/CurrentVersion/RunOnce/*",
"HKEY_USERS/*/SOFTWARE/Microsoft/Windows/CurrentVersion/Run/*",
"HKEY_USERS/*/SOFTWARE/Microsoft/Windows/CurrentVersion/RunOnce/*"
])
-- Run keys with unsigned binaries
SELECT Key.FullPath, Name, Data.value AS Value,
authenticode(filename=expand(path=Data.value)).Trusted AS Signed
FROM read_reg_key(
globs="HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows/CurrentVersion/Run/*")
WHERE NOT authenticode(filename=expand(path=Data.value)).Trusted = "trusted"
Windows services run as SYSTEM by default and survive reboots. Attackers create or hijack services for persistent, elevated access.
-- All registered services
SELECT Name, DisplayName, PathName, StartMode,
State, StartName AS RunAs
FROM wmi(query="SELECT * FROM Win32_Service",
namespace="ROOT/CIMV2")
-- Services running from unusual paths
SELECT Name, DisplayName, PathName, State
FROM wmi(query="SELECT * FROM Win32_Service",
namespace="ROOT/CIMV2")
WHERE NOT PathName =~ "(?i)^(C:\\\\Windows|C:\\\\Program Files)"
AND PathName
-- Recently created services (registry)
SELECT Key.FullPath, Key.Mtime AS Modified,
{SELECT Data.value FROM read_reg_key(
globs=Key.FullPath + "/ImagePath")} AS ImagePath
FROM read_reg_key(
globs="HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/*")
WHERE Key.Mtime > now() - 86400 * 7
ORDER BY Key.Mtime DESC
Scheduled tasks are a high-value persistence mechanism. They can run as any user, trigger on events, times, or startup, and are often abused for lateral movement via schtasks /create /s.
-- All scheduled tasks
SELECT * FROM Artifact.Windows.System.TaskScheduler()
-- Tasks executing from suspicious locations
SELECT Name, Path, Command, Arguments, UserId,
LastRunTime, NextRunTime
FROM Artifact.Windows.System.TaskScheduler()
WHERE NOT Command =~ "(?i)^(C:\\\\Windows|C:\\\\Program Files)"
-- Tasks created recently
SELECT Name, Path, Command, Arguments,
RegistrationDate
FROM Artifact.Windows.System.TaskScheduler()
WHERE RegistrationDate > now() - 86400 * 7
-- Raw XML parsing of task files
SELECT FullPath,
parse_xml(file=FullPath) AS TaskXML
FROM glob(globs="C:/Windows/System32/Tasks/**")
WHERE NOT IsDir
Startup folders execute anything placed inside them at user logon. Simple but effective - dropping an LNK or script here is a low-effort persistence method.
-- Check all startup folder entries
SELECT FullPath, Name, Size,
timestamp(epoch=Mtime) AS Modified
FROM glob(globs=[
"C:/ProgramData/Microsoft/Windows/Start Menu/Programs/Startup/*",
"C:/Users/*/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/Startup/*"
])
WHERE NOT IsDir
-- Analyze LNK files in startup
SELECT FullPath,
parse_lnk(filename=FullPath) AS LnkTarget
FROM glob(globs="C:/Users/*/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/Startup/*.lnk")
COM hijacking replaces legitimate COM class references with attacker-controlled DLLs. Stealthy because it leverages normal Windows functionality. User-level CLSID entries override machine-level ones.
-- HKCU COM overrides (user-level hijacks)
SELECT Key.FullPath,
Name, Data.value AS DLLPath
FROM read_reg_key(
globs="HKEY_USERS/*/SOFTWARE/Classes/CLSID/*/InProcServer32/*")
WHERE Data.value
AND NOT Data.value =~ "(?i)^C:\\\\Windows"
-- Compare HKCU vs HKLM (hijack detection)
LET hklm_com = SELECT Key.FullPath,
regex_replace(source=Key.FullPath,
re="HKEY_LOCAL_MACHINE/SOFTWARE/Classes/",
replace="") AS CLSID,
Data.value AS MachineValue
FROM read_reg_key(
globs="HKEY_LOCAL_MACHINE/SOFTWARE/Classes/CLSID/*/InProcServer32/*")
LET hkcu_com = SELECT Key.FullPath,
regex_replace(source=Key.FullPath,
re="HKEY_USERS/.+?/SOFTWARE/Classes/",
replace="") AS CLSID,
Data.value AS UserValue
FROM read_reg_key(
globs="HKEY_USERS/*/SOFTWARE/Classes/CLSID/*/InProcServer32/*")
SELECT * FROM hkcu_com
WHERE CLSID IN hklm_com.CLSID
WMI event subscriptions are a fileless persistence technique. An event filter triggers on a condition (e.g., system startup), and a consumer executes a payload. Hard to detect without specific tooling.
-- WMI Event Consumers (the action)
SELECT * FROM wmi(
query="SELECT * FROM __EventConsumer",
namespace="ROOT/Subscription")
-- WMI Event Filters (the trigger)
SELECT * FROM wmi(
query="SELECT * FROM __EventFilter",
namespace="ROOT/Subscription")
-- WMI Filter-to-Consumer Bindings (links trigger to action)
SELECT * FROM wmi(
query="SELECT * FROM __FilterToConsumerBinding",
namespace="ROOT/Subscription")
-- Full WMI persistence chain in one query
SELECT
{SELECT Name, Query FROM wmi(
query="SELECT * FROM __EventFilter",
namespace="ROOT/Subscription")} AS Filters,
{SELECT Name, CommandLineTemplate, ScriptText
FROM wmi(
query="SELECT * FROM __EventConsumer",
namespace="ROOT/Subscription")} AS Consumers,
{SELECT * FROM wmi(
query="SELECT * FROM __FilterToConsumerBinding",
namespace="ROOT/Subscription")} AS Bindings
FROM scope()
System-level artifacts for process, service, and driver enumeration.
| Artifact | Description |
|---|---|
Windows.System.Pslist |
Running processes with hashes |
Windows.System.Services |
All Windows services |
Windows.System.TaskScheduler |
Scheduled tasks |
Windows.System.Drivers |
Loaded kernel drivers |
Windows.System.DLLs |
Loaded DLLs per process |
Windows.System.CmdShell |
Run arbitrary shell cmd |
Windows.System.PowerShell |
Run PowerShell script |
Windows.System.Users |
Local user accounts |
Windows.System.Interfaces |
Network interfaces |
Windows.System.Hotfixes |
Installed KB patches |
Network configuration and connection data.
| Artifact | Description |
|---|---|
Windows.Network.Netstat |
Active TCP/UDP connections |
Windows.Network.NetstatEnriched |
Connections + process + geo |
Windows.Network.InterfaceAddresses |
NIC addresses and MACs |
Windows.Network.ArpCache |
ARP table entries |
Windows.Network.DNSCache |
DNS resolver cache |
Windows.Network.ListeningPorts |
Open listening ports |
Disk and artifact forensics for deep-dive investigations.
| Artifact | Description |
|---|---|
Windows.Forensics.Prefetch |
Program execution evidence |
Windows.Forensics.USN |
USN journal (file changes) |
Windows.Forensics.MFT |
Master File Table parsing |
Windows.Forensics.Timeline |
Super timeline creation |
Windows.Forensics.SRUM |
System Resource Usage Monitor |
Windows.Forensics.SAM |
SAM database (local accounts) |
Windows.Forensics.Lnk |
LNK (shortcut) file parser |
Windows.Forensics.RecycleBin |
Deleted file recovery |
Windows.Forensics.Shellbags |
Folder access history |
Windows.Forensics.Amcache |
Application compatibility cache |
Registry-based persistence and configuration.
| Artifact | Description |
|---|---|
Windows.Registry.Run |
Run/RunOnce keys |
Windows.Registry.Services |
Service registry entries |
Windows.Registry.Autoruns |
All autostart locations |
Windows.Registry.NTUser |
Per-user NTUser.dat parse |
Windows.Registry.AppCompatCache |
Shimcache entries |
Windows.Registry.RecentDocs |
Recently opened documents |
Event log parsing and analysis.
| Artifact | Description |
|---|---|
Windows.EventLogs.Evtx |
Generic EVTX parser |
Windows.EventLogs.PowerShellScriptBlock |
PS script block logging |
Windows.EventLogs.RDPAuth |
RDP authentication events |
Windows.EventLogs.Logon |
Logon/logoff events (4624/4634) |
Windows.EventLogs.ServiceCreation |
New service installs (7045) |
Windows.EventLogs.ProcessCreation |
Process creation (4688) |
Windows.EventLogs.ScheduledTasks |
Task creation/modification |
Windows.EventLogs.Kerbroasting |
Kerberos ticket requests |
Detection-focused artifacts for known attack patterns.
| Artifact | Description |
|---|---|
Windows.Detection.Yara.Process |
Yara scan process memory |
Windows.Detection.Yara.NTFS |
Yara scan disk files |
Windows.Detection.ProcessInjection |
Detect injected code |
Windows.Detection.BinaryRename |
Renamed system binaries |
Windows.Detection.Amsi |
AMSI bypass detection |
Windows.Detection.ForwardedImports |
Import forwarding abuse |
Windows.Detection.MaliciousPowershell |
Suspicious PS patterns |
| Artifact | Description |
|---|---|
Linux.Sys.Pslist |
Process listing |
Linux.Sys.Maps |
Process memory maps |
Linux.Sys.Crontab |
Cron jobs (all users) |
Linux.Sys.Services |
Systemd services |
Linux.Sys.Users |
User accounts |
Linux.Network.Netstat |
Network connections |
Linux.Forensics.Journal |
Systemd journal logs |
Linux.Detection.Yara.Process |
Yara scan processes |
Linux.Sys.BashHistory |
Bash history files |
Linux.Sys.SSHAuthorizedKeys |
SSH authorized keys |
Linux.Sys.LastLog |
Last login records |
Server-side artifacts run on the Velociraptor server itself, not on clients.
| Artifact | Description |
|---|---|
Server.Hunts.List |
List all hunts |
Server.Hunts.Results |
Get hunt results |
Server.Clients.List |
List all clients |
Server.Internal.Ping |
Check client connectivity |
Server.Utils.CreateCollector |
Build offline collector |
Server.Import.ArtifactExchange |
Import community artifacts |
Server.Monitor.Health |
Server health status |
Server.Enrichment.Virustotal |
VT hash lookup |
You can call any artifact directly from VQL using the Artifact. prefix. This lets you combine multiple artifacts into a single query.
-- Collect an artifact
SELECT * FROM Artifact.Windows.System.Pslist()
-- Artifact with parameters
SELECT * FROM Artifact.Windows.Detection.Yara.NTFS(
PathGlob="C:/Users/**/*.exe",
YaraRule="rule test { strings: $a = \"mimikatz\" condition: $a }")
-- Chain artifacts
LET services = SELECT Name, PathName
FROM Artifact.Windows.System.Services()
WHERE StartMode = "Auto"
SELECT Name, PathName,
hash(path=PathName, hashselect="SHA256") AS SHA256
FROM services
WHERE PathName
Custom artifacts are defined in YAML. They contain metadata, parameters, sources (VQL queries), and optional preconditions. This is the standard structure every artifact follows.
name: Custom.Windows.Detection.SuspiciousService
description: |
Detects services running from non-standard paths.
Flags unsigned binaries and recently created services.
author: Your Name
type: CLIENT # CLIENT, SERVER, CLIENT_EVENT, SERVER_EVENT
parameters:
- name: SuspiciousPathRegex
type: string
default: "^(?i)C:\\\\(Windows|Program Files)"
description: Regex for legitimate service paths (negated in query)
- name: MaxAge
type: int
default: 7
description: Number of days to look back for new services
precondition:
SELECT OS FROM info() WHERE OS = "windows"
sources:
- name: SuspiciousServices
query: |
SELECT Name, DisplayName, PathName, State,
StartMode, StartName AS RunAs,
timestamp(epoch=Key.Mtime) AS RegistryModified,
authenticode(filename=expand(path=PathName)).Trusted AS Signed
FROM Artifact.Windows.System.Services()
WHERE PathName
AND NOT PathName =~ SuspiciousPathRegex
ORDER BY RegistryModified DESC
This artifact hunts for potential C2 beaconing by analyzing network connection patterns. It looks for processes making repeated connections to the same external IP at regular intervals.
name: Custom.Windows.Detection.C2Beaconing
description: |
Detects potential C2 beaconing by looking for processes
with repeated outbound connections to the same remote
IP on the same port. Filters out private IP ranges.
author: Security Team
type: CLIENT
reference:
- https://attack.mitre.org/techniques/T1071/
parameters:
- name: MinConnections
type: int
default: 3
description: Minimum connections to flag
- name: ExcludeIPs
type: string
default: "^(127\\.|10\\.|172\\.(1[6-9]|2|3[01])\\.|192\\.168\\.)"
description: Regex for IPs to exclude
precondition:
SELECT OS FROM info() WHERE OS = "windows"
sources:
- name: BeaconCandidates
query: |
LET connections = SELECT
Pid, Name, Exe,
Raddr.IP AS RemoteIP,
Raddr.Port AS RemotePort,
count() AS ConnCount
FROM netstat()
WHERE Status = "ESTABLISHED"
AND NOT Raddr.IP =~ ExcludeIPs
AND Raddr.IP
GROUP BY Pid, Name, RemoteIP, RemotePort
SELECT Pid, Name, Exe,
RemoteIP, RemotePort, ConnCount,
hash(path=Exe, hashselect="SHA256") AS SHA256
FROM connections
WHERE ConnCount >= MinConnections
ORDER BY ConnCount DESC
- name: ProcessDetails
query: |
LET beacon_pids = SELECT Pid
FROM source(source="BeaconCandidates")
SELECT Pid, Name, Exe, CommandLine,
Username, TokenIsElevated,
{SELECT Name FROM pslist()
WHERE Pid = Ppid} AS ParentName
FROM pslist()
WHERE Pid IN beacon_pids.Pid
Parameters make artifacts configurable at collection time. Preconditions control whether the artifact runs (e.g., only on Windows). These make artifacts portable and reusable.
# Parameter types
parameters:
- name: SearchGlob
type: string
default: "C:/Users/**/*.exe"
description: File glob pattern to search
- name: MaxResults
type: int
default: 500
- name: HashFiles
type: bool
default: true
- name: TargetHashes
type: csv
default: |
Hash
e3b0c44298fc1c149afbf4c8996fb924
- name: DateAfter
type: timestamp
description: Only include files modified after this date
# Preconditions - OS checks
precondition:
SELECT OS FROM info() WHERE OS = "windows"
# Multi-OS precondition
precondition: |
SELECT OS FROM info()
WHERE OS = "windows" OR OS = "linux"
The Velociraptor Artifact Exchange is a community repository of shared artifacts. You can import them into your server or create your own and share them.
# Import artifacts from the exchange (server-side)
velociraptor --config server.config.yaml \
artifacts import /path/to/artifact.yaml
# List available artifacts
velociraptor --config server.config.yaml \
artifacts list | grep -i "detection"
# Show artifact definition
velociraptor --config server.config.yaml \
artifacts show Windows.Detection.Yara.Process
-- Import artifact exchange via VQL (in notebook)
SELECT * FROM Artifact.Server.Import.ArtifactExchange(
ExchangeURL="https://docs.velociraptor.app/exchange/")
-- List all custom artifacts
SELECT name, description, type
FROM artifact_definitions()
WHERE name =~ "^Custom\\."
-- Pack artifacts into a zip for sharing
SELECT * FROM collect(
artifacts="Custom.Windows.Detection.C2Beaconing",
output="/tmp/c2_beacon_artifact.zip")
A hunt pushes a VQL artifact to multiple clients simultaneously. Use hunts to sweep your entire fleet for indicators - compromised credentials, malware, persistence, lateral movement, etc.
-- Create a hunt (via VQL in server notebook)
SELECT * FROM hunt(
description="Hunt for Mimikatz across all Windows endpoints",
artifacts="Windows.Detection.Yara.NTFS",
spec=dict(`Windows.Detection.Yara.NTFS`=dict(
PathGlob="C:/Users/**/*.{exe,dll}",
YaraRule='rule Mimikatz { strings: $a = "mimikatz" wide ascii condition: $a }'
)),
os_condition="windows",
expires=now() + 86400 * 7
)
-- Hunt with label targeting
SELECT * FROM hunt(
description="Collect autoruns from Domain Controllers",
artifacts="Windows.Registry.Autoruns",
label="Domain Controller",
os_condition="windows"
)
# CLI hunt creation
velociraptor --config server.config.yaml \
hunt create \
--artifacts Windows.System.Pslist \
--description "Process listing across fleet" \
--os windows
Schedule recurring collections to continuously monitor endpoints. Useful for baseline comparisons and detecting changes over time (new services, processes, autoruns).
-- Schedule a client monitoring artifact (event query)
SELECT * FROM collect(
client_id="C.1234567890abcdef",
artifacts="Windows.Events.ProcessCreation",
timeout=0 -- run indefinitely
)
-- Collect from a specific client
SELECT * FROM collect(
client_id="C.1234567890abcdef",
artifacts=[
"Windows.System.Pslist",
"Windows.Network.NetstatEnriched",
"Windows.Registry.Autoruns"
],
timeout=600
)
-- Schedule via server event monitoring
SELECT * FROM clock(period=3600)
WHERE log(message="Hourly collection trigger")
AND collect(
client_id="C.1234567890abcdef",
artifacts="Windows.System.Pslist")
The offline collector is a standalone binary that runs artifact collections without needing a server connection. Ideal for air-gapped systems, initial triage, or when you can't install an agent.
# Build an offline collector (interactive)
velociraptor --config server.config.yaml \
collector create \
--artifacts Windows.System.Pslist,Windows.Registry.Autoruns,Windows.Forensics.Prefetch \
--output offline_collector.exe \
--format json
# Build via GUI:
# Server Artifacts -> Server.Utils.CreateCollector
# Select artifacts, configure parameters, download binary
-- Create offline collector via VQL
SELECT * FROM Artifact.Server.Utils.CreateCollector(
OS="Windows",
artifacts=[
"Windows.System.Pslist",
"Windows.Network.NetstatEnriched",
"Windows.Registry.Autoruns",
"Windows.Forensics.Prefetch",
"Windows.EventLogs.PowerShellScriptBlock"
],
parameters=dict(
`Windows.EventLogs.PowerShellScriptBlock`=dict(
DateAfter="2026-03-22"
)
),
output="/tmp/triage_collector.exe"
)
Export hunt and collection results for reporting, SIEM ingestion, or further analysis in external tools. Velociraptor supports JSON, CSV, and direct SIEM forwarding.
-- Export hunt results to CSV
SELECT * FROM hunt_results(
hunt_id="H.1234567890",
artifact="Windows.System.Pslist")
WHERE true
EXPORT(filename="/tmp/hunt_results.csv")
-- Export to JSON lines (for SIEM)
SELECT * FROM hunt_results(
hunt_id="H.1234567890",
artifact="Windows.Registry.Autoruns")
-- Inspect a specific collection
SELECT * FROM flows(client_id="C.1234567890abcdef")
ORDER BY create_time DESC LIMIT 10
-- Download collection results
SELECT * FROM flow_results(
client_id="C.1234567890abcdef",
flow_id="F.1234567890")
# CLI export
velociraptor --config server.config.yaml \
query "SELECT * FROM hunt_results(hunt_id='H.1234567890', artifact='Windows.System.Pslist')" \
--format json > results.json
velociraptor --config server.config.yaml \
query "SELECT * FROM hunt_results(hunt_id='H.1234567890', artifact='Windows.System.Pslist')" \
--format csv > results.csv
The Velociraptor CLI is the primary admin interface for automation, scripting, and headless operation. Every GUI action has a CLI equivalent.
# Run a VQL query against the server
velociraptor --config server.config.yaml query \
"SELECT * FROM clients() WHERE os_info.system = 'windows' LIMIT 10"
# Collect an artifact from a specific client
velociraptor --config server.config.yaml collect \
--client C.1234567890abcdef \
--artifacts Windows.System.Pslist
# List all hunts
velociraptor --config server.config.yaml query \
"SELECT hunt_id, description, state, stats FROM hunts()"
# List all connected clients
velociraptor --config server.config.yaml query \
"SELECT client_id, os_info.hostname AS Host, os_info.system AS OS, last_seen_at FROM clients()"
# Run VQL interactively
velociraptor --config server.config.yaml query --interactive
# Generate API config for external tool integration
velociraptor --config server.config.yaml \
config api_client --name automation_user \
--role reader,analyst \
api_client.yaml
# Run a VQL file
velociraptor --config server.config.yaml query \
--file my_investigation.vql
# Server health check
velociraptor --config server.config.yaml query \
"SELECT * FROM Artifact.Server.Monitor.Health()"