妙用深信服自研内存后门检测引擎,用“魔法”打败“魔法”

admin 2022年7月15日21:17:03评论502 views字数 6868阅读22分53秒阅读模式
妙用深信服自研内存后门检测引擎,用“魔法”打败“魔法”

前言


随着攻防对抗技术的不断演进,为了躲避成熟的文件查杀技术,攻击者开始将攻击载荷从磁盘向内存迁移。从原理层面绕过传统的文件查杀技术,攻击行为更加隐蔽。整个攻击过程只有部分文件甚至无文件落地,给防守方的检测响应带来了巨大的挑战。


实战攻防场景中,主流内存后门大致可以分为两类:


1)由 Webshell 技术演进而来的 Web 内存马,其中以 Java 内存马和 .NET 内存马最为常见;


2)热门黑客远控工具(Cobalt Strike,Metasploit 等)利用的一些无文件攻击手法。如利用反射 dll 注入、PowerShell 脚本执行等高级攻击手法,将后门程序注入到系统进程;


本文首先简单概述常见内存马和无文件攻击手法,然后介绍深见研究实验室为对抗此类内存攻击手法自研的 Mem-shell Inspector(MSI) 引擎。


JAVA 内存马

Java 内存马基于 Java 应用服务器(如 Tomcat,WebLogic,Jboss 等),利用服务器或者服务器中间件的功能接口在其进程内驻留并运行,接收攻击者发起的请求。


Java 内存马主要是利用 Java 服务器解析 URL 请求时,对内容的处理过程中某些特定功能函数的滥用实现。Java 内存马理论上可存在于所有 Java 应用服务器。可按照影响服务器以及使用特定的功能函数对其进行分类。常见的 Java 内存马有以下几种:

妙用深信服自研内存后门检测引擎,用“魔法”打败“魔法”


以 Tomcat 为例,Tomcat 的连接器负责 Socket 通信和应用层协议解析,并将得到的 ServletRequest 请求传递给容器。容器负责处理 ServletRequest 请求。Tomcat 中存在四种具有”父子“关系的容器,一个 Engine 具有多个 Host,一个 Host 具有多个 Context,一个 Context 具有多个 Servlet。当一个请求过来,会在容器里去寻找对应的Wrapper 容器,也就是一个 Servlet。整个调用过程是通过 Pipeline-Valve 管道进行的。Wrapper 容器最后一个 Valve会创建一个 Filter 链,然后调用 doFilter 方法,最后调用到 Servlet 的 service 方法。其处理访问请求的过程如下图所示:


妙用深信服自研内存后门检测引擎,用“魔法”打败“魔法”


Wrapper 容器的最后一个 Valve 创建 filter 链,最后调用 Servlet 的 Service 方法的过程是内容请求绑定的过程。也会是常见 Tomcat 组建内存马插入的关键节点。


正常情况下创建 FilterChain,并且要使得其 Filter 起作用,需要先从 StandardContext 中拿到 filterMaps 进行路径匹配,然后 在 filterchain 中添加 filterConfig。在此之前,则还需要向 StandardContext 中添加包含 filter 信息的 filterDef。因此,想要注入一个filter,需要先后往 StandardContext 中注入 filterDef, filterMap 以及 filterConfig 。过程如下:


