~ / foobarto.me / htb-machines
--:--:-- UTC
~ / htb-machines / active.md

active

Windows · Easy · released 2018-07-28 · retired 2019-02-09

Summary

This writeup is reconstructed from public walkthroughs (see Source attribution below). I have not personally rooted this box. The narrative below stitches together what those sources describe and explains, where it helps a learner, why the underlying primitives behave the way they do.

Treat the prose as a guided study tour rather than a fresh capture log. The chain is correct because every step has been validated by multiple independent reports, but the wording is mine and the practical screenshots and exact prompts are best read in the originals.

I have intentionally redacted machine-specific identifiers — IP addresses become <TARGET> and <DC>, passwords become <password> — because the educational value of the box is the methodology, not the lab artifacts. I have also avoided reproducing the flag values, since neither flag teaches anything that the chain itself does not.

Active is one of the cleanest pedagogical Windows boxes Hack The Box has ever shipped. It is a fully fledged Active Directory domain controller exposed to attackers with a handful of network ports and a single configuration mistake from the SYSVOL era.

The two attack primitives showcased — Group Policy Preferences (GPP) cpassword recovery, and Kerberoasting a privileged user account that has a Service Principal Name registered against it — are the bread-and-butter of internal AD pentesting. Anyone going into a real engagement needs both reflexes warmed up, and Active is the canonical lab environment for getting them there.

Despite the Easy rating, the techniques teach more than most Medium boxes: they are exactly what you will rehearse before walking onto a real internal network. The attack surface is intentionally narrow and the lessons are intentionally deep.

The chain runs end-to-end with public tooling: nmap and smbclient for recon, an unauthenticated read of the Replication share to grab Groups.xml, the Kali-distributed gpp-decrypt to recover a service account password from a 2014-era cpassword blob, then impacket’s GetUserSPNs.py to harvest a TGS hash for the Administrator account, hashcat mode 13100 with rockyou to crack the cleartext, and finally psexec.py or secretsdump.py to read both flags.

Nothing exotic, nothing custom-written — just a rehearsal of the attacks pentesters use against real domains. The lessons are about Microsoft’s design history and operational hygiene, not about a clever exploit.

Source attribution

I followed 0xdf’s writeup as the primary reference and cross-checked details against several reputable secondary sources. The dates, file paths, and structural details below come from that combination. Where two sources disagreed (mostly on minor formatting of commands), I deferred to 0xdf for the lab-specific details and to HackTricks/AD Security for the underlying mechanics.

Recon and AD enumeration

The first thing every public writeup highlights is that an nmap scan against <TARGET> lights up like a Christmas tree of Active Directory services. You see DNS on 53, Kerberos on 88, RPC endpoint mapper on 135, NetBIOS on 139, LDAP on 389, SMB on 445, kpasswd on 464, RPC over HTTP on 593, LDAPS on 636, the global catalog on 3268 and 3269, and a swarm of dynamic RPC ports starting around 49152.

That fingerprint is unmistakable: this is a Windows domain controller. The TLS certificates served on LDAPS and the global catalog ports name the DC, and the SMB service banner advertises the domain. From those scraps the writeups establish the domain name active.htb and confirm Windows Server 2008 R2 as the host OS.

The 2008 R2 detail matters because it tells you the box pre-dates many of the modern hardening defaults — no SMB signing required by default, RC4 still allowed on Kerberos, no Protected Users group enforcement, no Credential Guard. In other words, the host is exactly the kind of legacy DC you should expect to find in an aged enterprise.

Concept-first, the recon phase is doing two jobs at once. It is mapping the services, but it is also identifying the role.

Once you know the host is a DC, the entire enumeration plan changes — you stop thinking about web vulnerabilities or generic Windows services and start running the AD-specific checklist. That checklist begins with anonymous SMB.

