Java代码审计学习笔记

22 篇文章 3 订阅

此处仅作代码分析,不涉及环境配置及工具使用。

0x00 反序列化带来的安全问题

0x1 反射基础知识

反射是Java核心特征之一,反射允许运行中的Java程序获取自身的信息,并且可以操作类或对象的内部属性。
通过反射在运行时获得程序或程序集中每一个类型的成员和成员的信息, 反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。
反射的核心是 JVM 在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。
关于反射的基础知识,可以参考大佬的文章Java反射技术详解,讲的很好。这里不再赘述。

0x2 静态代理

代理是设计模式中的一种,可以在不修改原有对象的基础上通过代理对对象进行增强,添加额外的功能。代理有两种,一种叫静态代理,一种叫动态代理。
用一个生活中的例子来理解静态代理:
在司法实践中,案件诉讼的原被告可以请律师,这个过程就相当于案件当事人把案件委托给律师来代理,使用静态代理模式来模拟这个过程:
定义一个抽象的接口Speaker,代表说话这个能力:

package Main;
public interface Speaker {
	public void say();
}

然后定义一个实现该接口的当事人类Litigant,代表案件中的原被告:

package Main;
public class Litigant implements Speaker {

	@Override
	public void say() {
		System.out.println("我有诉讼");
	}
}

然后定义一个律师类,律师类也实现了Speaker接口,同时有一个属性叫客户:

package Main;
public class Layer implements Speaker {
	private Litigant yuangao = new Litigant();
	@Override
	public void say() {
		System.out.println("律师代理案件");
		yuangao.say();
		System.out.println("根据法律,应当......");
	}

}

然后定义一个法庭类:

package Main;

public class Court {
	public static void main(String[] args) {
		Layer zhangsan = new Layer();
		zhangsan.say();
	}
}

开庭,律师出席说话,在帮原告说话的同时自己也说了一些话,用自己的法律知识帮原告辩护,这就是“律师”对“原告”这个类进行了增强:
在这里插入图片描述
这就是静态代理。静态代理的缺陷也很明显,它的类型是事先预定好的,需要代理其他事情就得重新编写新的代理,比如要帮被告辩护就得重新定义一个律师,或者在律师类里新增一个属性,修改了代理类本身的结构。
为了解决这个缺陷产生了动态代理。

0x3 动态代理

Java提供了proxy类和InvocationHandler接口,可以生成动态代理类和动态代理对象。
还是用之前的例子,但因为律师分身乏术,不能一人同时代理两个人的案子,我们把律师升级成律师事务所:

package Main;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class Layers implements InvocationHandler{
	private Object litigant;
	public Layers(Object litigant) {
		this.litigant = litigant;
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("案件开始,请我的委托人说话.");
		method.invoke(this.litigant,args);
		System.out.println("我的委托人的话说完了,根据法律,应该.....");
		return null;
	}
}

然后把当事人这个类稍微修改一下,分为“本地人"类和”外地人“类,方便后面演示:
本地人接口:

package Main;
public interface Speaker {
	public void say();
}

实现本地人接口的”本地人“类

package Main;
public class LocalLitigant implements Speaker {
	String type;
	public Litigant(String type) {
		this.type = type;
	}
	@Override
	public void say() {
		System.out.println("我要诉讼"+this.type+"案子");
	}
}

外地人接口:

package Main;
public interface OtherSpeaker {
	public void othersay();
}

”外地人“类:

package Main;
public class Other implements OtherSpeaker {
	@Override
	public void othersay() {
		System.out.println("我要请人代理案子(外地方言)");
	}
}

法庭类也作一定的变动:

package Main;
import java.lang.reflect.Proxy;
public class Court {
	public static void main(String[] args) {
		LocalLitigant zhangsan = new LocalLitigant("刑事案件");
		Layers layer1 = new Layers(zhangsan);
		Speaker proxy = (Speaker) Proxy.newProxyInstance(Court.class.getClassLoader(), new Class[] {Speaker.class}, layer1);
		proxy.say();
	}
}

在这里插入图片描述
为什么要这么做呢,当我们有一个继承了另一个接口的外地人的类实例也要来委托律师的时候,静态代理就需要修改律师类,而动态代理就不用:

package Main;
import java.lang.reflect.Proxy;
public class Court {
	public static void main(String[] args) {
		LocalLitigant zhangsan = new LocalLitigant("刑事案件");
		Layers layer1 = new Layers(zhangsan);
		Other lisi = new Other();
		Layers layer2 = new Layers(lisi);
		Speaker proxy = (Speaker) Proxy.newProxyInstance(Court.class.getClassLoader(), new Class[] {Speaker.class}, layer1);
		OtherSpeaker proxy2 = (OtherSpeaker) Proxy.newProxyInstance(Court.class.getClassLoader(), new Class[] {OtherSpeaker.class}, layer2);
		proxy.say();
		proxy2.othersay();
	}
}

在这里插入图片描述

0x4 Java中的序列化与反序列化

序列化是指把 Java 对象转换为字节序列的过程,反序列化是指把字节序列恢复为 Java 对象的过程,打过CTF的web选手都会对php的反序列化有所了解,这里就不再赘述。
Java语言中序列化的前提:要序列化的类必须实现java.io.Serializable 接口,而且该类的所有属性必须是可序列化的
JDK中的序列化API:
使用ObjectOutputStream(对象输出流) 和ObjectInputStream(对象输入流):
ObjectOutputStream 类通过使用writeObject(Object object) 方法,将对象以二进制格式进行写入。
ObjectInputStream 类通过使用readObject()方法,从输入流中读取二进制流转换成Java对象。

0x5 反序列化带来的安全问题

反序列化基于readObject()方法,如果该方法的内容可控,就会带来安全问题,比如任意代码执行:

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值