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.
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.
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:
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.
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.
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).
Grepping the decompiled source lights up immediately. There's a hardcoded username, a base64 "encrypted" password, and a getPassword() that "decrypts" it:
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:
So we have support\ldap and its cleartext password — a low-privilege account, but enough to read the directory.
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:
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.
The info value is a valid credential, and Remote Management Users means it works over WinRM:
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:
Write the RBCD attribute on DC$ so it trusts ATTACK$ to act on behalf of others:
Now S4U2Self+S4U2Proxy as ATTACK$ to mint a cifs/dc.support.htb service ticket as the Administrator:
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:
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).| Stage | Root cause |
|---|---|
| → support-tools | Anonymous/guest SMB read on a share holding in-house tooling (CWE-284 — improper access control) |
| → ldap creds | Hardcoded 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 Admin | A support group holds GenericAll over the DC object → write msDS-AllowedToActOnBehalfOfOtherIdentity → RBCD (CWE-266 — over-privileged ACL) |
ilspycmd replaces dnSpy for .NET decompilation, and the entire RBCD chain (addcomputer.py → rbcd.py → getST.py → wmiexec.py) reproduces the PowerView/Powermad/Rubeus workflow in pure-Python impacket — which also sidesteps the CredSSP dance a non-domain-joined Windows box needs for WinRM.faketime wrapper was needed here; if it hadn't, bloodyAD auto-corrects skew and NTLM (-H) sidesteps Kerberos entirely.DOTNET_ROOT on Homebrew. Point it at .../Cellar/dotnet/<ver>/libexec, not bin, or ilspycmd reports the runtime as "not found".