On a misconfigured or legacy DC, the null session can still list shares and sometimes read them; even when it cannot, knowing what shares exist is half the battle. The presence of LDAP on 389 plus Kerberos on 88 plus SMB on 445 is the canonical “this is a DC” tell that should immediately prime your AD reflexes.

Concretely, the public walkthroughs run something close to:

nmap -p- -sV -sC -oA active <TARGET>
smbclient -N -L //<TARGET>/

The -N tells smbclient to do a null/anonymous bind. The output enumerates the standard shares — ADMIN$, C$, IPC$, NETLOGON, SYSVOL — along with one that should not be there: Replication. That share is the foothold.

Several writeups also note that rpcclient and ldapsearch with anonymous binds give very little on this box, so the SMB null session is the only easy door. Running smbmap -H <TARGET> as an unauthenticated user is the faster way to see, at a glance, the read/write permissions of each share.

On Active that single command tells you Replication is READ ONLY to everyone and that nothing else is anonymously accessible. CrackMapExec’s cme smb <TARGET> --shares -u '' -p '' does the same job in one line and is the more modern habit.

A second reflex worth practicing here is looking for usernames before you have credentials. Tools like enum4linux, rpcclient -U "" -N <TARGET> issuing enumdomusers, and lookupsid.py from impacket sometimes surface usernames you can then spray or kerberoast pre-authentication via AS-REP roasting.

On Active those queries are largely closed, which is fine — the SMB share alone is enough. But the habit of fanning out across every available enumeration channel before committing to one path is a discipline this box quietly rewards.

DNS is sometimes useful here too: a quick dig or nslookup of common AD-internal names against <TARGET> can reveal additional hosts in larger environments. Active is a single-host lab, so it does not pay off, but on a real engagement the DC’s DNS service is one of the richest sources of internal topology a low-privileged attacker can query.

LDAP anonymous binds are the other low-cost probe to try: ldapsearch -x -H ldap://<TARGET> -s base namingcontexts returns the naming context structure on most DCs without authentication, and ldapsearch -x -H ldap://<TARGET> -b 'DC=active,DC=htb' will return either domain object metadata or a clean access-denied. Either outcome is useful: the former gives you a free map of the directory, the latter confirms anonymous LDAP is locked down and pushes you toward the SMB path you were already going to take.

Foothold — Anonymous SMB read of Groups.xml + GPP cpassword

The Replication share is the gift. It is readable to an anonymous user, and what is sitting inside is a verbatim copy of a portion of SYSVOL: the domain Policies tree.

SYSVOL is the on-disk store every domain controller replicates so that Group Policy Objects can be applied uniformly across the forest. Within a GPO’s directory you find a deterministic structure of MACHINE and USER subtrees, and inside those, one or more Preferences folders that hold the XML payloads that drive Group Policy Preferences (GPP).

It is, in effect, a publicly readable copy of the configuration policy database of the domain. That is enormous reach for an unauthenticated user.

The path the walkthroughs all pull is:

\\<TARGET>\Replication\active.htb\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Preferences\Groups\Groups.xml

The GUID {31B2F340-016D-11D2-945F-00C04FB984F9} is the well-known identifier for the Default Domain Policy. Inside Groups.xml is an entry that adds or modifies a local user — in this case active.htb\SVC_TGS — and crucially carries a cpassword="..." attribute.

That attribute holds the user’s password, AES-256 encrypted, base64-encoded. There is also a userName attribute and a few flags telling the GPP client what to do with this account at apply time.

Here is where the conceptual punchline lands. To make GPP convenient, Microsoft did encrypt the password before writing it to disk so that anyone with read access to SYSVOL would not just see plaintext.

But because Group Policy is meant to apply automatically on every workstation across the forest, the decryption key has to be available to every machine. Microsoft solved that by hard-coding a single static AES key into the GPP client, and then they published that key in MSDN documentation so that integrators and customers could understand the on-disk format.

