Initial commit
This commit is contained in:
@@ -0,0 +1,627 @@
|
||||
# Velociraptor Artifact Development Guide
|
||||
|
||||
Guide to creating custom VQL artifacts for specific investigation and threat hunting scenarios.
|
||||
|
||||
## Table of Contents
|
||||
- [Artifact Structure](#artifact-structure)
|
||||
- [Parameter Types](#parameter-types)
|
||||
- [Source Types](#source-types)
|
||||
- [Best Practices](#best-practices)
|
||||
- [Common Patterns](#common-patterns)
|
||||
- [Testing Artifacts](#testing-artifacts)
|
||||
|
||||
## Artifact Structure
|
||||
|
||||
Velociraptor artifacts are YAML files with a defined structure:
|
||||
|
||||
```yaml
|
||||
name: Category.Subcategory.ArtifactName
|
||||
description: |
|
||||
Detailed description of what this artifact collects and why.
|
||||
Include use cases and expected output.
|
||||
|
||||
author: Your Name <email@domain.com>
|
||||
|
||||
type: CLIENT # CLIENT, SERVER, or CLIENT_EVENT
|
||||
|
||||
parameters:
|
||||
- name: ParameterName
|
||||
default: "default_value"
|
||||
type: string
|
||||
description: Parameter description
|
||||
|
||||
precondition: |
|
||||
SELECT OS FROM info() WHERE OS = 'windows'
|
||||
|
||||
sources:
|
||||
- name: SourceName
|
||||
query: |
|
||||
SELECT * FROM plugin()
|
||||
WHERE condition
|
||||
|
||||
reports:
|
||||
- type: CLIENT
|
||||
template: |
|
||||
# Report Title
|
||||
{{ .Description }}
|
||||
|
||||
{{ range .Rows }}
|
||||
- {{ .Column }}
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
### Required Fields
|
||||
|
||||
- **name**: Unique artifact identifier in dot notation
|
||||
- **description**: What the artifact does and when to use it
|
||||
- **sources**: At least one VQL query source
|
||||
|
||||
### Optional Fields
|
||||
|
||||
- **author**: Creator information
|
||||
- **type**: Artifact type (CLIENT, SERVER, CLIENT_EVENT)
|
||||
- **parameters**: User-configurable inputs
|
||||
- **precondition**: Check before running (OS, software presence)
|
||||
- **reports**: Output formatting templates
|
||||
- **references**: External documentation links
|
||||
|
||||
## Parameter Types
|
||||
|
||||
### String Parameters
|
||||
|
||||
```yaml
|
||||
parameters:
|
||||
- name: SearchPath
|
||||
default: "C:/Windows/System32/"
|
||||
type: string
|
||||
description: Directory path to search
|
||||
```
|
||||
|
||||
### Integer Parameters
|
||||
|
||||
```yaml
|
||||
parameters:
|
||||
- name: DaysBack
|
||||
default: 7
|
||||
type: int
|
||||
description: Number of days to look back
|
||||
```
|
||||
|
||||
### Boolean Parameters
|
||||
|
||||
```yaml
|
||||
parameters:
|
||||
- name: IncludeSystem
|
||||
default: Y
|
||||
type: bool
|
||||
description: Include system files
|
||||
```
|
||||
|
||||
### Regex Parameters
|
||||
|
||||
```yaml
|
||||
parameters:
|
||||
- name: ProcessPattern
|
||||
default: "(?i)(powershell|cmd)"
|
||||
type: regex
|
||||
description: Process name pattern to match
|
||||
```
|
||||
|
||||
### Choice Parameters
|
||||
|
||||
```yaml
|
||||
parameters:
|
||||
- name: LogLevel
|
||||
default: "INFO"
|
||||
type: choices
|
||||
choices:
|
||||
- DEBUG
|
||||
- INFO
|
||||
- WARNING
|
||||
- ERROR
|
||||
description: Logging verbosity
|
||||
```
|
||||
|
||||
### CSV Parameters
|
||||
|
||||
```yaml
|
||||
parameters:
|
||||
- name: IOCList
|
||||
default: |
|
||||
evil.com
|
||||
malicious.net
|
||||
type: csv
|
||||
description: List of IOC domains
|
||||
```
|
||||
|
||||
## Source Types
|
||||
|
||||
### Query Sources
|
||||
|
||||
Standard VQL query that collects data:
|
||||
|
||||
```yaml
|
||||
sources:
|
||||
- name: ProcessCollection
|
||||
query: |
|
||||
SELECT Pid, Name, CommandLine, Username
|
||||
FROM pslist()
|
||||
WHERE Name =~ ProcessPattern
|
||||
```
|
||||
|
||||
### Event Sources
|
||||
|
||||
Continuous monitoring queries for CLIENT_EVENT artifacts:
|
||||
|
||||
```yaml
|
||||
sources:
|
||||
- name: ProcessCreation
|
||||
query: |
|
||||
SELECT * FROM watch_evtx(
|
||||
filename="C:/Windows/System32/winevt/Logs/Security.evtx"
|
||||
)
|
||||
WHERE System.EventID.Value = 4688
|
||||
```
|
||||
|
||||
### Multiple Sources
|
||||
|
||||
Artifacts can have multiple sources for different data collection:
|
||||
|
||||
```yaml
|
||||
sources:
|
||||
- name: Processes
|
||||
query: |
|
||||
SELECT * FROM pslist()
|
||||
|
||||
- name: NetworkConnections
|
||||
query: |
|
||||
SELECT * FROM netstat()
|
||||
|
||||
- name: LoadedDLLs
|
||||
query: |
|
||||
SELECT * FROM modules()
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Use Preconditions
|
||||
|
||||
Prevent artifact execution on incompatible systems:
|
||||
|
||||
```yaml
|
||||
# Windows-only artifact
|
||||
precondition: |
|
||||
SELECT OS FROM info() WHERE OS = 'windows'
|
||||
|
||||
# Requires specific tool
|
||||
precondition: |
|
||||
SELECT * FROM stat(filename="C:/Tools/sysinternals/psexec.exe")
|
||||
|
||||
# Version check
|
||||
precondition: |
|
||||
SELECT * FROM info() WHERE OS = 'windows' AND OSVersion =~ '10'
|
||||
```
|
||||
|
||||
### 2. Parameterize Paths and Patterns
|
||||
|
||||
Make artifacts flexible and reusable:
|
||||
|
||||
```yaml
|
||||
parameters:
|
||||
- name: TargetPath
|
||||
default: "C:/Users/**/AppData/**"
|
||||
type: string
|
||||
|
||||
- name: FilePattern
|
||||
default: "*.exe"
|
||||
type: string
|
||||
|
||||
sources:
|
||||
- query: |
|
||||
SELECT * FROM glob(globs=TargetPath + "/" + FilePattern)
|
||||
```
|
||||
|
||||
### 3. Use LET for Query Composition
|
||||
|
||||
Break complex queries into manageable parts:
|
||||
|
||||
```yaml
|
||||
sources:
|
||||
- query: |
|
||||
-- Define reusable subqueries
|
||||
LET SuspiciousProcesses = SELECT Pid, Name, CommandLine
|
||||
FROM pslist()
|
||||
WHERE CommandLine =~ "(?i)(bypass|hidden)"
|
||||
|
||||
LET NetworkConnections = SELECT Pid, Raddr.IP AS RemoteIP
|
||||
FROM netstat()
|
||||
WHERE Status = "ESTABLISHED"
|
||||
|
||||
-- Join and correlate
|
||||
SELECT sp.Name,
|
||||
sp.CommandLine,
|
||||
nc.RemoteIP
|
||||
FROM SuspiciousProcesses sp
|
||||
JOIN NetworkConnections nc ON sp.Pid = nc.Pid
|
||||
```
|
||||
|
||||
### 4. Add Error Handling
|
||||
|
||||
Handle missing data gracefully:
|
||||
|
||||
```yaml
|
||||
sources:
|
||||
- query: |
|
||||
SELECT * FROM foreach(
|
||||
row={
|
||||
SELECT FullPath FROM glob(globs=SearchPath)
|
||||
},
|
||||
query={
|
||||
SELECT FullPath,
|
||||
hash(path=FullPath, accessor="file").SHA256 AS SHA256
|
||||
FROM scope()
|
||||
WHERE log(message="Processing: " + FullPath)
|
||||
},
|
||||
workers=5
|
||||
)
|
||||
WHERE SHA256 -- Filter out hash failures
|
||||
```
|
||||
|
||||
### 5. Include Documentation
|
||||
|
||||
Add inline comments and comprehensive descriptions:
|
||||
|
||||
```yaml
|
||||
description: |
|
||||
## Overview
|
||||
This artifact hunts for suspicious scheduled tasks.
|
||||
|
||||
## Use Cases
|
||||
- Persistence mechanism detection
|
||||
- Lateral movement artifact collection
|
||||
- Threat hunting campaigns
|
||||
|
||||
## Output
|
||||
Returns task name, actions, triggers, and creation time.
|
||||
|
||||
## References
|
||||
- MITRE ATT&CK T1053.005 (Scheduled Task/Job)
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Pattern: File Collection with Hashing
|
||||
|
||||
```yaml
|
||||
name: Custom.Windows.FileCollection
|
||||
description: Collect files matching patterns with hashes
|
||||
|
||||
parameters:
|
||||
- name: GlobPatterns
|
||||
default: |
|
||||
C:/Users/**/AppData/**/*.exe
|
||||
C:/Windows/Temp/**/*.dll
|
||||
type: csv
|
||||
|
||||
sources:
|
||||
- query: |
|
||||
SELECT FullPath,
|
||||
Size,
|
||||
timestamp(epoch=Mtime) AS Modified,
|
||||
timestamp(epoch=Btime) AS Created,
|
||||
hash(path=FullPath, accessor="file") AS Hashes
|
||||
FROM foreach(
|
||||
row={
|
||||
SELECT * FROM parse_csv(filename=GlobPatterns, accessor="data")
|
||||
},
|
||||
query={
|
||||
SELECT * FROM glob(globs=_value)
|
||||
}
|
||||
)
|
||||
WHERE NOT IsDir
|
||||
```
|
||||
|
||||
### Pattern: Event Log Analysis
|
||||
|
||||
```yaml
|
||||
name: Custom.Windows.EventLogHunt
|
||||
description: Hunt for specific event IDs with context
|
||||
|
||||
parameters:
|
||||
- name: LogFile
|
||||
default: "C:/Windows/System32/winevt/Logs/Security.evtx"
|
||||
type: string
|
||||
|
||||
- name: EventIDs
|
||||
default: "4624,4625,4672"
|
||||
type: csv
|
||||
|
||||
sources:
|
||||
- query: |
|
||||
LET EventIDList = SELECT parse_string_with_regex(
|
||||
string=EventIDs,
|
||||
regex="(\\d+)"
|
||||
).g1 AS EventID FROM scope()
|
||||
|
||||
SELECT timestamp(epoch=System.TimeCreated.SystemTime) AS EventTime,
|
||||
System.EventID.Value AS EventID,
|
||||
System.Computer AS Computer,
|
||||
EventData
|
||||
FROM parse_evtx(filename=LogFile)
|
||||
WHERE str(str=System.EventID.Value) IN EventIDList.EventID
|
||||
ORDER BY EventTime DESC
|
||||
```
|
||||
|
||||
### Pattern: Process Tree Analysis
|
||||
|
||||
```yaml
|
||||
name: Custom.Windows.ProcessTree
|
||||
description: Build process tree from a starting PID
|
||||
|
||||
parameters:
|
||||
- name: RootPID
|
||||
default: 0
|
||||
type: int
|
||||
description: Starting process PID (0 for all)
|
||||
|
||||
sources:
|
||||
- query: |
|
||||
LET ProcessList = SELECT Pid, Ppid, Name, CommandLine, Username, CreateTime
|
||||
FROM pslist()
|
||||
|
||||
LET RECURSIVE GetChildren(ParentPID) = SELECT *
|
||||
FROM ProcessList
|
||||
WHERE Ppid = ParentPID
|
||||
|
||||
LET RECURSIVE BuildTree(Level, ParentPID) = SELECT
|
||||
Level,
|
||||
Pid,
|
||||
Ppid,
|
||||
Name,
|
||||
CommandLine,
|
||||
Username,
|
||||
CreateTime
|
||||
FROM GetChildren(ParentPID=ParentPID)
|
||||
UNION ALL
|
||||
SELECT * FROM BuildTree(Level=Level+1, ParentPID=Pid)
|
||||
|
||||
SELECT * FROM if(
|
||||
condition=RootPID > 0,
|
||||
then={
|
||||
SELECT * FROM BuildTree(Level=0, ParentPID=RootPID)
|
||||
},
|
||||
else={
|
||||
SELECT 0 AS Level, * FROM ProcessList
|
||||
}
|
||||
)
|
||||
ORDER BY CreateTime
|
||||
```
|
||||
|
||||
### Pattern: Network IOC Matching
|
||||
|
||||
```yaml
|
||||
name: Custom.Windows.NetworkIOCMatch
|
||||
description: Match network connections against IOC list
|
||||
|
||||
parameters:
|
||||
- name: IOCList
|
||||
default: |
|
||||
IP,Description
|
||||
192.0.2.1,C2 Server
|
||||
198.51.100.50,Malicious Host
|
||||
type: csv
|
||||
|
||||
sources:
|
||||
- query: |
|
||||
LET IOCs = SELECT IP, Description
|
||||
FROM parse_csv(filename=IOCList, accessor="data")
|
||||
|
||||
LET Connections = SELECT
|
||||
Raddr.IP AS RemoteIP,
|
||||
Raddr.Port AS RemotePort,
|
||||
Pid,
|
||||
process_tracker_get(id=Pid).Name AS ProcessName,
|
||||
process_tracker_get(id=Pid).CommandLine AS CommandLine
|
||||
FROM netstat()
|
||||
WHERE Status = "ESTABLISHED"
|
||||
|
||||
SELECT c.RemoteIP,
|
||||
c.RemotePort,
|
||||
c.ProcessName,
|
||||
c.CommandLine,
|
||||
i.Description AS IOCMatch
|
||||
FROM Connections c
|
||||
JOIN IOCs i ON c.RemoteIP = i.IP
|
||||
```
|
||||
|
||||
### Pattern: Registry Timeline
|
||||
|
||||
```yaml
|
||||
name: Custom.Windows.RegistryTimeline
|
||||
description: Timeline registry modifications in specific keys
|
||||
|
||||
parameters:
|
||||
- name: RegistryPaths
|
||||
default: |
|
||||
HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows/CurrentVersion/Run/**
|
||||
HKEY_CURRENT_USER/SOFTWARE/Microsoft/Windows/CurrentVersion/Run/**
|
||||
type: csv
|
||||
|
||||
- name: DaysBack
|
||||
default: 7
|
||||
type: int
|
||||
|
||||
sources:
|
||||
- query: |
|
||||
LET StartTime = timestamp(epoch=now() - DaysBack * 86400)
|
||||
|
||||
SELECT timestamp(epoch=Key.Mtime) AS Modified,
|
||||
Key.FullPath AS RegistryPath,
|
||||
ValueName,
|
||||
ValueData.value AS Value
|
||||
FROM foreach(
|
||||
row={
|
||||
SELECT * FROM parse_csv(filename=RegistryPaths, accessor="data")
|
||||
},
|
||||
query={
|
||||
SELECT * FROM read_reg_key(globs=_value)
|
||||
}
|
||||
)
|
||||
WHERE Key.Mtime > StartTime
|
||||
ORDER BY Modified DESC
|
||||
```
|
||||
|
||||
## Testing Artifacts
|
||||
|
||||
### 1. Local Testing with GUI
|
||||
|
||||
```bash
|
||||
# Start Velociraptor in GUI mode
|
||||
velociraptor gui
|
||||
|
||||
# Navigate to: View Artifacts → Add Artifact
|
||||
# Paste your artifact YAML and click Save
|
||||
# Run artifact via Collected Artifacts → New Collection
|
||||
```
|
||||
|
||||
### 2. Command Line Testing
|
||||
|
||||
```bash
|
||||
# Test artifact syntax
|
||||
velociraptor artifacts show Custom.Artifact.Name
|
||||
|
||||
# Run artifact locally
|
||||
velociraptor artifacts collect Custom.Artifact.Name \
|
||||
--args ParameterName=value \
|
||||
--format json
|
||||
|
||||
# Run with output file
|
||||
velociraptor artifacts collect Custom.Artifact.Name \
|
||||
--output results.json
|
||||
```
|
||||
|
||||
### 3. Notebook Testing
|
||||
|
||||
Use VQL notebooks for interactive development:
|
||||
|
||||
```sql
|
||||
-- Test query components in isolation
|
||||
SELECT * FROM pslist() WHERE Name =~ "powershell" LIMIT 10
|
||||
|
||||
-- Test parameter substitution
|
||||
LET ProcessPattern = "(?i)(powershell|cmd)"
|
||||
SELECT * FROM pslist() WHERE Name =~ ProcessPattern
|
||||
|
||||
-- Test full artifact query
|
||||
/* Paste your artifact query here */
|
||||
```
|
||||
|
||||
### 4. Validation Checklist
|
||||
|
||||
Before deploying artifacts:
|
||||
|
||||
- [ ] Artifact name follows convention: Category.Subcategory.Name
|
||||
- [ ] Description includes use cases and expected output
|
||||
- [ ] Parameters have sensible defaults
|
||||
- [ ] Precondition prevents incompatible execution
|
||||
- [ ] Query tested in notebook mode
|
||||
- [ ] Error handling for missing data
|
||||
- [ ] Performance acceptable on test system
|
||||
- [ ] Output format is useful and parseable
|
||||
- [ ] Documentation includes MITRE ATT&CK mapping if applicable
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Limit Scope
|
||||
|
||||
```yaml
|
||||
# BAD: Scans entire filesystem
|
||||
SELECT * FROM glob(globs="C:/**/*.exe")
|
||||
|
||||
# GOOD: Targeted scope
|
||||
SELECT * FROM glob(globs=[
|
||||
"C:/Users/**/AppData/**/*.exe",
|
||||
"C:/Windows/Temp/**/*.exe"
|
||||
])
|
||||
```
|
||||
|
||||
### Use Workers for Parallel Processing
|
||||
|
||||
```yaml
|
||||
sources:
|
||||
- query: |
|
||||
SELECT * FROM foreach(
|
||||
row={SELECT * FROM glob(globs=SearchPath)},
|
||||
query={
|
||||
SELECT FullPath,
|
||||
hash(path=FullPath, accessor="file").SHA256 AS SHA256
|
||||
FROM scope()
|
||||
},
|
||||
workers=10 -- Process 10 files concurrently
|
||||
)
|
||||
```
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
```yaml
|
||||
sources:
|
||||
- query: |
|
||||
SELECT * FROM foreach(
|
||||
row={SELECT * FROM glob(globs="C:/**")},
|
||||
query={
|
||||
SELECT * FROM scope()
|
||||
WHERE rate(query_name="my_query", ops_per_sec=100)
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
## MITRE ATT&CK Mapping
|
||||
|
||||
Map artifacts to MITRE ATT&CK techniques:
|
||||
|
||||
```yaml
|
||||
name: Custom.Windows.PersistenceHunt
|
||||
description: |
|
||||
Hunt for persistence mechanisms.
|
||||
|
||||
MITRE ATT&CK Techniques:
|
||||
- T1547.001: Registry Run Keys / Startup Folder
|
||||
- T1053.005: Scheduled Task/Job
|
||||
- T1543.003: Windows Service
|
||||
- T1546.003: Windows Management Instrumentation Event Subscription
|
||||
|
||||
references:
|
||||
- https://attack.mitre.org/techniques/T1547/001/
|
||||
- https://attack.mitre.org/techniques/T1053/005/
|
||||
```
|
||||
|
||||
## Artifact Distribution
|
||||
|
||||
### Export Artifacts
|
||||
|
||||
```bash
|
||||
# Export single artifact
|
||||
velociraptor artifacts show Custom.Artifact.Name > artifact.yaml
|
||||
|
||||
# Export all custom artifacts
|
||||
velociraptor artifacts list --filter Custom > all_artifacts.yaml
|
||||
```
|
||||
|
||||
### Import Artifacts
|
||||
|
||||
```bash
|
||||
# Via command line
|
||||
velociraptor --config server.config.yaml artifacts import artifact.yaml
|
||||
|
||||
# Via GUI
|
||||
# Navigate to: View Artifacts → Upload Artifact Pack
|
||||
```
|
||||
|
||||
### Share via Artifact Exchange
|
||||
|
||||
Contribute artifacts to the community:
|
||||
|
||||
1. Test thoroughly across different systems
|
||||
2. Document clearly with examples
|
||||
3. Add MITRE ATT&CK mappings
|
||||
4. Submit to: https://docs.velociraptor.app/exchange/
|
||||
Reference in New Issue
Block a user