// step1: 注入filter classObject EvilFilterClass = new FilterInjector(this.getClass().getClassLoader()).g(code.decodeBuffer(classStr)).newInstance();
// step2: 获取StandardContextorg.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase =(org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();StandardContext standardContext = (StandardContext)webappClassLoaderBase.getResources().getContext();
// step3: 反射获取FilterDef,设置filter名等参数后,调用addFilterDef将FilterDef添加Class<?> FilterDef = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef");Constructor<?> declaredConstructors = FilterDef.getDeclaredConstructor();org.apache.tomcat.util.descriptor.web.FilterDef filterDef = (org.apache.tomcat.util.descriptor.web.FilterDef)declaredConstructors.newInstance();filterDef.setFilter((Filter)EvilFilterClass);filterDef.setFilterName(FilterName);filterDef.setFilterClass(EvilFilterClass.getClass().getName());standardContext.addFilterDef(filterDef);
// step4: 反射获取FilterMap并且设置拦截路径,并调用addFilterMapBefore将FilterMap添加进去Class FilterMap = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap");Constructor declaredConstructor = FilterMap.getDeclaredConstructor();org.apache.tomcat.util.descriptor.web.FilterMap filterMap = (org.apache.tomcat.util.descriptor.web.FilterMap)declaredConstructor.newInstance();filterMap.addURLPattern("routePath");filterMap.setFilterName(FilterName);filterMap.setDispatcher(DispatcherType.REQUEST.name());standardContext.addFilterMapBefore(filterMap);
// step5: 反射获取ApplicationFilterConfig,构造方法将 FilterDef传入后获取filterConfig后,将设置好的filterConfig添加进去Class<?> ApplicationFilterConfig = Class.forName("org.apache.catalina.core.ApplicationFilterConfig");Constructor<?> declaredConstructor1 = ApplicationFilterConfig.getDeclaredConstructor(Context.class,FilterDef.class);declaredConstructor1.setAccessible(true);org.apache.catalina.core.ApplicationFilterConfig filterConfig = (org.apache.catalina.core.ApplicationFilterConfig) declaredConstructor1.newInstance(standardContext,filterDef);
Field Configs = standardContext.getClass().getDeclaredField("filterConfigs");Configs.setAccessible(true);Map filterConfigs = (Map) Configs.get(standardContext);filterConfigs.put(FilterName,filterConfig);


1.利用 ClassLoad 注入 Filter 内存马类;


2.反射创建 filterDef,其中 fiterDef 的 filter  对象即为实例化的内存马对象;


3.利用 standardContext#addFilterDef 注入 filterDef;


4.反射创建 filterMap,并利用 standardContext#addFilterMapBefore 注入;


5.创建  filterConfig,利用反射直接注入 StandardContext 中。


通过此方法可以注入 Tomcat 的 Filter 内存马,检测的关键在于对  jvm Klass 对象方法调用链分析。


.NET 内存马

.NET 内存马是一种基于 .NET 运行环境,直接在内存中加载运行的后门程序。一般可以分为由 ASP Webshell 演进而来的 ASP.NET 内存马和利用 .NET 内存加载技术实现攻击载荷无文件落地的攻击手法。常见的 .NET 内存马有以下几种:

妙用深信服自研内存后门检测引擎,用“魔法”打败“魔法”

以托管加载器 Assembly.Load(Bytes[])方法为例,根据 MSDN,System.Reflection.Assembly.Load(Bytes[]) 用于加载包含 .NET assembly 的COFF结构程序到当前进程地址空间的调用者应用程序域中。通过System.Reflection.Assembly.Load(Bytes[]),可以实现类似反射 dll 注入功能。


参考 .NET core runtime 源码,System.Reflection.Assembly.Load(Bytes[]) 方法由 AssemblyNative::LoadFromStream 实现:


void QCALLTYPE AssemblyNative::LoadFromStream(INT_PTR ptrNativeAssemblyLoadContext, INT_PTR ptrAssemblyArray, INT32 cbAssemblyArrayLength, INT_PTR ptrSymbolArray, INT32 cbSymbolArrayLength, QCall::ObjectHandleOnStack retLoadedAssembly){...    // We must have a flat image stashed away since we need a private    // copy of the data which we can verify before doing the mapping.    PVOID pAssemblyArray = reinterpret_cast<PVOID>(ptrAssemblyArray);

PEImageHolder pILImage(PEImage::LoadFlat(pAssemblyArray, (COUNT_T)cbAssemblyArrayLength)); // [1]

// Need to verify that this is a valid CLR assembly. if (!pILImage->CheckILFormat()) // [2] ThrowHR(COR_E_BADIMAGEFORMAT, BFA_BAD_IL);

// Get the binder context in which the assembly will be loaded ICLRPrivBinder *pBinderContext = reinterpret_cast<ICLRPrivBinder*>(ptrNativeAssemblyLoadContext);

LoaderAllocator* pLoaderAllocator = NULL; if (SUCCEEDED(pBinderContext->GetLoaderAllocator((LPVOID*)&pLoaderAllocator)) && pLoaderAllocator->IsCollectible() && !pILImage->IsILOnly()) { // Loading IJW assemblies into a collectible AssemblyLoadContext is not allowed ThrowHR(COR_E_BADIMAGEFORMAT, BFA_IJW_IN_COLLECTIBLE_ALC); }

// Pass the stream based assembly as IL and NI in an attempt to bind and load it Assembly* pLoadedAssembly = AssemblyNative::LoadFromPEImage(pBinderContext, pILImage, NULL); // [3] { GCX_COOP(); retLoadedAssembly.Set(pLoadedAssembly->GetExposedObject()); }...}

此过程主要包含三步:


1.以 Flat 模式加载 .NET 程序集的 byte 数组到内存;


2.解析 .NET PE 格式,并校验 .NET 程序集;


3.加载 .NET 程序集到当前应用程序域。


通过此方法加载的 .NET 程序集不会落地,检测的关键在于对 CLR 内存中加载的 MSIL 指令分析。


无文件攻击

无文件攻击是指攻击者在攻击过程中,没有或只有部分文件落地。由于恶意特征少、攻击比较隐蔽,逐渐成为攻防对抗中的主流攻击手法。常见的无文件攻击方法有:


1.通过进程注入的方式,在内存中加载攻击载荷。


2.通过 PowerShell 命令远程下载 PowerShell 脚本,并执行脚本中的攻击载荷。


3.利用远程下载执行 vbscript、JavaScript 脚本,并执行攻击载荷。


4.利用系统服务漏洞执行远程攻击载荷,如永恒之蓝。


5.利用计划任务、系统服务等来执行攻击载荷。


6.利用特殊的文件系统储存攻击载荷,如 wmi 库。


7.利用文件系统存储没有特征的脚本、文档等来加载恶意内容,如 Word 中宏指令。


以 Metasploit 的进程迁移(migrate 模块)功能为例,其实现过程原理如下:


妙用深信服自研内存后门检测引擎,用“魔法”打败“魔法”

migrate 模块的关键步骤如下:


1.读取 metsrv.dll(metpreter payload 模板 dll)文件到内存中;


2.生成目标 payload:

a) msf 生成一小段汇编 migrate stub 主要用于建立 socket 连接。

b) 将 metsrv.dll 的 dos 头修改为一小段汇编 meterpreter_loader 主要用于调用 reflective loader函数和 dllmain 函数。在 metsrv.dll 的 config block区填充 meterpreter 建立 session 时的配置信息。

c) 最后将 migrate stub 和修改后的 metsrv.dll 拼接在一起生成最终的 payload。


3.向 msf server 发送 migrate 请求和 payload。


4.msf 向迁移目标进程分配一块内存并写入 payload。


5.msf 首先会创建的远程线程执行 migrate stub,如果失败了,就会尝试用 apc 注入的方式执行 migrate stub。migrate stub 会调用 meterpreter loader,meterpreter loader 才会调用 reflective loader。


6.reflective loader 进行反射式 dll 注入。


7.最后 msf client 和 msf server 建立一个新的 session。


整个过程没有文件落地,检测的关键在于对内存中的 x86 汇编指令分析。

Mem-shell Inspector 引擎简介

深信服深见研究实验室自研的 Mem-shell Inspector(MSI)引擎,从内存视角分析恶意指令,可以有效检测热门 Web 内存马和黑客工具利用的无文件攻击手法。


MSI 引擎已赋能深信服云主机安全保护平台 CWPP 和深信服终端检测响应平台 EDR,并支持将检测告警推送至深信服可扩展威胁检测响应平台 XDR,帮助客户解决攻防场景下,内存马、无文件攻击等高级攻击手法检测的痛点。


部分攻击手法的检测效果如下:

Java 内存马:

妙用深信服自研内存后门检测引擎,用“魔法”打败“魔法”


.NET 内存马:


妙用深信服自研内存后门检测引擎,用“魔法”打败“魔法”


Cobalt Strike 利用 PowerShell 植入内存后门:


妙用深信服自研内存后门检测引擎,用“魔法”打败“魔法”

结语


内存攻防已经成为攻防对抗必争之地。内存攻击的高隐蔽性和层出不穷的攻击手法,给检测响应带来了巨大的挑战。检测方法难以参考传统文件检测,如特征库、机器学习、文件沙箱等成熟方案。唯有不断跟踪、狩猎、研究最新攻击技术,不断创新检测方案,方能在内存攻防战场占领制高点。


妙用深信服自研内存后门检测引擎,用“魔法”打败“魔法”



原文始发于微信公众号(深信服千里目安全实验室):妙用深信服自研内存后门检测引擎,用“魔法”打败“魔法”

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年7月15日21:17:03
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   妙用深信服自研内存后门检测引擎,用“魔法”打败“魔法”https://cn-sec.com/archives/1180452.html

发表评论

匿名网友 填写信息