That published key, combined with the fact that any authenticated domain user (and on Active, any anonymous SMB reader) can read SYSVOL, made cpassword a one-step credential disclosure for the lifetime of the GPP feature.

In 2014 Microsoft acknowledged the design as a security boundary failure and shipped MS14-025 (KB2962486), which removed the ability to author new GPP entries containing passwords.

What MS14-025 explicitly did not do is delete previously created XML files, because doing so could break running policies in environments that depended on them. As a result, every domain that ever used GPP for password push-down before 2014 still has these XML files lurking in SYSVOL — exactly the situation Active simulates.

Sean Metcalf’s adsecurity.org writeup is the canonical reference here and reads like a tour of the same XML schema you are about to decrypt.

To recover the plaintext, the public Kali tool gpp-decrypt takes the cpassword string and applies the disclosed key:

smbclient -N //<TARGET>/Replication
# get \active.htb\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Preferences\Groups\Groups.xml
gpp-decrypt '<cpassword string>'

The output yields SVC_TGS’s password. From that moment on the attacker has valid domain credentials, even if they belong to a low-privilege service account.

Equivalent paths cover the same primitive: Get-GPPPassword from PowerSploit walks SYSVOL recursively from inside a domain-joined PowerShell session and decrypts every cpassword it finds, crackmapexec smb <TARGET> -u '' -p '' --gpp-passwords does the same from the attacker’s box, and the metasploit module auxiliary/scanner/smb/smb_enum_gpp does it from a Meterpreter-adjacent workflow.

Pick whichever you have at hand; the underlying primitive is the same single AES-256 decrypt with a publicly known key. The reason there are so many tools is that the bug is so common that every framework wrote its own once.

That itself is a learning signal: when a primitive has a tool in PowerSploit and metasploit and impacket and CrackMapExec and the Kali base image, it is one you must know cold.

User flag — SVC_TGS

With active.htb\SVC_TGS:<password> in hand, the next move is to re-enumerate the SMB shares as that authenticated user. Tools of choice are smbmap for fast read/write fingerprinting and smbclient for actually retrieving files:

smbmap -u SVC_TGS -p '<password>' -d active.htb -H <TARGET>
smbclient //<TARGET>/Users -U 'active.htb/SVC_TGS%<password>'

The Users share — a wrapper around C:\Users — is suddenly readable. Inside, SVC_TGS\Desktop\user.txt holds the user flag.

The mechanical step is trivial; the conceptual point is that any domain account, no matter how nominally underprivileged, gets you access to a wide class of shares and to the LDAP read surface needed for the next attack.

The principle that “authenticated user = significant attack surface” is one of the most important takeaways from this box. In a real environment, a single low-tier domain credential typically unlocks a sea of internal documentation, user lists, group memberships, and computer inventories that would otherwise be hidden behind anonymous-bind hardening.

BloodHound is the tool that monetises that observation: with one set of valid credentials it builds a graph of every account, every group, every computer, and every privileged relationship in the forest, and it will name your shortest path to Domain Admin in seconds. On Active that path is short enough to find by hand, but the muscle memory transfers.

It is also worth pausing to note that SVC_TGS was, in real-world AD parlance, a typical low-tier service account. It probably ran some non-critical Kerberos-named service.

The fact that it was provisioned via GPP back in the bad old days, never rotated, and was therefore recoverable from a single XML file is the gift that made the rest of the chain possible. The naming is a wink at the next step: the box’s privilege escalation is itself a TGS attack.

Privilege escalation — Kerberoasting Administrator’s SPN

The privilege escalation step is the second educational vignette of the box, and it is what gives Active its lasting value. Once SVC_TGS is logged in, every public walkthrough enumerates Service Principal Names (SPNs) registered on user accounts in the domain.

