java反序列化:
基础知识:
序列化,反系列化方法:
ObjectOutputStream类的 writeObject() 方法可以实现序列化。
ObjectInputStream 类的 readObject() 方法用于反序列化。
序列化过程就是: 对象 -> ObjectOutputStream (将对象转为字节) -> FileOutputStream (将字节写入磁盘)这里 ObjectOutputStream 装饰了 FileOutputStream,赋予了它“写入对象”的能力。
反序列化过程就是: FileInputStream (从磁盘读字节) -> ObjectInputStream (将字节还原为对象) -> 对象 这里 ObjectInputStream 装饰了 FileInputStream,赋予了它“读取并还原对象”的能力。
装饰:在不修改原有类代码的情况下,动态地给对象添加新的功能。
基本序列化,反序列化流程:
假设我们有个people类而且有tostring方法:
class SerializeDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//序列化操作
System.out.println("----------序列化开始----------");
People p = new People("John", "Male", 30);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("People.txt"));
oos.writeObject(p);
oos.flush();
oos.close();
//反序列化操作
System.out.println("----------反序列化开始----------");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("People.txt"));
People p1 = (People) ois.readObject();
ois.close();
System.out.println(p1);
}
}
漏洞原理:
一些自动执行的方法可以自定义,导致意外代码执行
触发点:
readObject():反序列化的起点,反序列化自动调用readResolve():是 Java 序列化机制中的一个钩子方法,在整个对象构造完成后,JVM 会调用readResolve(),并用它的返回值替代原来的反序列化结果。InvocationHandler.invoke():动态代理类的方法,在调用代理类任意函数时自动调用invoke方法
Gadget链:
定义:可以被链接起来造成反序列化漏洞执行特定功能的类或方法,他们并非恶意代码,但是在链接起来后可以产生危险的行为
构成:
- 入口点(Entry Point):反序列化的入口例如readObject()方法
- 触发类(Trigger Class):包含可直接被反序列化调用方法的类
- 连接类(Chain Class):将触发功能转化成目标功能的中间类
- 执行类(sink Class):最终造成恶意操作的类(例如Runtime)
Serializable 接口:
Serializable 是java提供的标记接口,位于java.io包中,但是这个接口并没有任何内容(没有定义任何方法),它只是作为一个序列化能力的标识,意味着任何
实现该接口的对象都可以实现序列化和反序列化的操作。
只有实现了Serializable或者Externalizable接口的类的对象才能被序列化为字节序列。(不是则会抛出异常)
动态代理相关类
关键类:
java.lang.reflect.Proxy:这是 JDK 动态代理的核心类。通过生成一个代理对象,代理对象会将方法调用转发给指定的InvocationHandlerjava.lang.reflect.InvocationHandler:这是一个 接口,代理对象的每个方法调用,都会被转发到InvocationHandler.invoke()。
例:
package TestCode;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//重写恶意的invoke方法
class ProxyHandler implements Serializable, InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Runtime.getRuntime().exec("calc"); // 会执行 calc
return null;
}
}
// 随便写一个接口
interface TestInterface {
void hello();
}
// 实现类
class Test implements TestInterface {
public void hello() {
System.out.println("Test");
}
}
// 创建动态代理
public class ProxyTest {
public static void main(String[] args) {
Test test = new Test();
TestInterface proxy = (TestInterface) Proxy.newProxyInstance(
test.getClass().getClassLoader(),
new Class[]{TestInterface.class},
new ProxyHandler()
);
proxy.hello();
}
}
命令实现类:
关键类:
- java.lang.Runtime
- java.lang.ProcessBuilder
- javax.script.ScriptEngine
例:
//方法1:Runtime.exec
Runtime.getRuntime().exec("calc.exe");
//方法2:ProcessBuilder
new ProcessBuilder("calc.exe").start();
//方法3:ScriptEngine
ScriptEngineManager manager=new ScriptEngineManager();
ScriptEngine engine =manager.getEngineByName("js");
engine.eval("java.lang.Runtime.getRuntime().exec('calc.exe')");
//需要注意的是,exec方法返回的是一个ProcessBuilder对象,并不会返回命令执行结果,所以我们还需要用BufferedReader额外获取命令执行结果并渲染返回到页面上