07jdk7u21原生利用链

打印 上一主题 下一主题

主题 851|帖子 851|积分 2553

JDK7u21

反序列化的关键


  • 在于找到可以动态方法执行的代码:例如CC链中的Transformer,CB链中的PropertyUtils#getProperty
JDK7u21中动态方法执行的点,AnnotationInvocationHandler#equalsImpl中的hisValue = memberMethod.invoke(o)。
  1. private Boolean equalsImpl(Object o) {
  2.         if (o == this)
  3.             return true;
  4.         if (!type.isInstance(o))
  5.             return false;
  6.         for (Method memberMethod : getMemberMethods()) {
  7.             String member = memberMethod.getName();
  8.             Object ourValue = memberValues.get(member);
  9.             Object hisValue = null;
  10.             AnnotationInvocationHandler hisHandler = asOneOfUs(o);
  11.             if (hisHandler != null) {
  12.                 hisValue = hisHandler.memberValues.get(member);
  13.             } else {
  14.                 try {
  15.                     hisValue = memberMethod.invoke(o);
  16.                 } catch (InvocationTargetException e) {
  17.                     return false;
  18.                 } catch (IllegalAccessException e) {
  19.                     throw new AssertionError(e);
  20.                 }
  21.             }
  22.             if (!memberValueEquals(ourValue, hisValue))
  23.                 return false;
  24.         }
  25.         return true;
  26.     }
  27. private Method[] getMemberMethods() {
  28.         if (memberMethods == null) {
  29.             memberMethods = AccessController.doPrivileged(
  30.                 new PrivilegedAction<Method[]>() {
  31.                     public Method[] run() {
  32.                         final Method[] mm = type.getDeclaredMethods();
  33.                         validateAnnotationMethods(mm);
  34.                         AccessibleObject.setAccessible(mm, true);
  35.                         return mm;
  36.                     }
  37.                 });
  38.         }
  39.         return memberMethods;
  40.     }
复制代码
AnnotationInvocationHandler#equalsImpl是一个私有方法,仅在AnnotationInvocationHandler#invoke中被调用,它遍历执行了this.type的所有方法,如果这里的this.type为TemplatesImpl,那就可以实现任意代码执行。
  1. public Object invoke(Object proxy, Method method, Object[] args) {
  2.         String member = method.getName();
  3.         Class<?>[] paramTypes = method.getParameterTypes();
  4.         // Handle Object and Annotation methods
  5.         if (member.equals("equals") && paramTypes.length == 1 &&
  6.             paramTypes[0] == Object.class)
  7.             return equalsImpl(args[0]);
  8.         if (paramTypes.length != 0)
  9.             throw new AssertionError("Too many parameters for an annotation method");
  10.         switch(member) {
  11.         case "toString":
  12.             return toStringImpl();
  13.         case "hashCode":
  14.             return hashCodeImpl();
  15.         case "annotationType":
  16.             return type;
  17.         }
  18.         // Handle annotation member accessors
  19.         Object result = memberValues.get(member);
  20.         if (result == null)
  21.             throw new IncompleteAnnotationException(type, member);
  22.         if (result instanceof ExceptionProxy)
  23.             throw ((ExceptionProxy) result).generateException();
  24.         if (result.getClass().isArray() && Array.getLength(result) != 0)
  25.             result = cloneArray(result);
  26.         return result;
  27.     }
复制代码
前面找到动态代码执行的关键后,构造链条的关键即在于如何调用equalsImpl和equalsImpl如何举行任意代码执行。
如何调用equalsImpl

由于AnnotationInvocationHandler实现了接口InvocationHandler,这里很明显可以采用CC1中用到的动态署理调用AnnotationInvocationHandler#invoke,但是注意到invoke的代码逻辑,我们署理的对象,必须是调用名为equals且只有一个Object类型的参数时,才会触发equalsImpl。
找到equals调用链

equals方法通常用于比力两个对象是否是同一引用,一个常见场景是集合set,集合是不允许重复对象的,所以在添加对象局势必涉及到比力操纵。
  1. // HashSet#readObject
  2. private void readObject(java.io.ObjectInputStream s)
  3.         throws java.io.IOException, ClassNotFoundException {
  4.         // Read in any hidden serialization magic
  5.         s.defaultReadObject();
  6.         // Read capacity and verify non-negative.
  7.         int capacity = s.readInt();
  8.         if (capacity < 0) {
  9.             throw new InvalidObjectException("Illegal capacity: " +
  10.                                              capacity);
  11.         }
  12.         // Read load factor and verify positive and non NaN.
  13.         float loadFactor = s.readFloat();
  14.         if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
  15.             throw new InvalidObjectException("Illegal load factor: " +
  16.                                              loadFactor);
  17.         }
  18.         // Read size and verify non-negative.
  19.         int size = s.readInt();
  20.         if (size < 0) {
  21.             throw new InvalidObjectException("Illegal size: " +
  22.                                              size);
  23.         }
  24.         // Set the capacity according to the size and load factor ensuring that
  25.         // the HashMap is at least 25% full but clamping to maximum capacity.
  26.         capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f),
  27.                 HashMap.MAXIMUM_CAPACITY);
  28.         // Create backing HashMap
  29.         map = (((HashSet<?>)this) instanceof LinkedHashSet ?
  30.                new LinkedHashMap<E,Object>(capacity, loadFactor) :
  31.                new HashMap<E,Object>(capacity, loadFactor));
  32.         // Read in all elements in the proper order.
  33.         for (int i=0; i<size; i++) {
  34.             @SuppressWarnings("unchecked")
  35.                 E e = (E) s.readObject();
  36.             map.put(e, PRESENT);
  37.         }
  38.     }
