Nginx Proxy Manager v2.11.3 RCE
写在前面
该漏洞并非是 NPM 项目的代码存在漏洞,而是 NPM Docker 环境的默认配置存在缺陷,在 NPM 用户拥有 Hosts 的 Manage 权限的情况下,可以获取容器内的 Shell 。
搭建环境
Nginx Proxy Manager (NPM) 基于 Ubuntu + Nginx 容器化部署。
创建一个空目录,创建 docker-compose.yml 文件,内容:
1 | |
在该目录下执行以下命令,以启动容器:
1 | |
访问 http://localhost:81/login 以进行操作。
初始用户密码:
1 | |
具体细节参考: quick-setup
用户登录后可以设置反向代理、重定向等功能。

这些功能基本上都是基于修改 nginx.conf 文件实现的,因此以下的内容本质上都是 nginx.conf 配置安全漏洞,只不过由于该项目允许用户在前端编辑和加载配置文件而能够被利用。
当然了,搞来搞去也就是在容器里而已,没啥意思,管好用户权限就行了。
从文件写入到 RCE
允许 PUT 方式上传
首先新建一个 Proxy Host:

图中 Domain Names 可以写该反代服务器的 IP 或域名,总之能够通过这个地址访问到这个容器里的反代端口即可,我们假设为 npm.example.com。其余字段随便写应该都不影响。
然后通过高级设置,自定义 nginx.conf 配置文件以允许 PUT 方式上传。

1 | |
现在我们可以通过向 URL http://npm.example.com/gupload/filename 发送 PUT 请求来将文件上传到 /etc/ 目录,上传后的文件名应该为 filename。
1 | |
可以看到文件能够被上传到容器内:

关于 ld.so.preload 劫持
理论上我可以通过 写 SSH 私钥、写 Crontab 的方式来实现命令执行,但是为什么要用 ld.so.preload 方式呢?
首先,这是个容器环境,没有 ssh 服务,所以写私钥没意义。
其次,这个容器基于 Ubuntu,尽管我们通过 dav_access user:rw; 指定了文件权限为 0600,但是这容器没开 cron 服务,执行不了命令。
而 ld.so.preload 方案则非常通用。我们不需要给上传的运行库文件设置什么权限,同时 ld.so.preload 文件中允许指定任意位置的 so。最重要的是,大部分进程在执行的时候都会预加载这个 so,十分甚至九分地好用。
编译 Preload.so
源码: preload.c 文件
1 | |
编译:
1 | |

上传 Preload.so
依次执行以下两个命令
1 | |
执行后,preload.so 和 ld.so.preload 文件应该被会上传到 /etc/ 路径下。
执行
成功上传后,理论上此时执行任何二进制程序,都会导致恶意 .so 被执行。
开启监听端口:
1 | |
根据项目代码,在 WebUI 中,保存配置、禁用和启用配置会造成执行 nginx 命令:

项目代码:

所以只要禁用配置,就可以执行 nginx 程序,同时造成执行恶意 so 文件以及里面的反弹 Shell 命令。

文件读取
相对于 RCE,这个功能显得不是很危险。
同样的修改 nginx.conf 配置文件。
1 | |

这样我们就可以通过访问 http://npm.example.com/ggimgs 来下载容器内的 database.sqlite 文件。
这个文件是 npm 的数据库,里面保存了用户密码。同理也可以下载别的文件(也没别的什么有用的文件了)。
