Skip to content
S scriptkittens

altered-state

you broke the domain. altered-state doesn't care: snapshot, diff, fix, done. reset...reconfigure...replay!

Personal site Built on Astro Rocket Astro 6 Lighthouse 4×100

you broke the domain. altered-state doesn’t care: snapshot, diff, fix, done. reset…reconfigure…replay!
logo

functional, opinionated, and not done. use accordingly.

why

because some things are better when learned together. i wanted one machine that could run a bunch of different ADCS scenarios…just not all at once. needed a way to:

  • save the state of each one
  • switch between them cleanly

how

connects to your DC over LDAP (Kerberos/GSSAPI), snapshots the full directory to a compressed binary, and tracks it by name. when you’re ready to switch scenarios or reset, it diffs current AD state against the target snapshot and generates the PowerShell to close the gap.

pics or it didn’t happen

default
certipy find -dc-ip 192.168.1.1 -u a.broderick@example.com -dc-host dc01.example.com -p 'Password' -stdout -enabled -vulnerable
Certipy v5.0.4 - by Oliver Lyak (ly4k)

[*] Finding certificate templates
[*] Found 33 certificate templates
[*] Finding certificate authorities
[*] Found 1 certificate authority
[*] Found 11 enabled certificate templates
[*] Finding issuance policies
[*] Found 33 issuance policies
[*] Found 0 OIDs linked to templates
[*] Retrieving CA configuration for 'example-DC01-CA' via RRP
[*] Successfully retrieved CA configuration for 'example-DC01-CA'
[*] Checking web enrollment for CA 'example-DC01-CA' @ 'DC01.example.com'
[!] Error checking web enrollment: [Errno 111] Connection refused
[!] Use -debug to print a stacktrace
[!] Error checking web enrollment: [Errno 111] Connection refused
[!] Use -debug to print a stacktrace
[*] Enumeration output:
Certificate Authorities
  0
    CA Name                             : example-DC01-CA
    DNS Name                            : DC01.example.com
    Certificate Subject                 : CN=example-DC01-CA, DC=example, DC=com
    Certificate Serial Number           : 283955EB8CFA60964D4F3763C9A7ED01
    Certificate Validity Start          : 2026-05-22 19:17:20+00:00
    Certificate Validity End            : 2031-05-22 19:27:19+00:00
    Web Enrollment
      HTTP
        Enabled                         : False
      HTTPS
        Enabled                         : False
    User Specified SAN                  : Disabled
    Request Disposition                 : Issue
    Enforce Encryption for Requests     : Enabled
    Active Policy                       : CertificateAuthority_MicrosoftDefault.Policy
    Permissions
      Owner                             : example.com\Administrators
      Access Rights
        ManageCa                        : example.com\Administrators
                                          example.com\Domain Admins
                                          example.com\Enterprise Admins
        ManageCertificates              : example.com\Administrators
                                          example.com\Domain Admins
                                          example.com\Enterprise Admins
        Enroll                          : example.com\Authenticated Users
Certificate Templates                   : [!] Could not find any certificate templates
esc6
certipy find -dc-ip 192.168.1.1 -u a.broderick@example.com -dc-host dc01.example.com -p 'Password'
[*] Enumeration output:
Certificate Authorities
  0
    CA Name                             : example-DC01-CA
    DNS Name                            : DC01.example.com
    Certificate Subject                 : CN=example-DC01-CA, DC=example, DC=com
    Certificate Serial Number           : 283955EB8CFA60964D4F3763C9A7ED01
    Certificate Validity Start          : 2026-05-22 19:17:20+00:00
    Certificate Validity End            : 2031-05-22 19:27:19+00:00
    Web Enrollment
      HTTP
        Enabled                         : False
      HTTPS
        Enabled                         : False
    User Specified SAN                  : Enabled   <-- CHECK IT OUT
    Request Disposition                 : Issue
    Enforce Encryption for Requests     : Enabled
    Active Policy                       : CertificateAuthority_MicrosoftDefault.Policy
    Permissions
      Owner                             : example.com\Administrators
      Access Rights
        ManageCa                        : example.com\Administrators
                                          example.com\Domain Admins
                                          example.com\Enterprise Admins
        ManageCertificates              : example.com\Administrators
                                          example.com\Domain Admins
                                          example.com\Enterprise Admins
        Enroll                          : example.com\Authenticated Users
    [!] Vulnerabilities
      ESC6                              : Enrollee can specify SAN.
    [*] Remarks
      ESC6                              : Other prerequisites may be required for this to be exploitable. See the wiki for more details.

