[Java安全】JDK 动态代理
文章目录
- 动态代理的两个主要类
- InvocationHandler:调用处理程序
- Proxy : 代理
- 代码示例
- 动态代理在反序列化中的作用
动态代理的两个主要类
jdk的动态代理主要需要了解两个类
- InvocationHandler 调用处理程序类
- Proxy 代理类
InvocationHandler:调用处理程序
public interface InvocationHandler
InvocationHandler是由代理实例调用处理程序实现的接口
每一个代理实例都有一个调用处理程序
Object invoke(Object proxy, 方法 method, Object[] args);
当在代理实例上调用方法时,其实就会自动调用这个调用处理程序 invoke()
参数:
- proxy : 代理实例对象
- method: 当前被调用的方法对象,通过它可以获取方法名、参数类型等信息
- args: 调用方法时传递的参数值
Proxy : 代理
public class Proxy extends Object implements Serializable
Proxy
提供了创建动态代理类的和实例的静态方法,它也是由这个方法所创建的动态代理类的超类
动态代理类是一个实现 在类创建时 在运行时指定的接口列表的类
代理接口是由代理类实现的接口
代理实例是代理类的实例对象
public static Object newProxyInstance(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
返回指定接口的代理类的实例,该接口将 方法调用 分派给指定的 调用处理程序
参数
loader
: 类加载器来定义代理类interfaces
: 代理类实现的接口列表h
: 调度方法调用的调用处理函数
代码示例
两个要点
- 代理的是接口,而不是单个用户
- 代理类是动态生成的,而不是静态写死的
首先定义接口类: UserService.java
package DynamicProxy;public interface UserService {public void add();public void del();public void update();public void query();
}
接口类的具体实现: UserServiceImpl.java
package DynamicProxy;public class UserServiceImpl implements UserService{@Overridepublic void add() {System.out.println("增加一个用户");}@Overridepublic void del() {System.out.println("删除一个用户");}@Overridepublic void update() {System.out.println("更新一个用户");}@Overridepublic void query() {System.out.println("查询一个用户");}
}
然后是动态代理的实现类: UserProxyInvocationHandler.java
package DynamicProxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class UserProxyInvocationHandler implements InvocationHandler {//要代理的接口private Object target;public UserProxyInvocationHandler() {}public UserProxyInvocationHandler(Object target) {this.target = target;}//动态生成代理类public Object getProxy() {Object proxy = Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);return proxy;}//处理代理类的实例对象@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {log(method.getName());Object obj = method.invoke(target, args);return obj;}//自定义需求public void log(String msg){System.out.println("【+】调用"+msg+"方法");}}
最后就是启动器 Client.java
package DynamicProxy;public class Client {public static void main(String[] args) {//真实角色UserService userService = new UserServiceImpl();//代理角色UserProxyInvocationHandler userProxyInvocationHandler = new UserProxyInvocationHandler(userService);//代理类(动态生成)UserService proxy = (UserService) userProxyInvocationHandler.getProxy();proxy.add();proxy.update();proxy.del();proxy.query();}
}
动态代理在反序列化中的作用
利用反序列化的漏洞,是需要一个入口类的
先假设存在一个能够漏洞利用的类为 B.f
,比如 Runtime.exec
这种。
我们将入口类定义为 A
,我们最理想的情况是 A[O] -> O.f,那么我们将传进去的参数 O
替换为 B
即可。但是这种情况是很少见的,不会这么简单。
那么如果入口类 A
存在 O.abc
这个方法,也就是 A[O] -> O.abc;而 O 呢,如果是一个动态代理类,O
的 invoke
方法里存在 .f
的方法,便可以漏洞利用了
动态代理对象调用方法时,其所有的方法调用都会自动路由到InvocationHandler.invoke()
里面去,也就是前面比如调用 proay.add()
它会路由到UserProxyInvocationHandler.invoke
方法里面去,从而多调用了一个log()
方法,也就是打印调用了哪个函数
A[O] -> O.abc
O[O2] invoke -> O2.f // 此时将 B 去替换 O2
最后 ---->
O[B] invoke -> B.f // 达到漏洞利用效果
参考
https://drun1baby.top/2022/06/01/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%9F%BA%E7%A1%80%E7%AF%87-04-JDK%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86/
https://www.bilibili.com/video/BV1mc411h719?p=9
https://www.bilibili.com/video/BV16h411z7o9/?p=3&vd_source=cf7d01598010e70f1da4b79252417e78