Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
1 contributor

Users who have contributed to this file

75 lines (56 sloc) 2.87 KB

uWSGI 远程代码执行漏洞

wofeiwo@蚂蚁金服天穹实验室

风险级别 严重
影响范围 uWSGI 1.9及以上(最新2.0.15,报告官方之后,官方在3.0开发版中修复了这个问题,不受到影响)
修复方案 不要将uWSGI端口暴露,建议绑定在127.0.0.1上。
发现时间 2018-01-30

背景

uWSGI是一个经常被使用的应用容器,通常情况下由于WSGI是Python实现的标准,所以uWSGI经常作为Python应用容器启动。但实际上uWSGI同样也支持加载Perl/Ruby/Go等应用。

uWSGI除了是应用容器的名称之外,它和Fastcgi之类的一样,也是前端server与后端应用容器之间的一个交流标准。目前nginx,apache也支持uwsgi协议进行代理转发请求。

漏洞成因

经过研究其协议和源码实现,我们发现在uwsgi的协议中,允许传递一些魔术变量,这些变量通常都是可以起到动态调整参数的作用。其中有一个参数UWSGI_FILE,可以用来忽略原有uWSGI绑定App,动态设定一个新的文件进行加载执行。

这里本身就是一个LFI的漏洞,可以任意执行本地存在的任何文件。同时,由于uWSGI程序中默认注册了一系列schemes,导致此问题可以更被放大。

void uwsgi_setup_schemes() {
	uwsgi_register_scheme("emperor", uwsgi_scheme_emperor);	
	uwsgi_register_scheme("http", uwsgi_scheme_http);	
	uwsgi_register_scheme("data", uwsgi_scheme_data);	
	uwsgi_register_scheme("sym", uwsgi_scheme_sym);	
	uwsgi_register_scheme("section", uwsgi_scheme_section);	
	uwsgi_register_scheme("fd", uwsgi_scheme_fd);	
	uwsgi_register_scheme("exec", uwsgi_scheme_exec);	
	uwsgi_register_scheme("call", uwsgi_scheme_call);	
	uwsgi_register_scheme("callint", uwsgi_scheme_callint);	
}

static char *uwsgi_scheme_exec(char *url, size_t *size, int add_zero) {
	int cpipe[2];
	if (pipe(cpipe)) {
		uwsgi_error("pipe()");
		exit(1);
	}
	uwsgi_run_command(url, NULL, cpipe[1]);
	char *buffer = uwsgi_read_fd(cpipe[0], size, add_zero);
	close(cpipe[0]);
	close(cpipe[1]);
	return buffer;
}

这其中很多都是危险协议,尤其注意到其中有exec协议,可以直接通过刚才的UWSGI_FILE变量传参,导致远程执行系统命令。

漏洞利用

由于uWSGI和php的fastcgi会默认绑定本地端口不一样,它是允许绑定端口直接对外的。因此需要找到一个可以访问到的uWSGI端口(uwsgi协议),对其发送uWSGI协议的payload即可。

例如,目标主机上是类似如下:

uwsgi --socket :8001 --module project.wsgi

执行利用程序:

$ python uwsgi_exp.py -u x.x.x.x:8001 -c "echo '111' >/tmp/test"
[*]Sending payload, wish you luck.
HTTP/1.1 200 OK
Content-Type: text/html

.......

在目标主机上查看,即可发现命令已被执行,/tmp/test文件已经存在。

$ cat /tmp/test
111
You can’t perform that action at this time.