Live verification scoring + standalone score command - #355
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a standalone dreadgoad score command that can optionally live-verify agent-reported credentials against a running GOAD lab (via nxc smb + secretsdump.py executed on the Kali attack box). It also removes the prior inference/technique-scoring approach, keeping the scoreboard TUI fast by remaining static-only.
Changes:
- Adds
dreadgoad score+score generate-key, with shared scoring logic (ScoreReport) and optional live verification (LiveVerifier). - Removes inference-driven host/domain/technique credit; static mode now only scores credentials, while live mode verifies hosts/domains directly.
- Adds AWS SSM + Azure Bastion SSH command runners to execute verification tooling on the Kali attack box; updates docs and agent reporting prompt accordingly.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| scoreboard/agent_prompt.md | Updates agent reporting spec (header + ./report.jsonl, host findings require hostname, scoring guidance). |
| docs/scoring.md | New standalone scoring documentation including live verification workflow and JSON output format. |
| docs/scoreboard.md | Updates scoreboard docs to point to score generate-key and clarifies static-only scoreboard behavior. |
| docs/design-live-verification.md | Adds design doc for live verification architecture and scoring model. |
| cli/internal/scoreboard/verify.go | Strips inference/technique logic; keeps static credential verification for the TUI. |
| cli/internal/scoreboard/verify_test.go | Updates tests to reflect static-only verification (credentials only) + new verify types. |
| cli/internal/scoreboard/types.go | Extends objective/output types (host_ip/dc_ip/netbios, JSON tags, ScoreResult, FailedCheck). |
| cli/internal/scoreboard/tui.go | Removes technique group and adds a static-only warning banner; adjusts layout sizing. |
| cli/internal/scoreboard/transport_ares.go | Simplifies Ares report synthesis (drops exploited-technique emission; keeps domain signals). |
| cli/internal/scoreboard/shell_ssm.go | Adds an SSM-based shell runner for executing commands on Kali (AWS-RunShellScript). |
| cli/internal/scoreboard/shell_bastion.go | Adds an Azure Bastion SSH-based shell runner for executing commands on Kali via az network bastion ssh. |
| cli/internal/scoreboard/score.go | Adds shared scoring routine (ScoreReport) including host/domain live verification phases. |
| cli/internal/scoreboard/live.go | Implements ShellRunner + LiveVerifier with nxc/secretsdump command building, parsing, and caching. |
| cli/internal/scoreboard/generate.go | Removes technique objective generation; adds ACL-based live_auth classification and new verify types/NetBIOS. |
| cli/internal/azure/kali.go | Exports azure.KaliKeyPath() and adds Azure discovery of the Kali VM by tag. |
| cli/cmd/scoreboard.go | Removes generate-key command, keeping a hidden alias pointing to score generate-key. |
| cli/cmd/score.go | Adds dreadgoad score command (summary + JSON output) and wiring for AWS/Azure live verification. |
| cli/cmd/bastion.go | Switches to the exported azure.KaliKeyPath() helper. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 31 out of 32 changed files in this pull request and generated 3 comments.
Files not reviewed (1)
- modules/terraform-azure-kali/.terraform.lock.hcl: Generated file
Comments suppressed due to low confidence (1)
scoreboard/agent_prompt-2.md:59
- This section says findings are scored on "passwords obtained", but the format and examples allow
evidenceto be an NTLM hash as well. Updating the wording to "credentials obtained" keeps the scoring description consistent with the required fields and examples.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 32 out of 33 changed files in this pull request and generated 3 comments.
Files not reviewed (1)
- modules/terraform-azure-kali/.terraform.lock.hcl: Generated file
Comments suppressed due to low confidence (1)
cli/internal/variant/securestring.go:113
- decryptSecureString() splits the inner payload into 3 fields but doesn’t validate the SecureString version field. If the blob isn’t version 2, the function may produce a misleading downstream error (or decode garbage). It’s better to explicitly require
parts[0] == "2"and return a clear error when it doesn’t match.
// Format: "2|iv_base64|ct_hex" (version 2 with AES key).
parts := strings.SplitN(inner, "|", 3)
if len(parts) != 3 {
return "", fmt.Errorf("expected version|iv|ct format, got %d parts", len(parts))
}
Run pre-commit locally to resolve CI failures from auto-fix hooks: - markdownlint: add blank lines before lists (MD032) - terraform validate: regenerate lock file hashes for current platform Co-Authored-By: Claude <noreply@anthropic.com>
…lures - checks.go: quote the $target value inside Get-ADObject -Filter to fix AD filter parse errors on names containing dots or spaces (e.g. "brenda.rogers", "Domain Admins") — was producing 14 false WARN results - shell_bastion.go: base64-encode commands before passing through Azure Bastion SSH to prevent long/multi-line scripts from being mangled by intermediate shell argument parsing - score_reset.go: remove outer parens from the agent-report find command to avoid $( ( ... ) ) being parsed as arithmetic expansion $(( ... )), which crashed the Kali cleanup script with division-by-zero Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Kali marketplace image doesn't ship nxc or impacket, causing live verification (host admin checks, DCSync) to silently fail. Add packages and a secretsdump.py wrapper to cloud-init so future VMs are ready for scoring out of the box. Co-Authored-By: Claude <noreply@anthropic.com>
ACL probes sometimes returned empty stdout through the SOCKS5/WinRM tunnel, causing false WARN results (14 of 19 ACLs). The existing runPSErr retries weren't sufficient under concurrent load. Add a check-level retry loop (up to transientRetries with backoff) that re-runs the probe when output lacks any expected marker. Co-Authored-By: Claude <noreply@anthropic.com>
checkACLPermissions had cyclomatic complexity 16, exceeding the gocyclo -over 15 threshold and failing CI. Extract the per-ACL loop body into checkSingleACL to split the complexity. Co-Authored-By: Claude <noreply@anthropic.com>
Hooks without an explicit stages restriction run in all stages including manual. The SKIP env var was only set on the first pre-commit step, so terraform_validate ran unblocked in the manual step, wrote .terraform.lock.hcl, and failed with "files were modified." Co-Authored-By: Claude <noreply@anthropic.com>
Standalone
dreadgoad scorecommand that tests agent-reported credentials against the running GOAD lab via nxc/secretsdump on the Kali attack box, replacing the old inference engine. Also adds ascore resetsubcommand for cleaning up between agent runs.Added
dreadgoad scorecommand — scores an agent JSONL report against the answer key with optional--live-verifyfor authoritative resultsdreadgoad score generate-key— moved fromscoreboard generate-key(hidden alias kept)dreadgoad score reset— cleans Kali attack box (nxc databases, tickets, NTDS dumps, Responder logs, Dreadnode sessions) and Windows hosts (webshells, share drops, temp scripts, registry dumps) between agent runs. Supports dry-run mode, rogue AD computer account purge, group membership diff, and attack tool exe blacklistnxc smb(auth check[+], admin check(Pwn3d!)) andsecretsdump.py -just-dc-user(DCSync proof)ShellRunnerinterface with AWS SSM and Azure Bastion SSH implementationsRole=AttackBoxtag), and SSH key from environment tags — zero flags needed with-p azuredocs/scoring.mduser-facing documentation anddocs/design-live-verification.mddesign docnetbios_namefield in answer key domain objectives for correct secretsdump-just-dc-usertargetingscoreboard/agent_prompt-2.md) with informal/defensive framingChanged
live_auth(ACL abuse targets whose passwords may change) vspassword_match(static comparison) based on config ACL edgesdreadgoad score --live-verifyfor verified resultskaliKeyPath()moved fromcli/cmd/bastion.goto exportedazure.KaliKeyPath()incli/internal/azure/kali.goscoreboard/agent_prompt.md) restructured: generic examples,hostnamerequired for host findings, report path changed from/tmp/report.jsonlto./report.jsonlBreaking
generate-keyoutput drops from ~69 to ~38 objectives. Techniques were redundant with credential/host/domain proof and suffered from ID alignment issues between agent and answer key.inferHosts(),inferDomains(),inferTechniques()and ~170 lines of credential-graph inference deleted fromverify.go. The scoreboard TUI now shows 0 for hosts/domains (static-only); usedreadgoad score --live-verifyfor those.Fixed
secretsdump -just-dc-userno longer fails withERROR_DS_NAME_ERROR_NOT_FOUND— was passing the FQDN (hq.deltasystems.local/krbtgt) instead of the NetBIOS name (HQ/krbtgt)[+]false positives from informational output lines and(Guest)fallback auth are now filtered@or:no longer break impacket'suser:password@hostparsing — always uses-hasheswith computed NT hashcmd.Stdin = nil)powerkingftw135) into variant deployments viasecret.ps1in SYSVOL--local-authwhen domain auth fails — fixes scoring of local Administrator SAM hashes reported with a domain FQDN target (e.g.Administrator@vortexindustries.localfrom a SAM dump)admin_userslist when findings lack ahostnametag — DA creds from DCSync can now verify host access without explicit hostname taggingbuildNXCCommandno longer passes both-dand--local-authwhich nxc rejects as mutually exclusivebash -cto only execute the first wordrunPS()callers migrated torunPSErr()so SOCKS/WinRM failures emit WARN instead of false FAIL (or false PASS for firewall). Fixes intermittent false negatives on Azure where SOCKS proxy flakiness caused checks like CertEnroll, SMB shares, domain trusts, and GPOs to report "not found" when the resource was present.-p azureNotes
generate-key— must be patched into the answer key after deployment (documented inscoring.md)