configuration

copy config.example.json to config.json in the same directory as the binary:

{
    "domain": "example.com",
    "hostname": "dc01.example.com",
    "never_touch_these_attributes": [  // not kidding, you will screw things up
    "certificatetemplates",
    "cn",
    "objectcategory"
]
}
fielddescription
domainAD domain FQDN
hostnamedomain controller FQDN used for LDAP bind
never_touch_these_attributesexcluded from comparison and remediation. leave these alone.

build

cargo build --release

build script copies wwwroot/ and config.json next to the binary automatically.

commands

init —> start here

snapshot current AD state as the default scenario baseline. run this on a clean lab before you touch anything.

altered-state.exe init [--overwrite] [--template <path>]

new —> name a scenario

captures a new baseline snapshot under a custom name. optionally init from a template (copies hooks, image, exclusions).

altered-state.exe new --name <name> [--description <desc>] [--template <path>] [--overwrite]

delete -> deletes a scenario

it’s okay, you can delete things too. if it’s the current, active scenario it will activate the pre-defined default before deleting

altered-state.exe delete --name [--force]

snapshot -> take a picture, it lasts longer

you made a few changes, you want to save them and see how it goes, use this

altered-state.exe snapshot --scenario <scenario> --description <desc>

list -> pretty prints all the scenarios & snapshots

see all the hard work you’ve done, with or without the details

altered-state.exe list [--detailed]

activate —> apply a scenario

diffs current AD state against the scenario snapshot and executes whatever PowerShell is needed to get there.

altered-state.exe activate --scenario <name> [--state baseline|current|snapshot-#]

update -> need to change something?

right now you can change the description of the scenario and designate which file should be used for play

altered-state.exe update --name <name> [--description <desc>] [--set-playable baseline|current|snapshot-#]

reset —> something broke. run this

restores the active scenario to its saved playable_state setting.

altered-state.exe reset --name <name>

serve —> web ui for players

spins up a minimal web interface so players can switch and reset scenarios without touching the CLI. they get activate and reset. nothing else.

altered-state.exe serve [--port 5000]

global flags

--config <path>    path to config.json (defaults to executable directory)
-v / -vv / -vvv   verbosity

scenario structure

each scenario lives under scenarios/<name>/:

scenarios/
  esc1/
    config.json       # scenario config: hooks, image, exclusions
    baseline.bin      # compressed LDAP snapshot
    snapshot-1.bin    # additional saved snapshot
    activation.ps1    # runs on activation
    cleanup.ps1       # runs on cleanup
    esc1.jpg          # optional scenario image

config.json

{
    "name": "esc1",
    "description": "ADCS ESC1 misconfiguration",
    "image_path": "...",
    "hooks": [
        {
            "hook_type": "Activation",
            "path": "C:\\path\\to\\activation.ps1",
            "arguments": [],
            "continue_on_error": true
        },
        {
            "hook_type": "Cleanup",
            "path": "C:\\path\\to\\cleanup.ps1",
            "arguments": [],
            "continue_on_error": true
        }
    ],
    "exclusions": [],
    "snapshots": [
        {
            "name": "snapshot-1",
            "description": "test1",
            "created_at": "2026-05-25 19:31:41.438812700 UTC",
            "file_path": "C:\\path\\to\\scenarios\\esc1\\snapshot-1.bin"
        }
    ],
    "playable_state": "snapshot-1"
}

hook types: Activation, Cleanup, PreAction

workflow (lab creators)

# 1. clean slate
altered-state.exe init

# 2. configure your scenario, then snapshot it, activates automatically
altered-state.exe new --name esc1 --template templates/esc1/config.json

# 3. back to baseline
altered-state.exe reset --name default

# 4. update a scenario to change the playable_state
altered-state.exe update --name esc1 --description "wow this is cool" --set-playable "snapshot-1"
Let's talk

Have a project in mind?

Whether it's a new build or something that needs a fresh perspective — I'd love to hear about it.