An SPN is an LDAP attribute that maps a user (or computer) account to a Kerberos service identity (e.g. MSSQLSvc/sql01.corp.local:1433 or HTTP/web.corp.local). When SPNs are registered on user accounts, those accounts become Kerberoastable: any authenticated domain user can request a TGS for that service, and the TGS portion encrypted to the service account’s long-term key (derived from the account’s NT hash) can be carried offline and brute-forced against a wordlist.

The attack does not depend on the requester having any rights to the service itself; the KDC issues TGS responses to anyone with a valid TGT. That is the structural property that makes Kerberoasting universal across AD environments.

On Active the kerberoastable account is, surprisingly, the domain Administrator. Whoever built the lab attached an SPN directly to the Administrator account — something that should never happen in production, because it makes the most powerful account in the domain trivially attackable by any user with a TGT.

The single command commonly used is:

impacket-GetUserSPNs active.htb/SVC_TGS:'<password>' -dc-ip <DC> -request

GetUserSPNs.py is the canonical implementation. Internally it does the LDAP search for (&(samAccountType=805306368)(servicePrincipalName=*)) — that filter restricts to user objects with a non-empty SPN attribute — and for each result it asks the KDC for a TGS using the requesting user’s TGT.

The KDC is happy to issue these tickets to anyone who has authenticated, because Kerberos by design does not police which clients can ask for service tickets — that is what the service is supposed to do at decryption time.

The ticket comes back with one half encrypted to a session key (which the attacker has) and another half — the actual service ticket the client would normally hand to the service — encrypted to the service account’s password-derived key.

There are no logs on the DC distinguishing a legitimate TGS request from a roasting request, which is part of why this attack remains so popular. Event ID 4769 (Kerberos service ticket requested) fires for every TGS request whether benign or malicious; only the requested encryption type (RC4 vs AES) and the volume of distinct SPNs requested in a short interval give defenders a signal.

The output of -request is a hashcat-formatted hash beginning with $krb5tgs$23$* (the 23 indicating RC4-HMAC, which is the legacy encryption type and the one most commonly returned). Cracking is offline:

hashcat -m 13100 admin.tgs /usr/share/wordlists/rockyou.txt

Mode 13100 in hashcat is “Kerberos 5 TGS-REP etype 23.” With rockyou.txt, every public writeup reports a successful crack to a recognizable date-styled password (the kind of memorable phrase that human admins reach for and that no enforcement policy ever catches).

The reason this works is straightforward: the encryption strength of the TGS hash is exactly the strength of the password. The “key” used by the KDC for an RC4 ticket is just MD4(password), with no salt and no iteration.

A plain GPU run can chew through billions of guesses per second against a single hash, so anything human-memorable falls. AES-keyed tickets (etypes 17 and 18) are slower but still entirely brute-forceable against weak passwords, and impacket lets you ask for either by passing -supported-enctypes.

There is also a legitimate technique called “downgrade roasting” where the attacker explicitly asks for an RC4 ticket on a target whose account supports both AES and RC4; the KDC honours the request and the resulting hash is the cheap-to-crack 13100 form.

With the cleartext Administrator password, the last step is a plain authenticated lateral move. impacket gives you two interchangeable options:

impacket-psexec active.htb/Administrator:'<password>'@<TARGET>
impacket-secretsdump active.htb/Administrator:'<password>'@<TARGET>

psexec.py drops a SYSTEM shell on the DC by uploading a service binary to ADMIN$, registering it via the Service Control Manager over RPC, and starting it; secretsdump.py instead pulls the SAM, LSA secrets, and NTDS.dit hashes by reaching into the registry and DRSUAPI.

From either path you can read C:\Users\Administrator\Desktop\root.txt. Game over.

A quieter alternative is wmiexec.py, which gives a semi-interactive shell over WMI and avoids dropping a binary on disk — worth knowing for engagements where AV is a concern. Yet another option is to use Evil-WinRM if WinRM is exposed (it is not by default on a 2008 R2 DC, but on later builds it often is), or to mount \\<TARGET>\C$ directly with smbclient and read the flag without ever taking a shell.

