Jenkins 任意文件读取漏洞复现与分析 - 【CVE-2018-1999002】
SECURITY-914 / CVE-2018-1999002
|
|
漏洞影响版本:
漏洞复现
测试环境: win平台
通过查找commit记录可知需要将其检出至 29ca81dd59c255ad633f1bd86cf1be40a5f02c64之前
检查core/pom.xml的第41行,确保版本为1.250
然后命令行下编译war包
在jenkins\war\target目录下获得编译好的jenkins.war,同目录下启动:
在管理员登陆(有cookie)的情况下

在没有登陆(未授权,cookie清空)的情况下,只有当管理员开启了allow anonymous read access的时候,才能实现任意文件读取,否则仍需登陆。
开启:

未开启:

而在linux下利用条件会更加苛刻,见后文。
漏洞分析
以payload为例,请求的url为/plugin/credentials/.ini。而在hudson/Plugin.java:227
doDynamic函数用于处理类似/plugin/xx的请求,serveLocalizedFile在stapler-1.250-sources.jar!/org/kohsuke/stapler/ResponseImpl.java第209行左右:
先看最里面的request.getLocale(),然后再来分析stapler.selectResourceByLocale()。
跟入request.getLocale(),至jetty-server-9.2.15.v20160210-sources.jar!/org/eclipse/jetty/server/Request.java:692:
这里用于处理HTTP请求中的Accept-Language头部。比如zh-cn,则会根据-的位置被分为两部分,language为zh,country为cn,然后返回Locale(language,country)对象。倘若不存在-,则country为空,language即对应我们的payload:../../../../../../../../../../../../windows/win,则此时返回一个Locale(language,"")
返回后即进入selectResourceByLocale(URL url, Locale locale),这里的locale参数即上一步返回的locale对象。
urlLocaleSelector对象的声明见stapler-1.250-sources.jar!/org/kohsuke/stapler/Stapler.java:390:
在stapler-1.250-sources.jar!/org/kohsuke/stapler/Stapler.java:324实现了LocaleDrivenResourceSelector类的open方法:
先看看开头的注释,这段代码本意是想根据对应的语言(Accept-Language)来返回不同的文件,比如在ja的条件下请求foo.html,则相当于去请求foo_ja.html,这个过程会先把foo.html分成两部分:文件名foo和扩展名.html,然后根据具体的语言/国家来组合成最终的文件名。
结合payload来看,我们请求的url为/plugin/credentials/.ini,则base为空,扩展名(ext变量)即为.ini,然后通过一系列的尝试openURL,在此例中即最后一个情形con = openURL(map(base+'_'+ locale.getLanguage()+ext));,会去请求_../../../../../../../../../../../../windows/win.ini ,尽管目录_..并不存在,但在win下可以直接通过路径穿越来绕过。但在linux,则需要一个带有_的目录来想办法绕过。
补丁分析
Jenkins官方修改了pom.xml,同时增加一个测试用例文件。真正的补丁在stapler这个web框架中,见commit记录: https://github.com/stapler/stapler/commit/8e9679b08c36a2f0cf2a81855d5e04e2ed2ac2b3 :

对从locale取出的language,country,variant均做了正则的校验,只允许字母数字以及特定格式的出现。在接下来的openUrl中,根据三种变量的不同检查情况来调用不同的请求,从而防止了路径穿越漏洞造成的任意文件读取漏洞。