Plugins
Plugins are the core unit of work in BlackRainbow. Each plugin generates attack sequences, then grades the results after execution. The plugin system uses a decorator-based registry for zero-config discovery.
How Plugins Work
Every plugin implements two methods:
generate(context): Takes anEngagementContext, returns a list ofAttackSequenceobjects. Each sequence defines what tools to run, with what arguments, against what target type.grade(sequence, result): Takes the original sequence and the execution result, returns aGradeResultwith a score, pass/fail, evidence, and metadata.
Optionally, plugins can implement get_model_prompt(context) to generate prompts for the BlackRainbow AI model (used in ZeroBoy, planned).
Plugin Registry
Plugins register themselves via the @register_plugin decorator:
from blackrainbow.plugins import register_plugin
from blackrainbow.plugins.base import PluginBase
@register_plugin
class MyPlugin(PluginBase):
plugin_id = "my-plugin"
description = "Does something useful"
colors = ["red"]
...
At import time, the decorator adds the class to PLUGIN_REGISTRY, a module-level dict keyed by plugin_id. The CLI triggers imports via _import_builtins() to keep startup fast.
Registry functions:
| Function | Description |
|---|---|
register_plugin(cls) | Decorator. Registers a plugin class by its plugin_id. |
get_plugin(plugin_id, config=None) | Instantiates a plugin by ID. Raises ValueError if not found. |
list_plugins() | Returns metadata for all registered plugins. |
Built-in: Recon Plugin
The recon plugin (plugin_id: "recon") handles network and service enumeration using nmap. It generates three scanning phases and grades results by parsing nmap output.
Three-Phase Scanning
Phase 1: Full TCP port scan
nmap -sC -sV -p- -oX /tmp/br-nmap-full-<host>.xml <host>
- Scans all 65,535 TCP ports
- Service and version detection (
-sV) - Default NSE scripts (
-sC) - XML output for structured parsing
- Timeout: 600 seconds
- MITRE: T1046 (Network Service Discovery)
Phase 2: Quick top-ports scan
nmap -sC -sV -oX /tmp/br-nmap-quick-<host>.xml <host>
- Default top 1000 ports
- Fast initial results while full scan runs
- Timeout: 120 seconds
- MITRE: T1046
Phase 3: Vulnerability scripts
nmap --script vuln -oX /tmp/br-nmap-vuln-<host>.xml <host>
- NSE vulnerability detection scripts
- Timeout: 300 seconds
- MITRE: T1595.002 (Active Scanning: Vulnerability Scanning)
Output Parsing
The recon plugin parses both XML and text nmap output:
XML parsing (parse_nmap_services): Extracts <port> elements from nmap XML. For each open port, captures port number, protocol, service name, product, and version.
Text fallback: Parses lines matching PORT/tcp open SERVICE VERSION format. Used when XML output is not available.
Vulnerability parsing (parse_nmap_vulns): Scans XML <script> elements for "VULNERABLE" markers. Falls back to text parsing for |_ prefixed NSE output blocks.
Grading Logic
The recon grader scores on a 0.0 to 1.0 scale:
| Discovery | Score Added |
|---|---|
| Services found (any) | +0.5 |
| Vulnerabilities found (any) | +0.5 |
A grade passes if score > 0 (at least one service discovered). The grade metadata includes the full services list and vulnerability list, which feeds into service accumulation.
Model Prompt
When ZeroBoy is active, the recon plugin generates a prompt asking the model to suggest targeted enumeration based on discovered services. The prompt requests JSON output with tool, args, looking_for, and MITRE technique for each suggestion.
Color System
Each plugin declares one or more colors that categorize its domain:
| Color | Domain |
|---|---|
| red | Offensive operations, exploitation |
| orange | Reconnaissance, enumeration |
| yellow | Credential access, lateral movement |
| blue | Detection, defense |
| green | OSINT, passive collection |
| grey | Detection engineering |
| purple | Combined attack + detect |
The recon plugin declares ["orange", "red", "green"]. Colors are displayed in the br plugins table and are used for filtering in the training pipeline.
Planned Plugins
| Plugin | Description | Status |
|---|---|---|
exploitation | Exploit generation and delivery | Planned |
credential-access | Password attacks, hash cracking | Planned |
web-exploit | Web application attacks | Planned |
Writing a Custom Plugin
Create a new file in blackrainbow/plugins/ (or any importable location):
"""Custom plugin example."""
from typing import Any, Dict, List
from blackrainbow.plugins import register_plugin
from blackrainbow.plugins.base import (
AttackSequence,
EngagementContext,
ExecutionResult,
GradeResult,
PluginBase,
Severity,
)
@register_plugin
class MyCustomPlugin(PluginBase):
plugin_id = "my-custom"
description = "Custom plugin for specific enumeration"
colors = ["orange"]
default_num_tests = 5
default_severity = Severity.MEDIUM
def generate(self, context: EngagementContext) -> List[AttackSequence]:
host = context.target.get("host", "")
return [
AttackSequence(
plugin_id=self.plugin_id,
description=f"Custom scan of {host}",
steps=[{
"tool": "my-tool",
"args": f"--target {host}",
"timeout": 120,
}],
target_type="network-service",
mitre_techniques=["T1046"],
colors=self.colors,
severity=self.severity,
metadata={"phase": "custom"},
)
]
def grade(self, sequence: AttackSequence, result: ExecutionResult) -> GradeResult:
found_something = "interesting" in result.stdout.lower()
return GradeResult(
plugin_id=self.plugin_id,
passed=found_something,
score=1.0 if found_something else 0.0,
objective_achieved=found_something,
evidence=[result.stdout[:500]] if found_something else [],
mitre_techniques=sequence.mitre_techniques,
reasoning="Found target data" if found_something else "Nothing found",
metadata={},
)
PluginBase Class Reference
| Attribute | Type | Default | Description |
|---|---|---|---|
plugin_id | str | "" | Unique identifier, used in config and registry |
description | str | "" | Human-readable description |
colors | List[str] | [] | Color categories |
default_num_tests | int | 5 | Default number of test sequences |
default_severity | Severity | MEDIUM | Default severity level |
| Method | Required | Description |
|---|---|---|
generate(context) | Yes | Generate attack sequences |
grade(sequence, result) | Yes | Grade execution results |
get_model_prompt(context) | No | Generate AI model prompt |
The constructor accepts an optional config dict. It reads numTests and severity from config, falling back to class defaults.