On Active, all of these end at the same place because Administrator is fully privileged on the DC by definition.

Why each step worked

The GPP cpassword recovery is not a bug in the modern sense; it is a deprecated feature whose remnants live forever in SYSVOL. Microsoft chose, in the early Group Policy Preferences design, to make the password-push-down feature workable across millions of clients by sharing a single static AES-256 key, and then they published that key in their own documentation so that customers and integrators would understand the encryption format.

Every domain controller in the world had the same key. When researchers pointed out that any authenticated user could read SYSVOL and decrypt anything stored there, Microsoft shipped MS14-025 (KB2962486) to disable creating new GPP password entries.

They explicitly did not delete or rewrite existing files, because doing so could silently break legacy policies. The result is a long tail of domains that still hold cpassword XML files in SYSVOL years after the patch.

Active simulates that long tail by exposing the Replication share anonymously and seeding it with one such file. Every step from “anonymous SMB read” to “plaintext SVC_TGS password” is just patiently walking through the documented format.

The Kerberoast works because of a structural property of Kerberos. When the KDC issues a TGS, it must encrypt the ticket with the long-term key of the target service so that only that service can read the inside.

If the service is registered on a user account, the long-term key is RC4-HMAC(NT-hash(password)) (or AES-derived if the account requests AES). The attacker cannot decrypt the ticket online without the password — but they do not need to.

Because they hold the ticket bytes, they can guess passwords offline, derive the candidate key, and try to decrypt a known plaintext field in the ticket (a known PAC structure or a known timestamp); a successful decryption means the candidate password is correct.

There is no authentication failure logging on the DC, no lockout policy, no rate-limiting. The only defense is the password itself: if it is long and random enough, the offline space becomes infeasible.

Service accounts historically have weak passwords because they were typed in once at install time and never rotated, which is exactly the situation Active reproduces.

The final psexec/secretsdump step “works” in the trivial sense that you have Administrator credentials. The point is that there is no further escalation needed: an SPN registered against Administrator is already game-over, because cracking that SPN’s TGS hands you full domain compromise.

In a properly tiered environment, Tier-0 accounts (Domain Admins, Enterprise Admins, the built-in Administrator) should never carry SPNs and should be subject to the Protected Users group’s restrictions, including the prohibition on RC4 and the shorter TGT lifetime. The fact that none of those controls are in play on Active is part of the simulation — it is what an unhardened legacy domain looks like.

Counterfactuals

Several small operational hygiene choices would have broken this chain at every step. If the administrators of active.htb had applied KB2962486 and run the cleanup script Microsoft published alongside it (which deletes legacy Groups.xml entries containing cpassword), the SYSVOL read would have produced nothing useful and the foothold would not exist.

Many real organizations skip that cleanup step because they fear breaking running policies; the takeaway for defenders is that patching MS14-025 is necessary but not sufficient — you also have to scrub SYSVOL for already-written cpassword files.

Tools like Get-GPPPassword -Server <DC> from PowerSploit can be repurposed defensively to find what needs scrubbing, and Microsoft’s own Enum-SettingsWithCpassword.ps1 shipped alongside KB2962486 for the same purpose.

A separate hygiene control is rotating service account passwords whenever a cpassword artifact is found, even if the file is then deleted. The XML may be gone, but if an attacker has already exfiltrated and decrypted it, the credential is permanently burned and must be changed — ideally to a managed-account-style randomized password or, better, to a gMSA whose secret is never directly visible to a human.

The Kerberoast would have been blunted in three independent ways. First, Administrator should never have an SPN registered. The accidental or sloppy registration of a service against a privileged user account is the actual vulnerability; the SPN should have been moved to a dedicated low-privilege service principal.

The audit query is simply Get-ADUser -LDAPFilter "(servicePrincipalName=*)" -Properties servicePrincipalName filtered down to members of any privileged group — find any privileged user with an SPN, then either remove the SPN or move the service.