复制代码
这里k.hashCode()作为产生hash的唯一变量,由于TemplateImpl的hashCode方法是一个Native方法,我们欠好追溯,因此选择看一下署理对象的hashCode,而proxy.hashCode() 仍旧会调用到 AnnotationInvocationHandler#invoke ,进而调用到 AnnotationInvocationHandler#hashCodeImpl
  1. // jdk8u21 hashmap
  2. public V put(K key, V value) {
  3.         if (key == null)
  4.             return putForNullKey(value);
  5.         int hash = hash(key);
  6.         int i = indexFor(hash, table.length);
  7.         for (Entry<K,V> e = table[i]; e != null; e = e.next) {
  8.             Object k;
  9.             if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
  10.                 V oldValue = e.value;
  11.                 e.value = value;
  12.                 e.recordAccess(this);
  13.                 return oldValue;
  14.             }
  15.         }
  16.         modCount++;
  17.         addEntry(hash, key, value, i);
  18.         return null;
  19.     }
复制代码
这里代码逻辑很简答,遍历 memberValues 这个Map中的每个key和value,计算每个 (127 * key.hashCode()) ^ value.hashCode() 并求和。
我们如何构造一个和恶意TemplateImpl的hash值一致的署理对象呢,JDK7u21中使用了一个非常巧妙的方法:

  • 当 memberValues 中只有一个key和一个value时,该哈希简化成 (127 * key.hashCode()) ^ value.hashCode()
  • 当 key.hashCode() 等于0时,任何数异或0的结果照旧他自己,所以该哈希简化成 value.hashCode() 。
  • 当 value 就是TemplateImpl对象时,这两个哈希就酿成完全相等
  1. if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
复制代码
可以通过一个简单的爆破程序找到一个hash值为0的字符串
POC代码
  1. final int hash(Object k) {
  2.         int h = 0;
  3.             // 是否启用备用hash算法,一般情况下不启用,可忽略
  4.         if (useAltHashing) {
  5.             if (k instanceof String) {
  6.                 return sun.misc.Hashing.stringHash32((String) k);
  7.             }
  8.             h = hashSeed;
  9.         }
  10.         h ^= k.hashCode();
  11.         // This function ensures that hashCodes that differ only by
  12.         // constant multiples at each bit position have a bounded
  13.         // number of collisions (approximately 8 at default load factor).
  14.         h ^= (h >>> 20) ^ (h >>> 12);
  15.         return h ^ (h >>> 7) ^ (h >>> 4);
  16.     }
复制代码
注意这里代码添加的顺序
  1. private int hashCodeImpl() {
  2.         int result = 0;
  3.         for (Map.Entry<String, Object> e : memberValues.entrySet()) {
  4.             result += (127 * e.getKey().hashCode()) ^
  5.                 memberValueHashCode(e.getValue());
  6.         }
  7.         return result;
  8.     }
  9. private static int memberValueHashCode(Object value) {
  10.         Class<?> type = value.getClass();
  11.         if (!type.isArray())    // primitive, string, class, enum const,
  12.                                 // or annotation
  13.             return value.hashCode();
  14.         // ......
  15.     }
复制代码
在hash表中,同一hash地址对应的链表中的元素是有先后顺序的,我们要包管key.equals(k)的k是恶意TemplatesImpl,key是署理对象,而不是反过来。
poc代码中的两个问题

有两个小问题,InvocationHandler tempHandler = (InvocationHandler) handlerConstructor.newInstance(Templates.class, map);  1.为啥可以传递Templates.class进去,要求不是继续或实现了Annotation 接口的class吗 2.第一个参数传Templates.class是由于这个接口的实现类只有TemplatesImpl 这一个吗,后面type遍历也能调方法
解答:
<blockquote>
遐想到cc1,我当时也有这个疑问。后面看了一下,大概是和不同jdk版本的AnnotationInvocationHandler实现不同,我看了jdk7u21和jdk8u66这两个版本的构造函数是不一样的,7u21中AnnotationInvocationHandler(Class var1, Map var2) {    this.type = var1;    this.memberValues = var2; } 8u66中AnnotationInvocationHandler(Class type, Map 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; } 这里Class
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

惊落一身雪

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表