ShellWeDance is a Rust project for PowerShell command-line analysis using psexposed-style indicators. It scores and matches command lines (and decoded -EncodedCommand content) against YAML indicators (regex + basescore + MITRE ATT&CK). The same codebase also provides Sigma rule evaluation against JSON logs via the sigma_zero library and CLI.
Indicators are based on the psexposed indicators format. See powershell.exposed for the online demo.
- PowerShell analysis (
shell-we-dance): Score and analyze PowerShell command lines using psexposed-style indicators (regex + basescore + tactic/technique). This repo ships with 92+ indicators in./indicators. - Base64 decoding: Automatically decodes
-enc/-encodedcommand(Base64 UTF-16LE) and shows the decoded script; matching runs on both the raw command line and the decoded content. - Sigma on decoded content (optional): Run Sigma rules on the decoded script when
--sigma-rules-diris set. - WASM UI: Browser app that loads indicators at runtime, shows severity (Clean / Low / Medium / High / Critical), match table, and which rules failed to load (if any). See wasm/README.md.
- Sigma rule evaluation (
sigma-zero): Evaluate Sigma detection rules against JSON/JSONL logs; streaming mode and correlation rules supported. - Output: Text, JSON, or JSONL; parallel processing with Rayon; per-file load status in CLI.
- Rust 1.70+ (rustup)
git clone https://github.com/ping2A/ShellWeDance
cd ShellWeDance
# Run without installing
cargo run --bin shell-we-dance -- -r ./indicators -c "powershell -enc ZQBjAGgAbwAgACIASABlAGwAbABvACIACgA="
cargo run --bin sigma-zero -- --rules-dir /path/to/sigma-rules --logs ./examples/logs
# Or build release binaries
cargo build --release
# Binaries: target/release/shell-we-dance, target/release/sigma-zero, target/release/sigma-zero-streaming, target/release/sigma-generate-logs
./target/release/shell-we-dance -r ./indicators -c "powershell ..."
./target/release/sigma-zero --rules-dir /path/to/sigma-rules --logs ./examples/logsRun the WASM app and CLI anywhere with Docker (no Rust or wasm-pack needed on the host):
# Build the image (builds CLI + WASM, serves the UI with nginx)
docker build -t shell-we-dance .
# Run and open http://localhost:8080
docker run -p 8080:80 shell-we-danceThe container serves the browser UI on port 80 (mapped to 8080 above). The shell-we-dance CLI is also installed in the container for scripting, e.g.:
docker run --rm shell-we-dance shell-we-dance -r /usr/share/nginx/html/indicators -c "powershell -enc ..."(Indicators in the image live under /usr/share/nginx/html/indicators.)
Analyze PowerShell command lines using psexposed-style indicators. The tool decodes -enc / -encodedcommand (Base64 UTF-16LE) and matches against both the raw command line and the decoded content. Optionally run Sigma rules on the decoded script with --sigma-rules-dir:
# Single command (decoded content shown when -enc is present)
cargo run --bin shell-we-dance -- -r ./indicators -c "powershell -enc ZQBjAGgAbwAgACIASABlAGwAbABvACIACgA="
# With Sigma rules on decoded content (optional)
cargo run --bin shell-we-dance -- -r ./indicators --sigma-rules-dir /path/to/sigma-rules -c "powershell -enc ..."
# From file (one command per line) or stdin
cargo run --bin shell-we-dance -- -r ./indicators -f commands.txt --format json
echo "powershell iex (Get-Content x.ps1)" | cargo run --bin shell-we-dance -- -r ./indicatorsThis repo ships with 92+ indicators in ./indicators (format compatible with psexposed indicators). The CLI prints per-file load status ([ok] / [FAIL]) when loading indicators. See indicators/README.md for indicator format.
Options: -r, --indicators-dir (indicator YAMLs), --sigma-rules-dir (optional Sigma rules for decoded content), -c, --command (command line, repeatable), -f, --file (one command per line), --format text|json|jsonl, --min-score, -v, --verbose.
A browser build runs the same analyzer: paste a PowerShell command and see indicator matches, decoded -EncodedCommand content, severity (Clean / Low / Medium / High / Critical), and a match table. Indicators are loaded at runtime from wasm/indicators/; the UI shows which rules failed to load (if any). The footer links to the original psexposed indicators and to ShellWeDance.
wasm-pack build wasm --out-dir wasm/pkg --target web
cd wasm && python3 -m http.server 8080
# Open http://localhost:8080See wasm/README.md for build and run details.
This repo also includes the sigma-zero CLI for evaluating Sigma detection rules against JSON/JSONL logs:
cargo run --bin sigma-zero -- --rules-dir /path/to/sigma-rules --logs /path/to/logsOptions: -r, --rules-dir, -l, --logs, -c, --correlation-rules, -w, --workers, -o, --output, -f, --format, --validate, --filter-tag, --filter-level, --filter-id, --field-map, -v, --verbose. Run sigma-zero --help for full usage.
# Single log file
cargo run --bin sigma-zero -- -r /path/to/rules -l ./logs/security.json
# JSON output, 8 workers
cargo run --bin sigma-zero -- -r ./rules -l ./logs -f json -o matches.json -w 8
# Validate rules only
cargo run --bin sigma-zero -- -r ./rules --validate
# Filter by tag/level
cargo run --bin sigma-zero -- -r ./rules -l ./logs --filter-tag attack.execution --filter-level highFor real-time or pipe-based evaluation, use sigma-zero-streaming. It reads JSON logs from stdin and evaluates them as they arrive:
# With cargo
tail -f /var/log/app.json | cargo run --bin sigma-zero-streaming -- -r ./rules
journalctl -f -o json | cargo run --bin sigma-zero-streaming -- -r ./rules
# Or after cargo build --release
tail -f /var/log/app.json | sigma-zero-streaming -r ./rulesStreaming options:
-r, --rules-dir– Path to Sigma rules-c, --correlation-rules– Optional correlation rules directory-b, --batch-size <N>– Process logs in batches of N (default: 1 for real-time)-f, --output-format <json|text|silent>– Output format (default: text)-m, --min-level <LEVEL>– Only output matches at or above this level (low, medium, high, critical)
Throughput: Use a larger batch size (e.g. -b 100) to trade latency for higher throughput when reading from a pipe or file.
Logs must be in JSON format with one log entry per line (JSONL). Each log entry should be a JSON object with arbitrary fields:
{
"timestamp": "2025-11-06T10:15:30Z",
"event_type": "process_creation",
"process_name": "powershell.exe",
"command_line": "powershell.exe -enc ZQBjAGgAbwAgACIASABlAGwAbABvACIACgA=",
"user": "john.doe",
"source_ip": "192.168.1.50"
}Rules follow the standard Sigma format. Here's an example:
title: Suspicious Process Execution
id: 12345678-1234-1234-1234-123456789abc
description: Detects execution of suspicious processes
status: experimental
level: high
detection:
selection:
process_name:
- '*powershell.exe'
- '*cmd.exe'
- '*mimikatz*'
command_line:
- '*-enc*'
- '*bypass*'
condition: selection
tags:
- attack.execution
- attack.t1059- Field matching: Exact match, substring match, wildcard (*) support
- Field modifiers:
startswith- Match values that start with patternendswith- Match values that end with patterncontains- Match values containing pattern (default)all- Require all values to match (instead of any)re- Regular expression matchingbase64- Match base64-decoded contentlt/lte/gt/gte- Numeric comparisons
- Advanced Conditions:
AND- All conditions must matchOR- At least one condition must matchNOT- Negate/exclude conditions- Parentheses
()for grouping 1 of them,all of them- Pattern-based selection1 of selection_*- Wildcard selection matching- Threshold/count conditions:
selection_name | count > 5or| count >= N– rule fires when the number of logs matching the selection (in the current batch) satisfies the threshold. Evaluated only in batch mode (file orevaluate_log_batch).
📖 See CONDITION_OPERATORS.md for complete documentation on all operators and modifiers.
- Multiple values: Arrays of values for OR logic
- Conditions:
- Single selection
- AND conditions (all selections must match)
- OR conditions (at least one selection must match)
- Wildcards: Use
*for wildcard matching (e.g.,*powershell*)
See FIELD_MODIFIERS.md for complete field modifier documentation.
- suspicious_process.yml: Detects suspicious process executions like PowerShell with encoded commands
- suspicious_network.yml: Detects connections to known malicious domains or suspicious IPs
- privilege_escalation.yml: Detects privilege escalation attempts
- modifiers_startswith.yml: Demonstrates startswith modifier usage
- modifiers_endswith.yml: Demonstrates endswith modifier for file extensions
- modifiers_regex.yml: Demonstrates regex pattern matching
- modifiers_all.yml: Demonstrates all modifier for multi-condition matching
- modifiers_base64.yml: Demonstrates base64 content detection
- modifiers_comparison.yml: Demonstrates numeric comparison operators
The project includes 4 realistic security log files (170 total events):
- security_events.json (15 events) - Basic security events with mixed legitimate and suspicious activity
- critical_security_events.json (50 events) - Comprehensive attack lifecycle from initial compromise to ransomware
- apt_attack_chain.json (50 events) - Advanced Persistent Threat multi-stage attack campaign
- mixed_traffic.json (55 events) - Realistic mix of legitimate (70%) and malicious (30%) traffic for false positive testing
Attack Coverage: All 12 MITRE ATT&CK tactics represented
Use Cases: Development, testing, training, incident response simulation
The engine automatically uses all available CPU cores. You can control this with the -w flag:
# Use 16 workers for maximum throughput on a 16+ core system
sigma-zero -r ./rules -l ./huge-logs -w 16- Logs are streamed line-by-line to minimize memory usage
- Parsed logs are processed in batches
- Results are collected incrementally
- Compile in release mode: Always use
cargo build --release - Adjust worker count: Match to your CPU core count for best results
- Use SSD storage: Faster disk I/O significantly improves performance
- Rule optimization: More specific rules (fewer wildcards) evaluate faster
To benchmark performance on your system:
# Create a large test log file
seq 1 1000000 | while read i; do
echo "{\"id\": $i, \"process_name\": \"test.exe\", \"command_line\": \"test command $i\"}"
done > large_test.json
# Time the evaluation
time sigma-zero -r /path/to/rules -l large_test.json -w $(nproc)Matches are output in JSON format:
{
"rule_id": "12345678-1234-1234-1234-123456789abc",
"rule_title": "Suspicious Process Execution",
"level": "high",
"matched_log": {
"timestamp": "2025-11-06T10:15:30Z",
"process_name": "powershell.exe",
"command_line": "powershell.exe -enc ...",
"user": "john.doe"
},
"timestamp": "2025-11-06T12:30:45.123Z"
}- Condition complexity: Complex condition expressions with nested parentheses and NOT operators are simplified
- Aggregation: Time-based aggregations and correlations not yet supported
- Field modifiers: Most common modifiers implemented (startswith, endswith, contains, all, re, base64, comparisons). Advanced modifiers like utf16le/utf16be are planned for future releases
- psexposed indicators — Original indicator format and community rules
- powershell.exposed — Online demo of psexposed-style analysis
- Sigma — Sigma rule format
- MITRE ATT&CK — Tactics and techniques
## About
ShellWeDance focuses on PowerShell command-line analysis using psexposed-style indicators (regex + score + MITRE ATT&CK). The same repo includes the sigma-zero engine and CLIs for evaluating Sigma rules against JSON logs, so you can use it as a small local SIEM or to score specific logs and command lines.
