基于请求响应对象搜索的Java中间件通用回显方法

文章资讯 2020-06-14 16:24:45

基于请求响应对象搜索的Java中间件通用回显方法

前言
看了很多师傅们在研究针对Java中间件+Java反序列化漏洞回显的
(。。。应该还有挺多,就不翻了)又看到了c0ny1师傅的作品:《java内存对象搜索辅助工具》配合IDEA在Java应用运行时,对内存中的对象进行搜索。比如可以可以用挖掘quest对象用于回显等场景。

按照经验来讲Web中间件是多线程的应用,一般qust对象都会存储在线程对象中,可以通过Thad.curntThad()或Thad.getThads()获取。并且目前回显思路主要是基于加载类,执行static块或者构造方法(原生反序列化、FastJson、Jackson一类的都有):TemlatesIml类的反序列化链,内嵌类的byte,defineClass。
其他反序列化链使用URLClassLoader进行远程加载类。
JNDI远程加载类。所以想到:我们能否写单个类,让它能够触发时,使用2的思路去寻找quest和sonse,从quest中获取命令参数,然后向Resonse中写入呢?
先说结论:可以实现,在Tomcat、Jetty和Weblogic中都可使用(只测了这三,个人觉得其它Java中间件也没差),还测试了Shiro、Fastjson、Jackson反序列化的场景。
Tomcat6+Shiro会报java.io.StamCorrutedExcetion:invadtye:错误,很迷,有空再解决吧。
精简后的编译完的class文件大小在2800-2900B左右,Shiro反序列化用的Cookie值大小可以控制在5000字节左右,勉强可以接受。
响应时间一般在3S内。
基本思路
javax.servlet.htt.HttServletRequest
javax.servlet.htt.HTTPServletResonsejava类中间件的quest和sonse分别实现以上两个接口
从Thad.curntThad()起始搜索实现了如上两个接口的对象
通过HttServletRequest的getHeader方法可以获取到请求头
通过HTTPServletResonse的getWriter方法可以获取到响应的Writer(开始用的ServletResonse的getOututStam接口,但是会报重复获取的OututStam的错误,此处就改为使用getWriter了)
都搜索到后,该执行执行,该输出输出初始代码
有点多,都贴上有水字数的嫌疑,还是贴gist链接吧。┓(′?`)┏
htts:gist.github.comfnmsd89118c2967cd53c244389564d2f8b368
编译完的class3888字节,好吉利~
为了类能小一些,没有做c0ny1师傅那么细致的搜索分类和剪枝。
为了方便,纯粹使用深度优先搜索(DFS),依次搜索字段
然而接下来为了瘦身,各种先定逻辑还得继续砍砍砍
**PS:**在搜索Tomcat的Request过程中,不知道为何会搜出一个并非当前Request的对象,所以这里限制了Request必须包含cmd头,才认为找到了真的Request,Resonse目前没有发现这个问题。
为代码瘦身
由于Nginx有Cookie4096B长度的限制,Tomcat有8096B的长度限制(感谢c0ny1师傅),所以为了能使用Temlates类的链,编译出的Class文件还是越小越好。
以下内容比较乱,是一个逐渐尝试减小Class文件的过程,没兴趣可以略过
整个分析过程使用classy-0.4.jar来进行对Class文件的分析:
htts:github.comzxh0classy1.初始
? 3888字节
2.去掉可有可无的静态字段
staticClassReqC=HttServletRequest.class;
staticClassResC=HttServletResonse.class;
staticintmax_deth=50;? 3734字节去掉字符串连接:3640字节
删除多余逻辑的限制逻辑
if(deth>50||(q!=nl&am;&am;s!=nl)){
turn;
}3611字节
删除getModifiers的调用(删除一个方法大概60字节的样子?)
3551字节
减少了一个空判断,大概4字节
3547
减少局变量的定义
Classa=obj.getClass();
if(a.isPrimitive()||a.toString().startsWith("java.lang")){
turntrue;
}变为:
if(obj.getClass().isPrimitive()||obj.getClass().toString().startsWith("java.lang")){
turntrue;
}3525,少了22字节
去掉调试时用的rintStackTrace
3488字节
删掉flush和waitFor
3451字节,貌似没少多少
删掉static块和start方法,只保留构造方法触发
3372
删除PrintWriter的变量赋值,删除Testbyfnmsd字符串
3260
删除掉roc的定义,直接跟getInutStam一块调用
3220
合并Scanner相关逻辑为一句:
s.getWriter().rintln(newScanner(Runtime.getRuntime().exec(q.getHeader("cmd")).getInutStam()).useDemiter("\A").next())
3130
以下本应有判断来进行的剪枝,改为使用异常处理兜着
去掉java.lang类的搜索剪枝
obj.getClass().toString().startsWith("java.lang")
节省了toString和startsWith
3009
去掉数组包裹类型的为非rimtive的判断
obj.getClass().getComonentTye().isPrimitive()
2962
去掉全部isPrimitive的判断
2917
去掉搜索时字段值为nl的判断
2908
给类名、字段名、方法名、局部变量名都改为一个字符
2850
这里改局部变量其实不起作用。
去掉是否为静态字段的判断
if((decladField.getModifiers()&am;0x00000008)==0)
2803(最终大小在2800-2900之间,不会差太多,后面不知道又改哪里了,到了2900左右)
瘦身总结
最终结果代码(使用构造方法触发,也可以改成Static,都一样):htts:gist.github.comfnmsd8165cedd9fe735d7ef438b2e977af327
(前面传错了一个中间的bug版本,会重复执行重复回显,感谢l1nk3r师傅早早的帮我发现了)尽量减少引用的方法和类型(包括像字符串连接这种隐式调用)
尽量减少字段、局部变量的定义
能用异常处理兜底的处理,减少判断。
蚊子腿也是肉,把字段名、参数名、变量名改小点(这块不确定,可能只有字段名有效)
比较大的地方主要在ConstantPo和变量定义上(ConstantPo还好理解,变量定义不太明白,后续还是好好在学习学习Class文件结构)测试
由于都是new一个对象或者static块执行,所以偷了个懒,简单写了个js页面,可以看到没有任何的输出语句,只是new了一下对象:
<%@ageimort="aa.a"%>
<%@agecontentTye="texthtml;charset=UTF-8"language="java"%>
<%
newa();
%>Weblogic(打包有点问题,页面没改成最简版的):Tomcat:Jetty:Tomat8+Shiro反序列化
这里用的Jdk7u21的链(TemlatesIml触发)来new我写的类,memberMe长度5036
懒得部环境的同学,可以用Vhub里面的Shiro环境,(SringBoot用的嵌入式Tomcat9)但是那个得用CommonsBeanutils1的链。3.FastjsonJNDI加载场景(这里用vhub的fastjson1.2.47-rce,SringBoot用的嵌入式Tomcat9)4.weblogic12+Jackson反序列化
这个用的我之前给Jackson提的链,可惜没混到CVE编号,嘤嘤嘤o(╥﹏╥)o其他原来设置了深度优先搜索最大50层,结果weblogic下,quest搜索出来在第50层,sonse在quest下面第51层,所以后来我最大深度改到了52层。
具体使用中使用TemlatesIml需要类链,需要使用Javassist添加AbstractTranslet接口,当然,也可以直接imlements,但是这样又多俩需要实现的接口,class会大一些,具体可以参考:htts:xz.ayun.comt6227一点思考:非HTTP的反序列化,搜Socket对象,然后从getOututStam再往里写能不能行呢?