Idea
Current behavior: when a port is detected, recon-deck pulls the KB entry keyed by the service name alone (e.g. `http`, `ssh`, `smb`) and renders the same checklist + commands regardless of what the scanner saw beyond that.
Problem: scanners surface a lot of detail past the service name — language, framework, server header, NSE script output, AutoRecon page screenshots — and that detail dramatically changes what an operator should check next. PHP on 80 is a different attack surface from .NET on 80, even though both are `http`.
Proposal
Layer fingerprint-driven check/command additions on top of the base service KB. Same data flow as today (KB → port → checks/commands), but each KB service entry can declare conditional groups that activate when scan output matches a pattern.
Examples
HTTP w/ PHP detected
- nmap: `http-server-header` shows `X-Powered-By: PHP/7.4`, or `http-php-version` script output
- AutoRecon: `whatweb` / `feroxbuster` extension hits include `.php`
- Adds checks:
- `Tested phpinfo.php / info.php / test.php`
- `Checked common PHP CVEs against detected version`
- `Tested LFI on common PHP entry points`
- Modifies command: `gobuster dir … -x php,html,txt` instead of `gobuster dir …`
HTTP w/ WordPress detected
- whatweb / wpscan / `http-generator` shows WordPress
- Adds checks: `Ran wpscan with API token`, `Tested xmlrpc.php pingback`, `Checked /wp-admin login throttling`
- Adds command: `wpscan --url http://{IP}/ --enumerate vp,vt,u --api-token $WPSCAN_TOKEN`
SMB w/ EternalBlue-vulnerable Windows
- nmap `smb-vuln-ms17-010` script returns VULNERABLE
- Adds command: `msfconsole -q -x "use exploit/windows/smb/ms17_010_eternalblue; set RHOSTS {IP}; …"`
- Pins "Tested for MS17-010 EternalBlue" check (already exists in baseline)
FTP w/ anonymous allowed
- nmap `ftp-anon` shows `Anonymous FTP login allowed`
- Adds check: `Listed anonymous root`
- Adds command: `wget -m ftp://anonymous:anon@{IP}/\`
KB schema sketch
Each service entry gets an optional `conditional` array:
```yaml
service: http
checks:
- key: enumerated-paths
label: Enumerated paths with directory brute-force
commands:
- id: gobuster-dir
label: gobuster directory enum
template: "gobuster dir -u http://{IP} -w {WORDLIST_RAFT_DIRS}"
conditional:
- id: php-detected
when:
anyOf:
- nmap_script_contains: { script: http-server-header, pattern: "PHP" }
- nmap_script_contains: { script: http-php-version, pattern: ".*" }
- autorecon_finding: { type: tech, value: php }
adds_checks:
- key: php-info-pages
label: "Tested phpinfo.php / info.php / test.php"
modifies_commands:
gobuster-dir:
append: " -x php,html,txt"
```
Open questions (research)
- Detection sources — what's reliable across nmap (script output, version field) and AutoRecon (whatweb JSON, feroxbuster ext counts, screenshot OCR)? Need a mini-spec for what counts as a "signal."
- AutoRecon integration — current importer at `src/lib/importer/autorecon.ts` parses tool outputs but discards most fingerprinting metadata. Probably needs to retain a per-port `fingerprints: { tech: string[], cves: string[], banners: string[] }` object.
- Conflict handling — what if two conditionals on the same service add overlapping commands? Last-wins, merge, or surface both?
- Ordering — do conditional checks render at the bottom of the list (clearly-marked "context-specific") or interleaved with baseline checks?
- Re-import semantics — if a re-scan loses the fingerprint, do we silently drop the conditional checks (and lose user state for them) or pin them once they're activated?
- KB lint extension — `scripts/lint-kb.ts` needs to validate the new `when` DSL so author errors surface at build time.
Implementation phases
Probably breaks into:
- P1: KB schema + lint extension for conditional groups (no detection yet)
- P2: Fingerprint extractor for nmap (script output + version banner regex matching)
- P3: Fingerprint extractor for AutoRecon importer
- P4: Resolver — runs at port-render time, applies conditional groups, surfaces "+" diff to operator
- P5: UI badge on each conditional check/command so operators understand why it's there ("detected PHP")
Out of scope (for now)
- Active probing (recon-deck doesn't run scans, only ingests output)
- ML-driven fingerprinting — purely rule-based against scanner output
- User-authored conditionals (KB only initially; user-commands stay flat)
Acceptance (high level)
- Pasting an nmap XML where port 80 has `http-server-header: PHP/7.4` → port detail pane shows PHP-specific checks + gobuster command with `-x php,…`
- Without that signal, port 80 renders the baseline KB only — no regression
- Conditional checks have a visible "why" badge / tooltip
- KB lint catches malformed `when` blocks
Idea
Current behavior: when a port is detected, recon-deck pulls the KB entry keyed by the service name alone (e.g. `http`, `ssh`, `smb`) and renders the same checklist + commands regardless of what the scanner saw beyond that.
Problem: scanners surface a lot of detail past the service name — language, framework, server header, NSE script output, AutoRecon page screenshots — and that detail dramatically changes what an operator should check next. PHP on 80 is a different attack surface from .NET on 80, even though both are `http`.
Proposal
Layer fingerprint-driven check/command additions on top of the base service KB. Same data flow as today (KB → port → checks/commands), but each KB service entry can declare conditional groups that activate when scan output matches a pattern.
Examples
HTTP w/ PHP detected
HTTP w/ WordPress detected
SMB w/ EternalBlue-vulnerable Windows
FTP w/ anonymous allowed
KB schema sketch
Each service entry gets an optional `conditional` array:
```yaml
service: http
checks:
label: Enumerated paths with directory brute-force
commands:
label: gobuster directory enum
template: "gobuster dir -u http://{IP} -w {WORDLIST_RAFT_DIRS}"
conditional:
when:
anyOf:
- nmap_script_contains: { script: http-server-header, pattern: "PHP" }
- nmap_script_contains: { script: http-php-version, pattern: ".*" }
- autorecon_finding: { type: tech, value: php }
adds_checks:
label: "Tested phpinfo.php / info.php / test.php"
modifies_commands:
gobuster-dir:
append: " -x php,html,txt"
```
Open questions (research)
Implementation phases
Probably breaks into:
Out of scope (for now)
Acceptance (high level)