风险级别 | 严重 |
---|---|
影响范围 | 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