Skip to content

feat: gate ADCS ESC6/ESC9 on same-domain KDC certificate binding - #30

Merged
l50 merged 2 commits into
mainfrom
fix/esc9-esc10-kdc-gating
Jul 30, 2026
Merged

feat: gate ADCS ESC6/ESC9 on same-domain KDC certificate binding#30
l50 merged 2 commits into
mainfrom
fix/esc9-esc10-kdc-gating

Conversation

@l50

@l50 l50 commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Key Changes:

  • Made ESC6 and ESC9 exploitability depend on the KDC's StrongCertificateBindingEnforcement value in the certificate's own domain, closing a gap where CA-side and template-side probes reported green while a patched KDC silently killed the attack routes (KB5014754 moved the default to Full Enforcement in Feb 2025)
  • Added the adcs_esc10_case1 KDC binding pin to the DC that owns the CA and vulnerable templates in GOAD and GOAD-variant-1, and made the role restart the KDC so the new binding mode actually takes effect
  • Introduced per-domain KDC binding validation that fails ESC6 (requires value 0) and ESC9 (requires value ≤1) when the KDC is missing, unset, or enforcing, rather than passing on CA/template flags alone
  • Reworked the scoreboard so KDC-bound ADCS techniques are only credited when a weak binding is pinned in the same domain as the CA

Added:

  • KDC restart step in the adcs_esc10_case1 role so the new binding mode takes effect immediately, gated on a changed registry write - ansible/roles/vulns_adcs_esc10_case1/tasks/main.yml
  • readKDCBinding helper and kdcBinding struct that resolve the validating DC for a certificate's domain and read its enforcement value, shared by ESC6 and ESC9 - cli/internal/validate/checks.go
  • ESC9 enforcement checks (checkESC9Enforcement, checkESC9KDCBinding) that verify both the template drops the SID security extension and the KDC still accepts a weak mapping - cli/internal/validate/checks.go
  • Same-domain KDC gating in the scoreboard via weakCertBindingDomains and kdcBoundADCSTechniques, so ESC6/ESC9 are only credited when the pin lives in the CA's domain - cli/internal/scoreboard/generate.go
  • Comprehensive ESC9 KDC binding test suite covering enforcement thresholds, absent values, missing templates, and same-domain DC resolution - cli/internal/validate/esc9_kdc_test.go
  • Scoreboard tests pinning KDC-bound technique gating, including the GOAD shape where a permissive KDC in another forest must not credit the route - cli/internal/scoreboard/topology_gating_test.go
  • New "KDC binding is a hard gate" documentation section with an enforcement-mode table and notes on the moved default and event 39 log level - docs/GOAD-vulnerabilities-comprehensive.md

Changed:

  • ESC6 validation now treats an absent StrongCertificateBindingEnforcement value as Full Enforcement (FAIL) rather than unknown (WARN), reflecting the KB5014754 default, and points operators to the adcs_esc10_case1 vuln to pin it - cli/internal/validate/checks.go
  • Enabled adcs_esc10_case1 in the CA/template domain for both GOAD and GOAD-variant-1 - ad/GOAD/data/config.json, ad/GOAD-variant-1/data/config.json
  • Split the monolithic topology gating test into focused functions per concern to stay under the gocyclo threshold, and renamed ESC6 test helpers (esc6Lab/esc6Envelope to kdcBindingLab/kdcEnvelope) for reuse by ESC9 - cli/internal/scoreboard/topology_gating_test.go, cli/internal/validate/esc6_kdc_test.go
  • Documented the ESC6/ESC9 KDC dependency across compromise-path and domain docs, including corrected ESC9 prerequisites (value 0 or 1) and the meereen pin note - docs/domain-compromise-paths.md, docs/domains-and-users.md, docs/GOAD-vulnerabilities-comprehensive.md, ansible/roles/vulns_adcs_esc10_case1/README.md

**Added:**

- Added `weakCertBindingDomains` and `kdcBoundADCSTechniques` in scoreboard generation to withhold credit for ESC6/ESC9 unless the certificate's own domain pins `StrongCertificateBindingEnforcement=0` via `adcs_esc10_case1` - cli/internal/scoreboard/generate.go
- Added `checkESC9Enforcement` and `checkESC9KDCBinding` validation, gating ESC9 on both the template's `CT_FLAG_NO_SECURITY_EXTENSION` flag and a KDC binding of ≤1, with the template checked first so labs shipping no ESC9 template report INFO - cli/internal/validate/checks.go
- Added `esc9_kdc_test.go` covering ESC9 KDC thresholds, template gating, missing flag, and same-domain DC resolution - cli/internal/validate/esc9_kdc_test.go
- Added scoreboard topology tests asserting ESC6/ESC9 are credited only when the pin lives in the CA's own domain - cli/internal/scoreboard/topology_gating_test.go
- Added a KDC restart to the esc10_case1 role so the new binding mode takes effect, since the running KDC otherwise stays on its old mode - ansible/roles/vulns_adcs_esc10_case1/tasks/main.yml and README.md
- Added `adcs_esc10_case1` to the essos DC vuln lists so both labs pin the weak binding in the CA domain - ad/GOAD/data/config.json and ad/GOAD-variant-1/data/config.json
- Documented KDC binding as a hard gate, including the KB5014754 default shift, the ESC6-vs-ESC9 threshold table, and event 39 level semantics - docs/GOAD-vulnerabilities-comprehensive.md, docs/domain-compromise-paths.md, docs/domains-and-users.md

**Changed:**

- Refactored ESC6's inline KDC read into a shared `readKDCBinding` helper returning a `kdcBinding` struct, and reused it for ESC9 - cli/internal/validate/checks.go
- Changed the absent-value verdict from WARN to FAIL for both ESC6 and ESC9, since the shipped default is now Full Enforcement rather than unknown; the message advises pinning with `adcs_esc10_case1` - cli/internal/validate/checks.go
- Renamed the ESC6 test helpers (`esc6Envelope`/`esc6Lab` → `kdcEnvelope`/`kdcBindingLab`) to reflect their shared use and updated the absent-value expectations to FAIL - cli/internal/validate/esc6_kdc_test.go
**Changed:**

- Topology gating tests - Split the monolithic `TestTopologyGatedTechniques` into five focused functions (NoADCS, NoChildDomain, NHAWebEnrollment, HasADCS, VariantMatchesGOAD, KDCBound) to keep each under the repo's gocyclo threshold, updating subtest names to drop redundant prefixes - `cli/internal/scoreboard/topology_gating_test.go`
- Documented that the KDC restart step is now conditional - `ansible/roles/vulns_adcs_esc10_case1/README.md`
@l50 l50 changed the title fix: pin KDC certificate binding so ESC6 and ESC9 stay exploitable feat: gate ADCS ESC6/ESC9 on same-domain KDC certificate binding Jul 30, 2026
@l50
l50 merged commit 3a65819 into main Jul 30, 2026
9 checks passed
@l50
l50 deleted the fix/esc9-esc10-kdc-gating branch July 30, 2026 18:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant