Live verification scoring + standalone score command#355
Draft
mkultraWasHere wants to merge 18 commits into
Draft
Live verification scoring + standalone score command#355mkultraWasHere wants to merge 18 commits into
mkultraWasHere wants to merge 18 commits into
Conversation
Split scoring out of the scoreboard into a standalone `dreadgoad score` command that supports live credential verification against running GOAD hosts. Remove the inference engine entirely — hosts, domains, and techniques are now verified directly rather than inferred from credential graph relationships. Key changes: - New `dreadgoad score` command with `--live-verify` and `--attack-box` flags for testing credentials via nxc/secretsdump on the Kali attack box - `dreadgoad score generate-key` replaces `dreadgoad scoreboard generate-key` (hidden alias kept for backward compat) - ACL-aware credential classification: users targeted by write-capable ACL rights (GenericAll, GenericWrite, WriteDacl, etc.) get `live_auth` verify type instead of `password_match` - ShellRunner interface with SSMShellRunner (AWS) and BastionShellRunner stub (Azure) - LiveVerifier with AuthCheck, AdminCheck, DCSync methods and per-run output caching to avoid duplicate SSM calls - Scoreboard TUI adds static-only warning banner - Agent prompt updated with clearer reporting contract: credentials are the proof, hostname required for host findings, generic examples Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fixes found during critical review and live testing against a running GOAD range: - Normalize hostname matching: strip FQDN suffix and $ so CASTELBLACK.north.sevenkingdoms.local matches castelblack - Accept any domain-matching finding for DCSync, not just static DA list — escalated users (e.g., lord.varys adding himself to DA) now get tested. Known DAs tried first to minimize round-trips - Always use -hashes for secretsdump to avoid impacket user:password@host parsing breaking on passwords with @ or : characters - Tighten nxc [+] match to require username on same line, preventing false positives from informational nxc output - Filter nxc (Guest) auth fallback as invalid — guest access doesn't prove the credential works - Detect STATUS_ACCOUNT_LOCKED_OUT and STATUS_ACCOUNT_DISABLED in nxc output with specific failure reasons - Add --local-auth flag for local accounts (empty domain, bare hostname) - Emit FailedCheck when host/domain IPs missing from answer key or no findings reference a hostname - Fix SSM TimeoutSeconds minimum (must be >= 30) - Add JSON tags to GroupStats, doc comments on phase functions, rename nxcCacheKey to commandCacheKey, update package doc Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implement BastionShellRunner that executes commands on the Kali attack
box via `az network bastion ssh`. Replaces the stub that returned
"not yet implemented".
The runner shells out to `az network bastion ssh` with the command
appended after `--`, capturing stdout non-interactively. Like the SSM
runner, it returns stdout even on non-zero exit (nxc returns non-zero
on auth failure but we need the output).
New CLI flags for Azure:
--bastion-name Azure Bastion resource name
--bastion-rg Azure Bastion resource group
--ssh-key Path to SSH private key for the Kali VM
--ssh-user SSH username (default: "kali")
Usage:
dreadgoad score --live-verify \
--attack-box /subscriptions/.../virtualMachines/kali \
--bastion-name myBastion --bastion-rg myRG \
--ssh-key ~/.dreadgoad/keys/kali-key \
--report report.jsonl
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Require --ssh-key flag for Azure live verification (auth-type is ssh-key, so omitting the key causes opaque connection failures) - Set cmd.Stdin = nil to prevent hangs if ssh prompts for passphrase - Increase timeout buffer from 30s to 60s to account for Bastion API call, tunnel setup, and SSH handshake overhead Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Azure live verification no longer requires --bastion-name, --bastion-rg,
--attack-box, or --ssh-key flags. All are auto-discovered from the
environment:
dreadgoad score --live-verify -p azure --report report.jsonl
Discovery chain:
- Bastion: DiscoverBastion(ctx, env) finds the Bastion host tagged
Project=DreadGOAD + Environment=<env>
- Kali VM: DiscoverKali(ctx, env) finds the VM with Role=AttackBox tag
- SSH key: KaliKeyPath(env, vmName) derives the ephemeral key path from
~/.dreadgoad/keys/azure-{env}-{deployment}-kali
Explicit overrides still work:
--attack-box /subscriptions/.../kali (skip VM discovery)
--ssh-key ~/.ssh/custom_key (skip key discovery)
Also moves kaliKeyPath from cmd/bastion.go to azure.KaliKeyPath for
reuse, and adds azure.DiscoverKali helper.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add docs/scoring.md covering the full dreadgoad score workflow: answer key generation, static scoring, live verification on AWS and Azure, output format, and scoreboard vs score comparison. Update scoreboard.md to reference the new score command and link to scoring.md for live verification and generate-key documentation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…dd summary output - Fix secretsdump -just-dc-user to use NetBIOS domain name instead of FQDN (was causing ERROR_DS_NAME_ERROR_NOT_FOUND for all domains) - Store netbios_name from GOAD config in answer key domain objectives - Remove technique scoring entirely — credentials, hosts, and domains are sufficient proof of exploitation - Remove ~500 lines: extractTechniques, scoreTechniques, ares technique synthesis, technique TUI groups, and related tests - Add human-readable summary to score command output - Update agent prompt to remove technique reporting section - Update scoring docs to remove technique references Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.
- Align doc paths from /tmp/report.jsonl to ./report.jsonl to match updated agent prompt - Fix misleading "regenerate with IPs" error messages to point at patching host_ip/dc_ip post-deployment - Remove stale comments about exploited Redis set and inference paths in transport_ares.go - Include netbios in DCSync cache key to prevent stale cache hits - Make DN prefix filtering case-insensitive in buildACLTargets - Remove technique scoring references from design doc - Fix grammar in agent prompt tips Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…didates - Print human-readable summary to stderr so stdout stays valid JSON for piping to jq - Emit failed_checks when host admin check fails (no Pwn3d!) - Emit failed_checks when no domain candidates exist or all DCSync attempts fail - Filter krbtgt username from DCSync candidates to avoid trying synthetic placeholder evidence Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Update scoreboard run flag default from /tmp/report.jsonl to ./report.jsonl - Update scoreboard run help text and docs table Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…e attack-box format - Emit failed_check when live_auth credential verification is skipped due to missing dc_ip (was silently falling back to static mismatch) - Validate --attack-box format: must start with i- (AWS) or /subscriptions/ (Azure), reject unrecognized values with clear error - Fix stale comment about krbtgt finding being used by scoreDomains - Clarify design doc on provider auto-detection vs -p azure Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…docs Empty username from malformed targets would match any nxc output line (strings.Contains is always true for ""). Also update FailedCheck docstring and scoring.md to reflect that failed_checks includes both errors and clean rejections. Co-Authored-By: Claude <noreply@anthropic.com>
…M poll deadline - Guard against empty username in parseNXCOutput (strings.Contains with empty string always matches) - Break after first credential objective match per finding to prevent duplicate verified entries from repeated reports - Align SSM polling deadline with effective timeout (30s minimum) so poll doesn't expire before SSM command completes - Update FailedCheck docstring and scoring.md field descriptions to reflect actual semantics (errors + clean rejections) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The variant generator did text replacement on .ps1 files but couldn't reach passwords encrypted inside PowerShell SecureString blobs (AES-CBC ciphertext). This leaked original GOAD passwords (e.g. powerkingftw135) into variant deployments via secret.ps1 in SYSVOL. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nflict scoreHosts now retries with --local-auth when domain auth fails for a host finding (e.g. local Administrator SAM hash reported with a domain FQDN in the target). buildNXCCommand no longer passes both -d and --local-auth which nxc rejects as mutually exclusive. Also fixes Azure bastion SSH command passing (was splitting command into separate args). Co-Authored-By: Claude <noreply@anthropic.com>
The variant generator now maps share names (e.g. "thewall") to randomized corporate-style names (e.g. "fileshare", "teamdata"), preventing agents from recognizing original GOAD lab structure. Adds GenerateShareName(), mapShares(), and rebuildShareKeys() following existing patterns for groups/OUs/cities. Share name word list uses compound/uncommon words to avoid collisions with AD config terms. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
scoreHosts now has a second pass: if no hostname-tagged finding verifies admin access, it searches all findings for credentials belonging to the host's configured admin_users list. This handles cases where an agent reports DA creds via DCSync without tagging them with a hostname. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.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)nxc 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-usertargetingChanged
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)buildNXCCommandno longer passes both-dand--local-authwhich nxc rejects as mutually exclusivebash -cto only execute the first wordNotes
generate-key— must be patched into the answer key after deployment (documented inscoring.md)