ITPub博客

首页 > IT基础架构 > 网络安全 > Nexus Repository Manager 2.x 命令注入漏洞 (CVE-2019-5475) 两次绕过

Nexus Repository Manager 2.x 命令注入漏洞 (CVE-2019-5475) 两次绕过

原创 网络安全 作者:酷酷的晓得哥 时间:2020-07-01 15:01:36 0 删除 编辑

作者: Badcode and Longofo@知道创宇404实验室  

时间: 2020年2月9日

原文链接: https://paper.seebug.org/1260/

英文链接: https://paper.seebug.org/1261/


前言

2019年9月初我们应急了Nexus Repository Manager 2.x 命令注入漏洞(CVE-2019-5475),其大致的原因和复现步骤在   hackerone  上公布了,在应急完这个漏洞之后,我们分析该漏洞的修复补丁,发现修复不完全,仍然可以绕过,本篇文章记录该漏洞的两次绕过。虽然早发布了两次的修复版本,由于官方第二次更新公告太慢 https://support.sonatype.com/hc/en-us/articles/360033490774,所以现在才发。

几次更新时间线:

  • CVE-2019-5475(2019-08-09)
  • 第一次绕过,CVE-2019-15588(2019-10-28)
  • 第二次绕过,未分配CVE,更新了公告影响版本(2020-3-25)

注:原始漏洞分析、第一次绕过分析、第二次绕过分析部分主要由Badcode师傅编写,第二次绕过分析+、最新版本分析主要由Longofo添加。

原始漏洞分析

利用条件

  • 需管理员权限(默认认证:admin/admin123)

漏洞分析

以下分析的代码基于 2.14.9-01 版本。

漏洞点是出现在 Yum Repository 插件中,当配置 Yum 的   createrepo或者 mergerepo


代码层面会跳到   YumCapability activationCondition 方法中。



在上面 Path of "createrepo" 中设置的值会通过 getConfig().getCreaterepoPath() 获取到,获取到该值之后,调用 this.validate() 方法



传进来的 path 是用户可控的,之后将 path 拼接 --version 之后传递给 commandLineExecutor.exec() 方法,看起来像是执行命令的方法,而事实也是如此。跟进 CommandLineExecutor 类的 exec 方法



在执行命令前先对命令解析, CommandLine.parse(),会以空格作为分隔,获取可执行文件及参数。

最终是调用了 Runtime.getRuntime().exec()执行了命令。

例如,用户传入的 command 是 cmd.exe /c whoami,最后到 getRuntime().exec()方法就是 Runtime.getRuntime().exec({"cmd.exe","/c","whoami"})

所以漏洞的原理也很简单,就是在 createrepo或者 mergerepo路径设置的时候,该路径可以由用户指定,中途拼接了 --version字符串,最终到了 getRuntime.exec()执行了命令。

漏洞复现

Path of "createrepo"里面传入 payload。



Status 栏可以看到执行的结果



第一次绕过分析

第一次补丁分析

官方补丁改了几个地方,关键点在 这里



常规做法,在执行命令前对命令进行过滤。新增加了一个 getCleanCommand() 方法,对命令进行过滤。



allowedExecutables是一个 HashSet,里面只有两个值, createrepomergerepo。先判断用户传入的 command是否在 allowedExecutables里面,如果在,直接拼接 params--version直接返回。接着对用户传入的 command进行路径判断,如果是以nexus的工作目录( applicationDirectories.getWorkDirectory().getAbsolutePath())开头的,直接返回 null。继续判断,如果文件名不在 allowedExecutables则返回 null,也就是这条命令需要 以 /createrepo或者 /mergerepo结尾。都通过判断之后,文件的绝对路径拼接 --version  返回。

第一次补丁绕过

说实话,看到这个补丁的第一眼,我就觉得大概率可以绕。

传入的命令满足两个条件即可,不以nexus的工作目录开头,并且以 /createrepo或者 /mergerepo结尾即可。

看到补丁中的 getCleanCommand()方法, new File(command)是关键, new File()是通过将给定的路径名字符串转换为抽象路径名来创建新的File实例。 值得注意的是,这里面路径字符串是可以使用空格的,也就是

String f = "/etc/passwd /shadow";
File file = new File(f);

这种是合法的,并且调用 file.getName() 取到的值是 shadow 。结合这个特性,就可以绕过补丁里面的判断

String cmd = "/bin/bash -c whoami /createrepo";
File file = new File(cmd);
System.out.println(file.getName());
System.out.println(file.getAbsolutePath());

运行结果



可以看到, file.getName()的值正是 createrepo,满足判断。

第一次绕过测试

测试环境
  • 2.14.14-01 版本
  • Linux
测试步骤

Path of "createrepo"里面传入 payload。



Status 栏查看执行的结果



可以看到,成功绕过了补丁。

在 Windows 环境下面就麻烦点了,没有办法使用 cmd.exe /c whoami这种形式执行命令了,因为 cmd.exe /c whoami经过 new File()  之后变成了 cmd.exe \c whoami,后面是执行不了的。可以直接执行exe,注意后面是还会拼接 --version的,所以很多命令是执行不了的,但是还是有办法利用能执行任意exe这点来做后续的攻击的。

第二次绕过分析

第二次补丁分析

在我提交上述绕过方式后,官方修复了这种绕过方式,看下官方的 补丁


getCleanCommand()  方法中增加了一个 file.exists()判断文件是否存在。之前的 /bin/bash -c whoami /createrepo这种形式的肯定就不行了,因为这个文件并不存在。所以现在又多了一个判断,难度又加大了。难道就没有办法绕过了?不是的,还是可以绕过的。

