【作者:百度安全SiemPent Team】
0x01 漏洞概述
Jenkins 官方在 7 月 18 号发布了安全公告,对Jenkins的两个高危漏洞进行通告,其中包括配置文件路径改动导致管理员权限开放的漏洞CVE-2018-1999001,未授权用户通过发送一个精心构造的登陆凭据,能够致使匿名用户获取Jenkins的管理权限。
0x02 漏洞影响面
漏洞公告
https://jenkins.io/security/advisory/2018-07-18/
影响版本
Jenkins weekly 2.89 ~ 2.132
Jenkins LTL 2.73.3 ~ 2.121.1
修复版本
Jenkins weekly 2.133
Jenkins LTS 2.121.2
0x03 漏洞成因
该漏洞是由于CVE-2017-100391的修复代码不严谨导致的,并利用了Jenkins和系统的部分特性。因此要分析该漏洞,需要先了解Jenkins的特性和CVE-2017-100391的详情。
Jenkins的特性
1. 根目录有全局的配置文件config.xml,如果Jenkins启动的时候找不到这个文件,系统就会让匿名用户拥有管理员权限;
2. Jenkins的每个用户有一个独立的文件夹,文件夹名是以用户名命名的,其中都有config.xml文件,存储了用户的信息。例如用户tom的配置信息存储在users/tom/config.xml中,用户admin的配置信息存储在user/admin/config.xml文件中。用户名映射为本地文件夹的最初方案,不对用户名进行任何编码和转换,用户名就是本地文件夹名。由于系统版本的原因,该方案存在诸多的bug,jenkins官方为了修复bug,前后有3种比较稳定的方案。
方案一:
1. 用户名如果是[a-zA-Z0-9_. @-]这些字符组成的,可以直接当作文件夹名称使用;
2. 如果用户名中包含其它的特殊字符,那么特殊字符会被转换成”$”符号开头的十六进制形式;
3. 每次转换,都会检查本地是否有最初方案方式命名的文件夹,如果有就把文件夹名称用现有方案进行重命名。
参看代码:
jenkins/core/src/main/java/hudson/model/User.java 的 User.getOrCreate()函数,如图:
该方案存在设计缺陷,因此被爆出漏洞CVE-2017-100391:
1. 如果用户名中包含 .. 这样的字符串,将会扰乱Jenkins中的其他配置文件;
2. 用户名中可能会包含一些特殊的名称,例如COM(Windows系统)
为了修复CVE-2017-100391,Jenkins官方更改了用户名映射为本地文件夹的设计方案。代码变动详情:
https://github.com/jenkinsci/jenkins/commit/566a8ddb885f0bef9bc848e60455c0aabbf0c1d3#diff-1b2e968f637b757e3ad0aa7ae87739f9
参看代码:
jenkins/core/src/main/java/jenkins/model/IdStrategy.java,如图:
jenkins/core/src/main/java/hudson/model/User.java,如图:
该方案就是,用户名映射为本地文件夹的第二套方案。
方案二:
1. 对“..”、”.”等特殊符号进行了转换,如“..”转换为”$002e$002e”,“.”符号转换为”$002f”;
2. 对系统中的特殊字符串进行了转换,如”con”转换为”$0063on”,“prn”转换为”$0070rn”,“aux”转换为”$0061ux”等;
3. 检查如果存在以方案一方式命名的文件夹,就会把它的配置文件config.xml进行移动,并按照方案二的方式重新命名。如存在users/..test3/的文件夹,就把该文件夹下的config.xml移动重名为users/$002e$002etest3/config.xml。
方案二继续被爆出存在漏洞,也就是CVE-2018-1999001。当用户名为”..”时,方案二第三步会检查是否存在users/../config.xml文件。由于”..”在系统文件夹路径中的特性,users/../config.xml实际就是jenkins根目录默认的全局配置文件,所以它会被移动到users/$002e$002e/config.xml。jenkins重启时,检测不到全局配置文件的存在,会让匿名用户拥有管理员的权限,因此也就产生了漏洞CVE-2018-1999001。
Jenkins支持HTTP基本身份认证和基于表单的身份认证方式,当HTTP请求带有HTTP基本身份认证头时,过滤器会检测到并对请求进行身份验证,相关的调用关系如下:
BasicHeaderProcessor.doFilter() ->BasicHeaderApiTokenAuthenticator.authenticate()
其中,jenkins/core/src/main/java/jenkins/security/BasicHeaderProcessor.java,doFilter()方法代码片段如图:
最终由BasicHeaderApiTokenAuthenticator这个扩展接口类来进行用户名密码的匹配性验证,文件jenkins/core/src/main/java/jenkins/security/BasicHeaderApiTokenAuthenticator.java,代码如图:
getById()方法最终调用了存在漏洞的getOrCreate()方法,代码文件
jenkins/core/src/main/java/hudson/model/User.java,如图:
在getOrCreate()方法中,有两个关键参数configFile、unsanitizedLegacyConfigFile,它们的值通过如下方法获取:
获取configFile、unsanitizedLegacyConfigFile参数值的时候分别调用了
jenkins/core/src/main/java/hudson/model/IdStrategy.java文件中的filenameOf()方法和legacyFilenameOf()方法。这两个方法是根据HTTP基本认证头中传入的username来生成配置文件的路径,生成方式对应了前文中提到的方案二和方案一。代码片段如图:
为了修复CVE-2017-100391,当满足一定条件时,会对旧方案生成的配置文件路径进行改动。由于”..”在系统文件路径中的特殊性,导致了漏洞的产生。漏洞代码片段如图:
漏洞CVE-2018-1999001的修复方案是,在移动旧方案配置文件之前,会将配置文件路径转换成一个规则的绝对路径。假如jenkins的根目录是/jenkins/,当用户名为”..”时,路径“/jenkins/users/../config.xml”会被转换成规则的绝对路径“/jenkins/config.xml”,然后检查这个规则的绝对路径是否以”/jenkins/users/“开头的,如果不是,就不进行移动。
代码修复链接:
https://github.com/jenkinsci/jenkins/commit/5f4d014b3b7f89e206c6c8509540ed559f604959#diff-1b2e968f637b757e3ad0aa7ae87739f9
参看代码:
jenkins/core/src/main/java/hudson/model/User.java
0x05 漏洞测试
一个未授权用户访问漏洞Jenkins版本的任意页面时,如果http header中加入了Authorization基础认证头,并且基础认证头中的用户名为”..”,就会触发该漏洞。
首先生成Authorization基础认证头的值,base64编码处理由”..”、”:”、“any-password”拼接构成的字符串,如图:
以漏洞版本Jenkins 2.89为例,在http header中加入Authorization基础认证头,其值为“Li46YW55LXBhc3N3b3Jk”,GET请求登录页面,请求截图如下:
访问之后,发现Jenkins安装目录的配置文件config.xml被转移到了/users/$002e$002e目录下,如图所示:
重启Jenkins服务,访问发现Jenkins进入了legacy模式,匿名用户可获取管理员权限,如图所示:
0x06 结语
根据百度安全的分析研究,该漏洞有如下特点:
1、影响范围较广
2、漏洞利用难度小
3、未授权即可远程攻击,但需要目标系统重启Jenkins服务才能完成整套攻击
4、一旦完成攻击危害严重
建议相关用户尽快评估,并升级Jenkins版本。
Jenkins weekly 升级到 2.133 版本
Jenkins LTS 升级到 2.121.2 版本