Second, even on the right account, a sufficiently long randomized password — Microsoft and most pentesters quote 25 characters or more of true random — pushes offline cracking out of feasible time and budget.

Third, Group Managed Service Accounts (gMSA) sidestep the problem entirely: the password is generated and rotated by AD itself with 240 bytes of entropy, and only authorized hosts can retrieve it. Any of those three controls, on its own, would have stopped the chain at privilege escalation.

A fourth, more recent option is to put high-value accounts into the Protected Users group, which disables RC4 pre-auth and enforces AES, raising the cracking cost meaningfully even if a weak password slips through.

A final smaller counterfactual concerns the anonymous SMB read. If Replication had not been exposed without authentication, the attacker would have needed valid credentials before they could even read Groups.xml.

In a world where SVC_TGS is the only mistake, that means the chain might not have started at all. Locking down null/anonymous SMB on domain controllers — the RestrictAnonymous/RestrictAnonymousSAM/RestrictNullSessAccess family of registry settings, and disabling the legacy Pre-Windows 2000 Compatible Access group — is a standard hardening control that would have raised the cost of the foothold significantly.

The combination of “anonymous SMB read of SYSVOL artifacts” and “GPP cpassword left in place” is what makes the box trivially solvable from zero credentials; remove either and the difficulty rises sharply.

Key Takeaways

Every Windows AD pentester checks SYSVOL for cpassword on day one of an internal engagement. The reflex is universal: as soon as you have any foothold on a domain-joined network — anonymous, authenticated, or via a dropped session — you list \\<DC>\SYSVOL\ and \\<DC>\NETLOGON\ and grep recursively for cpassword.

Tools like PowerSploit’s Get-GPPPassword, the metasploit smb_enum_gpp module, and CrackMapExec’s --gpp-passwords option automate the search. Active is the lab where you learn that reflex; the real-world payoff is that you will, on a non-trivial fraction of engagements against legacy environments, find at least one stale cpassword.

Kerberoasting is the bread-and-butter Active Directory privilege escalation technique. It needs only an authenticated domain account, makes no noise on the DC beyond a normal TGS request, and pays out whenever any service account password is weaker than ~16-20 characters of high-entropy random text.

The defensive stance you should walk away with is the inverse: every service account in your environment needs either a long randomized password or, better, a managed-account substitute (gMSA, dMSA, or its modern successors). And no privileged account should ever carry an SPN; the audit query for that is one LDAP filter away and worth running on every engagement and every defense-in-depth review.

impacket is the canonical toolkit for these primitives, and getting fluent with it pays off across many boxes. GetUserSPNs.py for kerberoasting, psexec.py and wmiexec.py and smbexec.py for lateral movement once you have credentials, secretsdump.py for pulling NTDS.dit and LSA secrets once you are local admin or DA, lookupsid.py and samrdump.py for early enumeration, GetNPUsers.py for AS-REP roasting, ticketer.py and goldenPac.py for golden/silver ticket forgery once you have keys.

Every one of those has a hashcat-mode-aware companion. Together with gpp-decrypt, they cover roughly the entire workflow that Active rehearses, and most of the rest of the introductory AD curriculum.

Finally, a note on study habits. Boxes like Active are best worked twice: once for the chain (get the flags, prove you can do it) and once for the why (re-read each step until you can explain to a colleague why the KDC hands out a roastable ticket, or why MS14-025 cannot retroactively delete cpassword files).

The second pass is where the durable learning sits. The exam-style writeups, including this one, exist for that second pass: skim them, then put them down and try to reconstruct the chain from memory, naming each primitive as you go.

If you can describe to someone else, in a single sentence each, what the GPP cpassword AES key is, why a TGS hash is offline-crackable, and why MS14-025 leaves cpassword files in place, you have the durable version of the lesson.

References

← all htb machines hackthebox.com ↗