第二次补丁绕过

现在传入的命令要满足三个条件了

  • 不以nexus的工作目录开头
  • /createrepo或者 /mergerepo结尾
  • 并且这 createrepo或者 mergerepo这个文件存在

看到 file.exists()我就想起了 php 中的   file_exists(),以前搞 php 的时候也遇到过这种判断。有个系统特性,在 Windows 环境下,目录跳转是允许跳转不存在的目录的,而在Linux下面是不能跳转不存在目录的。

测试一下

Linux



可以看到, file.exists()返回了 false

Windows



file.exists()返回了 true

上面我们说了 new File(pathname),pathname 是允许带空格的。在利用上面WIndows环境下的特性,把cmd设置成   C:\\Windows\\System32\\calc.exe \\..\\..\\win.ini


经过 parse()  方法,最终到 getRuntime.exec({"C:\\Windows\\System32\\calc.exe","\\..\\..\\win.ini"}),这样就能执行 calc了。

在上面这个测试 win.ini是确实存在的文件,回到补丁上面,需要判断 createrepo或者 mergerepo存在。首先从功能上来说,createrepo 命令用于创建 yum 源(软件仓库),即为存放于本地特定位置的众多rpm包建立索引,描述各包所需依赖信息,并形成元数据。也就是这个 createrepo在Windows下不太可能存在。如果这个不存在的话是没有办法经过判断的。既然服务器内不存在 createrepo,那就想办法创建一个,我首先试的是找个上传点,尝试上传一个 createrepo,但是没找到上传之后名字还能保持不变的点。在 Artifacts Upload处上传之后,都变成 Artifact-Version.Packaging这种形式的名字了, Artifact-Version.Packaging这个是不满足第二个判断的,得以 createrepo结尾。

一开始看到 file.exists()就走进了思维定势,以为是判断文件存在的,但是看了官方的文档,发现是判断文件或者目录存在的。。这点也就是这个漏洞形成的第二个关键点,我不能创建文件,但是可以创建文件夹啊。在 Artifacts Upload上传Artifacts 的时候,可以通过 GAV Parameters来定义。


  Group 设置为 test123 Artifact 设置为 test123 Version 设置成 1 ,当上传 Artifacts 的时候,是会在服务器中创建对应的目录的。对应的结构如下




如果我们将 Group设置为 createrepo,那么就会创建对应的 createrepo目录。

结合两个特性来测试一下

String cmd = "C:\\Windows\\System32\\calc.exe \\..\\..\\..\\nexus\\sonatype-work\\nexus\\storage\\thirdparty\\createrepo";
File file = new File(cmd);
System.out.println(file.exists());
System.out.println(file.getName());
System.out.println(file.getAbsolutePath());

可以看到, file.exists()返回了true, file.getName()返回了 createrepo,都符合判断了。

最后到 getRuntime()里面大概就是 getRuntime.exec({"C:\Windows\System32\notepad.exe","\..\..\..\nexus\sonatype-work\nexus\storage\thirdparty\createrepo","--version"})

是可以成功执行 notepad.exe的。(calc.exe演示看不到进程哈,所以换成Notepad.exe)

第二次绕过测试

测试环境
  • 2.14.15-01 版本
  • Windows
测试步骤

Path of "createrepo"里面传入 payload。

查看进程, notepad.exe 启动了



可以看到,成功绕过了补丁。

第二次绕过分析+

经过Badcode师傅第二次绕过分析,可以看到能成功在Windows系统执行命令了。但是有一个很大的限制:

  1. nexus需要安装在系统盘
  2. 一些带参数的命令无法使用

在上面说到的 Artifacts Upload上传处是可以上传任意文件的,并且上传后的文件名都是通过自定义的参数拼接得到,所以都能猜到。那么可以上传自己编写的任意exe文件了。

第二次绕过分析+测试

测试环境
  • 2.14.15-01 版本
  • Windows
测试步骤

导航到 Views/Repositories->Repositories->3rd party->Configuration,我们可以看到 默认本地存储位置的绝对路径(之后上传的内容也在这个目录下):


导航到 Views/Repositories->Repositories->3rd party->Artifact Upload ,我们可以上传恶意的exe文件:



该exe文件将被重命名为 createrepo-1.exe (自定义的参数拼接的):



同样在 Path of "createrepo" 里面传入 payload(这时需要注意前面部分这时是以nexus安装目录开头的,这在补丁中会判断,所以这里可以在最顶层加 ..\ 或者弄个虚假层 aaa\..\ 等)



可以看到createrepo-1.exe已经执行了:



最新版本分析

最新版本补丁分析

第二次补丁绕过之后,官方又进行了修复,官方 补丁主要如下


删除了之前的修复方式,增加了 YumCapabilityUpdateValidator 类,在 validate 中将获取的值与properties中设置的值使用 equals 进行绝对相等验证。这个值要修改只能通过 sonatype-work/nexus/conf/capabilities.xml



最新版本验证

前端直接禁止修改了,通过抓包修改测试:

YumCapabilityUpdateValidator.validate 断到



可以看到这种修复方式无法再绕过了,除非有文件覆盖的地方覆盖配置文件,例如解压覆盖那种方式,不过没找到。

不过 Artifacts Upload那里可以上传任意文件的地方依然还在,如果其他地方再出现上面的情况依然可以利用到。




来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/69912109/viewspace-2701631/,如需转载,请注明出处,否则将追究法律责任。

全部评论
无事不通,无事不晓!

注册时间:2019-03-04

  • 博文量
    44
  • 访问量
    24217