Real targets. Real exploits. Real reports. This is what I can break and how I break it.
Real engagements. Real exploits. Real reports.
Black-box penetration test against OWASP Juice Shop, an intentionally vulnerable Node.js e-commerce application. Objective: gain unauthorized administrative access and demonstrate the real-world blast radius of every finding. The assessment identified 9 vulnerabilities across 7 OWASP Top 10 (2021) categories, including two Critical-severity issues enabling complete session takeover. Every finding is backed by a working exploit, a captured Burp Suite request/response, a root cause analysis, and a remediation recommendation. In a production environment, this attack chain would expose all customer data, enable arbitrary account takeover, and grant persistent administrative control with no detectable footprint.
Mapped the full attack surface before running a single exploit. Enumerated all API endpoints via Burp Suite passive crawl, discovered an unauthenticated /ftp/ directory exposing backup files, and extracted every REST route from main.js via static analysis. No automated scanner. All manual.
Findings did not exist in isolation. They chained. The SQLi bypass produced the admin JWT. The FTP exposure yielded the seed phrase and OAuth secret. IDOR gave access to every user basket. Together they formed a single kill chain from unauthenticated visitor to full application owner.
Every finding is backed by a working exploit, a captured Burp Suite request/response, a root cause analysis, and a remediation recommendation. 45 evidence screenshots documented across all phases. Full report on GitHub.
email: ' OR 1=1--
password: anything
→ Admin JWT extracted. Session hijacked.
Root cause: User input concatenated directly into SQL string with no parameterisation. Any raw query without a prepared statement is this bug.
Fix: Replace with parameterised queries or an ORM. Input validation is secondary: the query itself must never accept literal user input.
GET /ftp/package.json.bak HTTP/1.1
→ BIP-39 seed phrase exposed.
→ OAuth client_secret in plaintext.
Root cause: Backup files deployed to a publicly accessible directory with no access controls. A forgotten file is all it takes.
Fix: Remove /ftp/ from the public webroot. Enforce authentication on all non-public paths. Add backup file patterns to .gitignore and deployment exclusion lists.
GET /api/BasketItems/1 → user A cart
GET /api/BasketItems/2 → user B cart
→ Zero ownership validation.
Root cause: API returns objects by ID alone with no ownership check. The server trusts the client to only request its own data.
Fix: Verify req.user.id matches the resource owner on every object request server-side. Use UUIDs instead of sequential integers to raise the enumeration cost.
Attempt 1: admin123 → 401
Attempt 2: password → 401
Attempt 3: admin12345 → 200 ✓
→ No lockout. No rate limit.
Root cause: Login endpoint accepts unlimited attempts with no throttling or lockout. Password complexity is the only defence.
Fix: Implement account lockout after 5 failed attempts, IP-based rate limiting via express-rate-limit, and exponential backoff. Add CAPTCHA on repeated failures.
<iframe src="javascript:alert('XSS')">
→ Executes in victim's browser context.
→ Escalates to session cookie theft.
Root cause: User-controlled input reflected into HTML without output encoding. As a developer, I have written this exact pattern.
Fix: Encode all user input before rendering into the DOM. Use DOMPurify or framework-level encoding. Set a strict Content-Security-Policy to block inline script execution.
1. UI blocks non-image files → JS only
2. Intercept POST in Burp Suite
3. Rename payload.php → avatar.jpg
→ Server accepted. No server check.
Root cause: File type enforcement happens only in JavaScript. Any intercepting proxy bypasses it in seconds. The server performs zero validation.
Fix: Validate file type and MIME server-side using an extension allowlist. Scan file content, not just the header. Store uploads outside the webroot and serve through a controlled handler.
End-to-end forensic investigation of a compromised Linux web server across six evidence domains. A single attacker (198.51.100.47) conducted a targeted breach on 14 November 2025. Starting from DirBuster reconnaissance at 02:55 UTC, they achieved root in 36 minutes via SSH brute force, web shell upload, and privilege escalation, then installed a Cobalt Strike beacon disguised as a kernel process, created a backdoor user, loaded a rootkit to hide PID 31337, and exfiltrated credentials via DNS tunnelling. Every step was reconstructed from raw evidence: system logs, a RAM dump, a packet capture, disk images, and a custom ELF malware sample, producing a complete 12-event attack timeline mapped to 10 MITRE ATT&CK techniques.
Reconstructed initial access and persistence from apache, auth, syslog, and kern logs. Identified brute force source IP, web shell upload, privilege escalation via sudo, rootkit loading, and cron persistence.
Used Volatility3 to expose a Cobalt Strike beacon disguised as kworker-update (PID 31337). Recovered plaintext credentials from RAM and reconstructed attacker commands from bash history in memory.
Analysed a PCAP with tshark to identify the C2 domain, count 47 scanned ports, reconstruct the reverse shell session (TCP stream 190), and decode base64-encoded credentials exfiltrated inside DNS subdomains.
Mounted disk images read-only, used foremost to carve a PNG from raw sectors, and recovered deleted credentials and a staged exfiltration plan (OPERATION NIGHTFALL) from unallocated space using strings.
Static analysis only. Extracted C2 IP hardcoded in an ELF binary using strings, identified the beacon User-Agent, and fully reversed a two-stage VBA macro infection chain including sandbox evasion and encoded PowerShell payload.
Correlated all five evidence sources to reconstruct a 12-event canonical attack timeline from 02:55 to 03:55 UTC. Third flag (SHA-256 timeline hash) not captured: format mismatch despite 1.3M brute-force combinations. Documented in full.
SSH brute force from 198.51.100.47 succeeded after 83 attempts targeting the admin account. Web shell uploaded to /uploads/shell.php within 2 minutes of login. Both attack vectors confirmed via apache-access.log and auth.log cross-reference.
Three independent persistence methods deployed: backdoor user svc-backup with injected SSH key, cron job running beacon every 5 minutes, and a rootkit (rootkit_mod.ko) hiding PID 31337 from process listings. Removal of any one leaves two others active.
Volatility3 process tree analysis revealed kworker-update (PID 31337) spawned from bash, not kthreadd. Wrong parent process = disguised malware. Beacon confirmed via cmdline output showing AES-256 C2 connection to 203.0.113.99:443.
tshark analysis of PCAP identified base64-encoded credentials embedded inside DNS subdomain queries to evil-c2.example.com. Decoded to reveal stolen /etc/shadow hashes and SSH private keys. Exfil passed through firewalls that permitted DNS traffic.
strings analysis on raw disk image recovered OPERATION NIGHTFALL: a deleted file staging a planned DNS tunnel exfiltration of customer database and financial records. Filesystem showed no record of the file. Raw disk sector analysis recovered it intact.
All five evidence sources correlated into a single 12-event canonical timeline from 02:55 to 03:55 UTC. Attacker achieved root in 36 minutes from first recon hit. Timeline maps each event to MITRE ATT&CK technique, source log, and timestamp, ready for legal or executive reporting.
This investigation demonstrates my ability to:
Two-part tool built directly from the CyberDefenders Linux Breach Investigation CTF. dfir-triage.sh runs all six forensic modules in sequence: log analysis, memory forensics, network forensics, disk forensics, malware triage, and timeline correlation. Attacker IP discovered in Module 1 is automatically threaded into Modules 2 through 5 so each module hunts the same actor. report.py then reads the JSON findings and timeline and renders a tabbed HTML incident report covering Summary, Findings, Timeline, and MITRE ATT&CK mapping. No external dependencies.
Parses auth.log, apache access/error logs, syslog, kern.log. Extracts attacker IP, brute force count, web shell path, and persistence indicators. Feeds discovered IP downstream.
Runs Volatility3 pstree and netscan. Flags anomalous parent-child chains, suspicious PIDs, and C2 connections. Matches network sessions to the attacker IP from Module 1.
Uses tshark to count conversations, filter by attacker IP, extract DNS queries, and identify C2 traffic. Reconstructs session data from the supplied PCAP file.
Mounts disk image read-only, runs strings and foremost, searches for deleted files and attacker artifacts in unallocated space. Reports carved files and suspicious strings.
Static analysis: file type detection, strings extraction for IPs/domains/paths, ELF header inspection, and optional VirusTotal hash lookup. No sandbox required.
Correlates timestamped events from all five prior modules, sorts chronologically, and writes timeline.txt. report.py renders this as a visual MITRE-mapped incident timeline in HTML.
Active network pentest lab, custom Python offensive tooling, and a red-team scenario. Each will follow the same attack-driven format: target, exploit chain, impact, evidence.
HackTheBox machine writeups, DFIR challenge series, and lab write-ups, documenting methodology, tools, and findings. First HTB writeup live: HTB Cap (Easy). DFIR Challenge Series: 1 write-up live, 5 dropping Apr 4-12.
Documented HTB machine compromises: full attack chains, techniques, and lessons learned from every root.
Python/Gunicorn-based security dashboard running on Linux. Exploited an IDOR vulnerability to access another user's packet capture, extracted FTP plaintext credentials via Wireshark, reused those credentials for SSH access, then escalated to root via Linux capabilities (cap_setuid) on Python 3.8.
PCAP files stored at sequential IDs: changing /data/1 to /data/0 exposed another user's capture without any authorisation check.
FTP transmits credentials in cleartext. User nathan's password captured in the downloaded PCAP and recovered with a simple Wireshark FTP filter.
FTP credentials accepted on SSH: same password across services with no isolation, granting an interactive shell as nathan.
cap_setuid+eip misconfigured on /usr/bin/python3.8: one liner calls os.setuid(0) and drops a root shell.
Windows Server 2016 Domain Controller running Active Directory. Enumerated the full domain user list via RPC null session without credentials, identified svc-alfresco with pre-auth disabled and obtained its AS-REP hash, cracked the hash offline with Hashcat, then used BloodHound to trace a four-hop nested group path from svc-alfresco through Account Operators to WriteDACL on HTB.LOCAL. Granted DCSync rights via PowerView, dumped all domain hashes with impacket-secretsdump, and compromised Administrator via Pass the Hash.
Full domain user list retrieved via rpcclient -U "" -N with no credentials. Standard AD misconfiguration exposing the entire attack surface before a single password is known.
svc-alfresco had UF_DONT_REQUIRE_PREAUTH set. AS-REP hash obtained without credentials and cracked offline with Hashcat mode 18200 against rockyou.txt in seconds.
Four-hop nested group chain from svc-alfresco to Exchange Windows Permissions granted WriteDACL on HTB.LOCAL. Used PowerView to write DCSync ACEs, then dumped all domain hashes via impacket-secretsdump.
Administrator NTLM hash obtained via DCSync. Used directly with evil-winrm -H flag to authenticate as Administrator without ever cracking the plaintext password.
Windows Domain Controller with a printer admin panel on port 80. The settings page sends LDAP credentials to any server address configured on the form. Replaced the address with Kali IP, started a netcat listener on port 389, clicked Update. The printer authenticated and transmitted svc-printer : 1edFg43012!! in plaintext. The compromised account is a member of Server Operators. Modified the VSS service binary path to add svc-printer to local Administrators via sc.exe. Reconnected for a fresh session token. Full Administrator access.
The printer admin settings page sends LDAP bind credentials to any server address entered in the form. Changing the address to an attacker-controlled IP and listening on port 389 captures the service account password in plaintext with no exploitation required.
The compromised service account held Server Operators membership, allowing modification of service binary paths on the DC. Changing the VSS service binpath to a net localgroup command and starting the service executed it as SYSTEM, adding the account to local Administrators.
A printer service account had no business being a member of Server Operators on the domain controller. Built-in privileged groups grant broad system-level rights and should only contain named administrator accounts with documented justification.
The printer panel transmitted credentials over an unencrypted LDAP connection on port 389. Using LDAPS on port 636 with certificate validation would prevent credential capture via a rogue listener, as the TLS handshake would fail against an attacker-controlled server.
Windows Domain Controller with an anonymous Shares SMB share containing a zip-protected WinRM backup. Cracked the zip password (supremelegacy) and PFX password (thuglegacy) independently with john, extracted the certificate and key with openssl, and authenticated to WinRM over HTTPS on port 5986 as legacyy. PowerShell history exposed credentials for svc_deploy. BloodHound mapped LAPS_READERS group membership to ReadLAPSPassword on the DC. A single ldapsearch query against ms-Mcs-AdmPwd returned the Administrator password in plaintext.
A zip-protected WinRM authentication certificate for legacyy was stored on an anonymous SMB share. Both the zip and PFX passwords were in rockyou.txt. Certificate-based WinRM access granted without any domain credentials.
PowerShell ConsoleHost_history.txt contained a ConvertTo-SecureString command with the svc_deploy password passed as plaintext. PSReadLine writes every command to disk. History files are never automatically purged.
svc_deploy is a member of LAPS_READERS, which holds ReadLAPSPassword on DC01. The LAPS-managed Administrator password returned in plaintext from a single ldapsearch query against ms-Mcs-AdmPwd.
Both supremelegacy (zip) and thuglegacy (PFX) are present in rockyou.txt. Password-protected archives containing credentials must use passwords that are not in common wordlists to provide any meaningful protection.
Windows Domain Controller with an anonymously accessible support-tools SMB share. A custom .NET binary on the share contained XOR-encoded LDAP credentials, recovered via monodis IL decompilation and a Python decode script. Authenticated LDAP enumeration found a second account's password stored in the user object's info attribute. BloodHound revealed GenericAll over the DC via group membership, enabling a full RBCD attack using four impacket commands to forge an Administrator Kerberos ticket and dump all domain hashes.
LDAP credentials embedded in UserInfo.exe obfuscated with a two-pass XOR cipher using the static key armando. .NET IL is always recoverable with monodis. Obfuscation is not encryption.
Plaintext password stored in the info attribute of the support user object. LDAP attribute fields are visible to all authenticated domain users. A single grep reveals it immediately.
The Shared Support Accounts group holds GenericAll over the DC computer object. support is a member. Full computer object control enables RBCD without any group membership change.
MachineAccountQuota above zero plus GenericAll over DC$ enabled a full RBCD chain: create fake computer, write delegation attribute, forge Administrator Kerberos ticket, dump all domain hashes via secretsdump.
Windows Domain Controller running Windows Server 2008 R2. Anonymous SMB access to a non-standard Replication share exposed Groups.xml containing a GPP cpassword. Used gpp-decrypt to recover SVC_TGS : GPPstillStandingStrong2k18, retrieved the user flag via SMB, then used those credentials to Kerberoast the Administrator account. Cracked the TGS hash offline with Hashcat mode 13100, and delivered a SYSTEM shell via impacket-psexec.
Replication share readable without credentials. Non-standard shares exposed to unauthenticated users are always high-priority targets on a Domain Controller.
Groups.xml in the Replication share contained a cpassword encrypted with Microsoft's published AES key. One command with gpp-decrypt recovered SVC_TGS plaintext credentials.
The built-in Administrator account had an SPN registered. Any authenticated domain user can Kerberoast it and crack the TGS hash offline. High-privilege accounts must never hold SPNs.
Administrator Kerberos TGS hash cracked from rockyou.txt via Hashcat mode 13100. Plaintext credentials used directly with impacket-psexec for a SYSTEM shell.
Windows Domain Controller hosting the Egotistical Bank web application. Harvested six employee names from the public-facing website to build a username wordlist, identified fsmith with pre-authentication disabled and captured the AS-REP hash, cracked it offline with Hashcat, then used WinPEAS to find AutoLogon registry credentials for svc_loanmgr. BloodHound confirmed that account held pre-assigned DCSync rights on the domain object. Dumped all domain hashes via impacket-secretsdump and compromised Administrator via Pass the Hash.
Six full employee names listed publicly on the bank website. With RPC null sessions blocked, the web app was the only path to a username wordlist. Names on company websites are always an enumeration target.
fsmith had UF_DONT_REQUIRE_PREAUTH set. AS-REP hash captured unauthenticated and cracked offline with Hashcat mode 18200 against rockyou.txt.
WinPEAS discovered svc_loanmgr credentials stored in plaintext under HKLM\...\Winlogon\DefaultPassword. AutoLogon credential exposure is a common misconfiguration in Windows environments with service accounts.
svc_loanmgr held GetChanges and GetChangesAll directly on the domain object. No ACL manipulation required. These two rights alone are sufficient to dump every credential in the domain via impacket-secretsdump.
A six-part DFIR investigation of a compromised Linux web server. Each challenge adds a new evidence domain: system logs, RAM dump, packet capture, disk images, malware sample, and final timeline synthesis. 29 of 30 flags captured. Full repository on GitHub with all commands, methodology, and canonical attack timeline.
Documenting every machine rooted on HackTheBox. Write-ups added as completed.
Industry-recognised credentials validating expertise in cybersecurity, compliance, and AI governance. Click any badge to verify on Credly.
Courses, workshops, and hands-on training spanning offensive security, cloud infrastructure, and development. Click any card to view the certificate.
The pipeline is loaded. These sections are actively being built, so check back soon.
Step-by-step walkthroughs of HackTheBox machines, documenting exploit chains, methodology, and post-exploitation techniques. Write-ups live: Cap, Forest, Sauna.
View Writeups ↑Custom Python-based offensive and defensive tooling: recon automation, payload generators, SIEM integrations, and network scanning utilities.
In DevelopmentStructured lab environments covering Active Directory attack chains, network pivoting, malware analysis sandbox walkthroughs, and detection engineering labs.
PlannedWhether it's a penetration testing engagement, a security consultation, or a collaboration, I'm ready. Let's talk.