Skip to content

Commit 995c182

Browse files
authored
feat: add universal and missing ADCS techniques to scoreboard logic (#207)
**Key Changes:** - Added support for ADCS ESC8, ESC5, and ESC14 techniques in scoreboard logic - Implemented universal technique attribution for default lab vulnerabilities - Updated tests and config to include new techniques and ensure coverage **Added:** - Universal technique attribution function - Added `addUniversalTechniques` to scoreboard generation, crediting lab-wide vulnerabilities such as noPac, PrintNightmare, ZeroLogon, Certifried, Machine Account Quota abuse, MITM6, and others, making them visible regardless of per-host markers - New ADCS techniques - Included ADCS ESC5, ESC8, and ESC14 in the scoreboard label mapping for complete coverage of known ADCS escalation paths **Changed:** - Scoreboard extraction logic - Replaced hardcoded "child_to_parent" technique with call to `addUniversalTechniques` to generalize handling of lab-wide vulnerabilities - Config updates - Added "adcs_esc8" to the list of applicable vulns in specific lab host configurations to reflect new technique coverage - Test coverage - Extended expected answer keys in tests to include "adcs_esc8", noPac, PrintNightmare, ZeroLogon, Certifried, machine account quota, mitm6, and other universal techniques to match new logic **Removed:** - Direct addition of "child_to_parent" technique in extraction function, replaced by new universal handling logic
1 parent c6b2d4e commit 995c182

2 files changed

Lines changed: 42 additions & 2 deletions

File tree

‎cli/internal/scoreboard/generate.go‎

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,13 +315,16 @@ var adcsLabels = map[string]string{
315315
"adcs_esc2": "ADCS ESC2",
316316
"adcs_esc3": "ADCS ESC3",
317317
"adcs_esc4": "ADCS ESC4",
318+
"adcs_esc5": "ADCS ESC5",
318319
"adcs_esc6": "ADCS ESC6",
319320
"adcs_esc7": "ADCS ESC7",
321+
"adcs_esc8": "ADCS ESC8",
320322
"adcs_esc9": "ADCS ESC9",
321323
"adcs_esc10_case1": "ADCS ESC10 (Case 1)",
322324
"adcs_esc10_case2": "ADCS ESC10 (Case 2)",
323325
"adcs_esc11": "ADCS ESC11",
324326
"adcs_esc13": "ADCS ESC13",
327+
"adcs_esc14": "ADCS ESC14",
325328
"adcs_esc15": "ADCS ESC15",
326329
}
327330

@@ -355,7 +358,8 @@ func extractTechniques(lab map[string]any, asrep map[string][]string) []Objectiv
355358
addKerberosTechniques(domains, asrep, add)
356359
addHostTechniques(hosts, add)
357360
addDomainTechniques(domains, add)
358-
add("child_to_parent", "Child-to-Parent Domain Escalation", "domain_trust")
361+
addADCSWebEnrollmentTechnique(domains, add)
362+
addUniversalTechniques(add)
359363

360364
keys := make([]string, 0, len(techniques))
361365
for k := range techniques {
@@ -496,6 +500,40 @@ func addPrivescTechniques(h map[string]any, add techniqueAdd) {
496500
}
497501
}
498502

503+
// addADCSWebEnrollmentTechnique credits ESC8 when any domain has Web Enrollment
504+
// installed. ESC8 isn't a per-host vulns marker (Ansible would try to dispatch
505+
// a non-existent vulns_adcs_esc8 role) — it's gated by the domain-level
506+
// ca_web_enrollment flag, which defaults to true. Mirrors validate/checks.go's
507+
// CAWebEnrollment() logic.
508+
func addADCSWebEnrollmentTechnique(domains map[string]any, add techniqueAdd) {
509+
for _, dRaw := range domains {
510+
d, _ := dRaw.(map[string]any)
511+
if v, ok := d["ca_web_enrollment"].(bool); ok && !v {
512+
continue
513+
}
514+
add("adcs_esc8", "ADCS ESC8", "adcs")
515+
return
516+
}
517+
}
518+
519+
// addUniversalTechniques credits techniques that are exploitable against any
520+
// default-configured GOAD lab: cross-domain escalation, unpatched DC CVEs,
521+
// ADCS-adjacent abuses (Certifried), IPv6 poisoning, and MAQ=10 chains. These
522+
// don't have per-host markers — the lab's defaults (unpatched Server roles,
523+
// MAQ=10, Print Spooler on, ca_web_enrollment=true) make them universally
524+
// applicable. Documented in docs/GOAD-vulnerabilities-comprehensive.md.
525+
func addUniversalTechniques(add techniqueAdd) {
526+
add("child_to_parent", "Child-to-Parent Domain Escalation", "domain_trust")
527+
add("nopac", "noPac (CVE-2021-42287/42278)", "cve")
528+
add("printnightmare", "PrintNightmare (CVE-2021-1675)", "cve")
529+
add("zerologon", "ZeroLogon (CVE-2020-1472)", "cve")
530+
add("cve_2019_1040", "CVE-2019-1040 (Remove-MIC NTLM Bypass)", "cve")
531+
add("certifried", "Certifried (CVE-2022-26923)", "adcs")
532+
add("krbrelayup", "KrbRelayUp (RBCD self-relay)", "privilege_escalation")
533+
add("machine_account_quota", "Machine Account Quota Abuse (MAQ=10)", "privilege_escalation")
534+
add("mitm6", "MITM6 IPv6/DHCPv6 Poisoning", "network")
535+
}
536+
499537
func addDomainTechniques(domains map[string]any, add techniqueAdd) {
500538
addIfAnyDomainHas(domains, "acls", add, "acl_abuse", "ACL Abuse Chain", "acl_abuse")
501539
addIfAnyDomainHas(domains, "trust", add, "cross_forest_trust", "Cross-Forest Trust Exploitation", "domain_trust")

‎cli/internal/scoreboard/verify_test.go‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ func TestAnswerKeyHasAllExpectedTechniques(t *testing.T) {
114114
want := []string{
115115
"asrep_roast", "kerberoast",
116116
"adcs_esc1", "adcs_esc2", "adcs_esc3", "adcs_esc4", "adcs_esc6",
117-
"adcs_esc7", "adcs_esc9", "adcs_esc11", "adcs_esc13", "adcs_esc15",
117+
"adcs_esc7", "adcs_esc8", "adcs_esc9", "adcs_esc11", "adcs_esc13", "adcs_esc15",
118118
"adcs_esc10_case1", "adcs_esc10_case2",
119119
"golden_ticket-essos.local",
120120
"golden_ticket-north.sevenkingdoms.local",
@@ -126,6 +126,8 @@ func TestAnswerKeyHasAllExpectedTechniques(t *testing.T) {
126126
"acl_abuse", "cross_forest_trust", "child_to_parent",
127127
"constrained_delegation", "unconstrained_delegation",
128128
"seimpersonate",
129+
"nopac", "printnightmare", "zerologon", "cve_2019_1040",
130+
"certifried", "krbrelayup", "machine_account_quota", "mitm6",
129131
}
130132
for _, w := range want {
131133
if !techIDs[w] {

0 commit comments

Comments
 (0)