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 and DFIR challenge series, documenting methodology, tools, and findings. 13 HTB machine write-ups live: Cicada, Heist, Return, Timelapse, Support, Active, Sauna, Forest, Cap, Lame, Shocker, Bashed, Nibbles. DFIR Challenge Series: all 6 write-ups live.
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.
Linux machine running Samba 3.0.20-Debian. nmap service scan identifies the version directly. CVE-2007-2447 exploited via the Metasploit usermap_script module: the Samba username map script option passes the authentication username to /bin/sh without sanitizing shell metacharacters. Injecting a reverse shell payload through the username field delivers an immediate root shell. No privilege escalation required.
Samba 3.0.20 passes the SMB authentication username to /bin/sh unsanitized when username map script is enabled. Shell metacharacter injection executes arbitrary commands as the Samba daemon user.
The Samba daemon runs with root privileges. RCE via CVE-2007-2447 lands directly as root with no privilege escalation step required.
vsftpd 2.3.4 allows anonymous login. The share is empty on this machine but anonymous FTP access exposes the service surface and should be restricted.
Linux machine running Apache 2.4.18. Gobuster with .sh extension discovers /cgi-bin/user.sh inside a 403 directory. CVE-2014-6271 (Shellshock) exploited via the User-Agent header: Apache passes HTTP headers as environment variables to CGI scripts running on a vulnerable bash version. Reverse shell lands as shelly. sudo NOPASSWD: /usr/bin/perl provides instant root via GTFOBins exec.
Apache CGI handler passes HTTP headers as environment variables to bash 4.3. Shellshock payload in the User-Agent header executes as the web user before the script runs.
/usr/bin/perl available as root with no password. GTFOBins exec technique: sudo perl -e 'exec "/bin/bash";' yields an instant root shell.
The directory returns 403 but scripts inside it are reachable. Gobuster with .sh extension finds /cgi-bin/user.sh, the Shellshock attack surface.
Linux machine running Apache on port 80. Homepage references the developer's own phpbash tool. Gobuster finds a /dev/ directory with directory listing enabled, exposing phpbash.php. Web shell executes as www-data. sudo -u scriptmanager NOPASSWD:ALL provides a lateral move with no password. The /scripts/ directory is owned by scriptmanager but test.txt is root-owned with a fresh timestamp: cron is running test.py as root. Overwrite the script with a reverse shell, wait for the cron tick, root shell on port 5555.
Developer debugging tool left in a web-accessible directory with directory listing enabled. phpbash.php executes arbitrary commands as the web user with no authentication required.
Root's crontab runs /scripts/test.py which is owned and writable by scriptmanager. Overwriting the script with a reverse shell payload yields a root shell on the next cron tick.
www-data can run all commands as scriptmanager with no password. Provides access to the /scripts/ directory owned by scriptmanager, bridging the foothold to the cron privesc.
Linux machine running Apache on port 80 with the Nibbleblog CMS. An HTML comment in the index page source exposes the CMS directory before any enumeration tool runs. Default credentials (admin/nibbles) grant admin access. The My Image plugin accepts PHP file uploads despite throwing image processing errors, the file hits disk regardless. Shell lands as nibbler. sudo NOPASSWD on a world-writable script in nibbler's home directory provides instant root via printf payload overwrite.
The plugin throws image processing errors but writes the file to disk regardless. Accessing the upload path directly confirms code execution as the web user. Errors during upload do not mean the file was rejected.
sudo NOPASSWD rule targets /home/nibbler/personal/stuff/monitor.sh, which has rwxrwxrwx permissions. Overwriting the script content and running it via sudo executes arbitrary commands as root.
admin/nibbles (machine name as password) grants full admin dashboard access. No brute-force required. CMS admin panels should enforce credential rotation and lockout policies.
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 Active Directory machine built entirely around credential exposure. An anonymous SMB share exposes a default password in an HR notice. impacket-lookupsid SID brute force builds the full user list via the guest account. Password spray hits michael.wrightson. crackmapexec --users reveals david.orelious password in his AD description field. DEV share access yields a PowerShell backup script with emily.oscars credentials in a ConvertTo-SecureString plaintext call. emily has SeBackupPrivilege. Three commands dump SAM and SYSTEM. impacket-secretsdump extracts the Administrator NTLM hash. Pass-the-Hash delivers the shell.
HR share open to unauthenticated SMB. A plaintext onboarding document contained the company default password. Combined with SID brute force via the guest account, a full user list and valid credential were obtained before any login attempt.
emily.oscars had SeBackupPrivilege, which bypasses ACLs on all files including protected registry hives. reg save extracted SAM and SYSTEM. impacket-secretsdump returned the Administrator NTLM hash. Pass-the-Hash via evil-winrm delivered the Administrator shell.
david.orelious stored his own password in his AD description field, visible to every authenticated domain user. crackmapexec --users surfaces description fields alongside usernames. This is a persistent misconfiguration that hands credentials to any account that can query LDAP.
Backup_script.ps1 on the DEV share called ConvertTo-SecureString with -AsPlainText -Force, storing emily.oscars password as a readable string in the file. Any script containing credentials on a network share is equivalent to a plaintext password file.
Windows Server 2019 support portal with guest access. A ticket attachment contained a Cisco router configuration with three password entries. Two Cisco Type 7 passwords were decoded instantly via Python XOR. The Type 5 MD5crypt hash cracked with hashcat mode 500. RID brute force built a full user list. Credential spray over WinRM hit Chase. Firefox was running as a privileged user. procdump and Select-String pulled the Administrator password in plaintext from process memory.
A Cisco router configuration with password hashes was attached to a publicly accessible support ticket. Type 7 encoding is reversible. Type 5 is MD5crypt, weak against dictionary attacks. Three usable credentials recovered without authentication.
Firefox was running as a privileged user with an active session. procdump captured the full process memory. Select-String found a URL-encoded login submission containing the Administrator password in plaintext from a recent browser session on the local portal.
The admin password decoded from the Cisco config was reused as the Windows password for the Chase account. Cisco device credentials should never share passwords with Windows domain or local accounts, as Cisco configs are frequently stored in plaintext on file shares and ticketing systems.
The support portal allowed guest access to view open tickets including file attachments. Configuration files, credentials, and internal infrastructure details should never be accessible without authentication, regardless of the intended audience of the ticket.
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. 13 write-ups live: Cicada, Heist, Return, Timelapse, Support, Active, Sauna, Forest, Cap, Lame, Shocker, Bashed, Nibbles.
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.