From a standard account to a Golden Ticket: attacking Active Directory in a homelab
To see what my production fixes actually stop, I attacked a deliberately vulnerable Active Directory in my homelab, from a standard user account to full control of the domain.
In short
I had hardened a production directory without ever seeing the attacks each fix was meant to counter.
I built a vulnerable domain in my homelab and ran eight steps, from reconnaissance to a Golden Ticket, recording for each its trace and its countermeasure.
Full control of the domain from a standard account. Defender blocked Pass-the-Hash via psexec, and the Golden Ticket only went through after three rejections by the controller.
Context
In production, I audited and hardened the Active Directory of a small business, without ever having seen the attacks those fixes block. This project fills that gap, from the attacker's side.
Everything happens in my homelab, a three-node Proxmox cluster: nothing touches the company, and these techniques were only used on this lab, which I own. The scenario starts from minimal access, a standard user's password, as after a successful phishing attempt. For each step, three questions: what the attack exploits, the trace it leaves, and the measure that would have stopped it.
What I did
- Build a reproducible labA Windows Server 2022 controller and a Kali workstation with static addresses. A PowerShell script injects weaknesses common in companies: weak passwords, service accounts with SPNs, an administrator with a guessable password, a relaxed policy. Snapshot of the controller before the first attack.
- Map with a standard accountNmap finds the controller through the ports it must expose (DNS, Kerberos, LDAP, SMB). BloodHound then reads the whole directory and shows the paths to Domain Admins, without any privilege.
- Crack a service account offlineAny user can request a service account's ticket and look for its password offline: that is Kerberoasting. Two accounts resist the rockyou wordlist without rules, which does not make them strong; a third one, added with a common password, falls in under a second.
- Get a domain adminA dictionary attack on the single account BloodHound pointed to, not password spraying, which tries one password across many accounts. The right password is 11 characters long and meets Windows complexity rules: common passwords must be refused too.
- Take the domainWith that account, DCSync replicates every account's hashes, including
krbtgt. Defender blocks Pass-the-Hash attempted with psexec; a Golden Ticket forged withkrbtgt's AES256 key, like legitimate tickets, opens every share on the controller without a password.
See technical details
The lab. Two VMs on an internal network without DHCP: the DC-LAB controller (4 vCPUs, 4 GB of RAM), a new LAB.AD.local forest, DNS pointing to itself; the Kali workstation (2 vCPUs, 4 GB), DNS pointing to the controller, because Kerberos needs correct name resolution. Windows Firewall disabled on the controller, Microsoft Defender enabled.
| Weakness introduced | What it enables |
|---|---|
| Users with common passwords | The attacker's starting account |
| Two service accounts with SPNs | Kerberoasting |
| Unconstrained delegation on a service account | Impersonating anyone who authenticates to it |
| A Domain Admins member with a guessable password | Taking control of the domain |
| Policy: 4 characters, no complexity, no expiry | Making all of the above possible |

Mapping. The BloodHound collector records 9 users and 52 groups, with their rights and delegations. The Community Edition would not start properly on Kali; version 4, with a single graph database, was enough: what I needed was the map, not the latest interface. PingCastle, run in parallel, correctly rates the injected weaknesses as high risk.


Dead ends and findings. AS-REP roasting needs an account without Kerberos pre-authentication: it was enabled on the target account, so I dropped that path. In the hashes replicated by DCSync, the LM hashes are empty: the old algorithm is not stored.

An alert nobody sees. Defender's block stays local to the controller. On a server with no open session, the alert goes unnoticed: that is the job of central collection, like my Wazuh SOC.
An empty ticket file, with no error. The extraction tool's write option produced an empty file while the tickets were shown on screen. I checked the file's actual content, then filtered standard output myself: easier to verify.
| Attack | Trace and detection | Countermeasure |
|---|---|---|
| Reconnaissance (Nmap, BloodHound) | Unusual volume of LDAP queries from one workstation, visible only if logs are centralised | You cannot stop an authenticated account from reading the directory: you detect it |
| Kerberoasting | Burst of service ticket requests in RC4 (event 4769 on the controller) | Random service passwords of 25+ characters, or group managed service accounts (gMSA) |
| Dictionary attack on one account | Repeated authentication failures on the same account | Lockout threshold, long and uncommon passwords for every admin account, MFA on sensitive accounts |
| DCSync | Replication request from a machine that is not a controller (event 4662) | Limit the accounts that hold replication rights |
| Pass-the-Hash | Defender alert, observed here, but local to the server | Protected Users group for sensitive accounts, up-to-date antivirus or EDR, central collection |
| Golden Ticket | No file or service, but Kerberos anomalies that can be spotted if the controller's logs are collected | Protect the accounts with replication rights (the key comes from DCSync); double krbtgt rotation to invalidate tickets already forged; an up-to-date Windows Server already rejects non-existent accounts |
The incident
The Golden Ticket rejected three times
The controller rejected the forged ticket, even though the krbtgt key was correct.
Three causes removed one by one: a clock skew of several hours, when Kerberos tolerates five minutes; a ticket forged before the time was corrected; an invented account, which an up-to-date Windows Server 2022 rejects. Forged again for the real “Administrator” account, the ticket was accepted.
Results
The ticket gives access to every share on the controller, system drive included, and stays valid for years. Changing every password does not invalidate it; only a double krbtgt rotation does.
That is exactly what I had applied to the production directory: double krbtgt rotation, LM and NTLMv1 refused, 11 admin accounts flagged “sensitive and cannot be delegated”, which protects them from unconstrained delegation, and the Protected Users group tested. This lab showed me concretely what each of these measures counters, and its limits: the double rotation invalidates tickets already forged, it does not prevent forging a new one if the key leaks again. Recent patches also narrow the attacker's margin: the same attack, which used to work with a made-up account, now requires an existing one.

What I would improve
- Exploit the unconstrained delegation set up on the service account: one more realistic step, and the effect of the production “sensitive account” flag made visible.
- Collect the Windows-side traces (events 4769 and 4662, once auditing is enabled) and send them to the SIEM, to check that each step raises an alert.
- Isolate the lab in a dedicated VLAN with no route to the rest of the homelab: the first scan saw five hosts for two lab machines.