zoukankan      html  css  js  c++  java
  • java反序列化学习

    java反序列化学习

    序列化:把一个对象的状态信息转换为字节序列,用于存储或者传输
    反序列化:把字节序列转换为java对象
    想要将一个对象序列化,必须实现Serializable或者Externalizable的接口类

    序列化:

    import java.io.*;
    
    class User implements java.io.Serializable{  //创建一个user类,并实现Serializable接口
        public String name;
        public int age;
        public void method01(){
            System.out.println("名字"+name+"年龄:"+"age");
        }
    }
    
    public class demo01 {
        public static void main(String[] args) {
            User a = new User();    //静态方法中不能引用非静态变量
            a.name = "小明";
            a.age = 19;
            try {
                FileOutputStream file01 = new FileOutputStream("C:\Users\0x_end\Desktop\1.txt");//输出到本地文件的路径
                ObjectOutputStream out = new ObjectOutputStream(file01);    //序列化对象
                out.writeObject(a);
                out.close();    //关闭流对象
                file01.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    会在桌面上生成一个1.txt的文件
    ObjectOutputStream.writeObject()方法生成的是一个二进制文件,所以我们用记事本打开会出现乱码
    我们可以用一个二进制/十六进制的编辑器来打开文件

    反序列化:

    import java.io.*;
    
    class User implements java.io.Serializable{  //创建一个user类,并实现Serializable接口
        public String name;
        public int age;
        public void method01(){
            System.out.println("名字"+name+"年龄:"+"age");
        }
    }
    
    public class demo01 {
        public static void main(String[] args) {
            User a = new User();
            try {
                FileInputStream file02 = new FileInputStream("C:\Users\0x_end\Desktop\1.txt");	//文件路径
                ObjectInputStream out2 = new ObjectInputStream(file02);	//反序列化
                a = (User) out2.readObject();	//读取文件
                file02.close();	//关闭流对象
                out2.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException | ClassNotFoundException e) {
                e.printStackTrace();
            }
            System.out.println(a.name);
            System.out.println(a.age);
        }
    }
    成功输出

    URLDNS分析

    payload

    先看URLDNS的payload,使用getObject()获取我们传入的参数URL
    URLStreamHandler handler = new SilentURLStreamHandler();    //创建一个URLStreamHandler
    
    HashMap ht = new HashMap();    //将包含URL的HashMap
    按照我的理解可能是,创建一个HashMap的对象ht,用于存放URL经过HashMap操作后的数据,也是最后返回的值

    URL u = new URL(null, url, handler); // URL要用作键
    这一句就好理解了,HashMap存储的内容是键值对,创建一个URL的对象u,作为KEY(键)

    ht.put(u, url); //The value can be anything that is Serializable, URL as the key is what triggers the DNS lookup.
    添加键值对,u为key,url为value(值)

    Reflections.setFieldValue(u, "hashCode", -1);
    修改了"hashCode"字段的值为-1

    利用链

    在注释中我们可以看到利用链
     /*   Gadget Chain:
     *     HashMap.readObject()
     *       HashMap.putVal()
     *         HashMap.hash()
     *           URL.hashCode()
     */

    readObject方法

    文件路径:....JAVAlibsrcjava.basejavautilHashMap.java
    根据注释我进入到HashMap中的readObject方法,可以看到在49行,调用了putval方法,并对key进行了hash计算。
        /**
         * Reconstitutes this map from a stream (that is, deserializes it).
         * @param s the stream
         * @throws ClassNotFoundException if the class of a serialized object
         *         could not be found
         * @throws IOException if an I/O error occurs
         */
        @java.io.Serial
        private void readObject(java.io.ObjectInputStream s)
            throws IOException, ClassNotFoundException {
            // Read in the threshold (ignored), loadfactor, and any hidden stuff
            s.defaultReadObject();
            reinitialize();
            if (loadFactor <= 0 || Float.isNaN(loadFactor))
                throw new InvalidObjectException("Illegal load factor: " +
                                                 loadFactor);
            s.readInt();                // Read and ignore number of buckets
            int mappings = s.readInt(); // Read number of mappings (size)
            if (mappings < 0)
                throw new InvalidObjectException("Illegal mappings count: " +
                                                 mappings);
            else if (mappings > 0) { // (if zero, use defaults)
                // Size the table using given load factor only if within
                // range of 0.25...4.0
                float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f);
                float fc = (float)mappings / lf + 1.0f;
                int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ?
                           DEFAULT_INITIAL_CAPACITY :
                           (fc >= MAXIMUM_CAPACITY) ?
                           MAXIMUM_CAPACITY :
                           tableSizeFor((int)fc));
                float ft = (float)cap * lf;
                threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ?
                             (int)ft : Integer.MAX_VALUE);
    
                // Check Map.Entry[].class since it's the nearest public type to
                // what we're actually creating.
                SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Map.Entry[].class, cap);
                @SuppressWarnings({"rawtypes","unchecked"})
                Node<K,V>[] tab = (Node<K,V>[])new Node[cap];
                table = tab;
    
                // Read the keys and values, and put the mappings in the HashMap
                for (int i = 0; i < mappings; i++) {
                    @SuppressWarnings("unchecked")
                        K key = (K) s.readObject();
                    @SuppressWarnings("unchecked")
                        V value = (V) s.readObject();
                    putVal(hash(key), key, value, false, false);
                }
            }
        }

    hash方法

    文件路径:....JAVAlibsrcjava.basejavautilHashMap.java
    然后跟进到hash方法,如果key的值等于null返回0,否则就返回key.hashCode与(h>>>16)异或后的结果
    h>>>16:无符号右移16位
        static final int hash(Object key) {
            int h;
            return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
        }

    hashCode方法

    文件路径:....JAVAlibsrcjava.basejava etURL.java
    在URLDNS.java中可以看到我们传入的key其实是一个URL对象,所以我们可以看URL.java中的hashCode方法,如果hashCode不等于-1,就返回hashCode,否则执行handler.hashCode(this)
        public synchronized int hashCode() {
            if (hashCode != -1)
                return hashCode;
    
            hashCode = handler.hashCode(this);
            return hashCode;
        }
    handler是定义的URLStreamHandler字段,跟进到URLStreamHandler.java中,这里简单来说就是解析URL的各个部分,贴一段网上的简单介绍
    “一般而言,URL 的格式是: protocol://[authority]hostname:port/resource?queryString 。 URL 类能够解析出 protocol、 hostname 、 port 等信息。 Protocol 决定了交互规范,通用的协议,比如 HTTP 、 File 、 FTP 等协议, JDK 自带了默认的通讯实现。当然,自定义实现是允许的。 Hostname 和 port 一般用于 Socket 或者基于 Socket 其他协议通讯方式。Resource 即资源上下文。可能读者利用 URL ,通过指定协议( protocol )来获取指定资源的读写,比如 JDK 内置了HTTP 、 File 、 FTP 等协议的处理方法。”
    然后在第18行,调用了getHostAddress方法
    文件路径:....JAVAlibsrcjava.basejava etURLStreamHandler.java
        /**
         * Provides the default hash calculation. May be overridden by handlers for
         * other protocols that have different requirements for hashCode
         * calculation.
         * @param u a URL object
         * @return an {@code int} suitable for hash table indexing
         * @since 1.3
         */
        protected int hashCode(URL u) {
            int h = 0;
    
            // Generate the protocol part.
            String protocol = u.getProtocol();
            if (protocol != null)
                h += protocol.hashCode();
    
            // Generate the host part.
            InetAddress addr = getHostAddress(u);
            if (addr != null) {
                h += addr.hashCode();
            } else {
                String host = u.getHost();
                if (host != null)
                    h += host.toLowerCase().hashCode();
            }
    
            // Generate the file part.
            String file = u.getFile();
            if (file != null)
                h += file.hashCode();
    
            // Generate the port part.
            if (u.getPort() == -1)
                h += getDefaultPort();
            else
                h += u.getPort();
    
            // Generate the ref part.
            String ref = u.getRef();
            if (ref != null)
                h += ref.hashCode();
    
            return h;
        }

    getHostAddress方法

    文件路径:....JAVAlibsrcjava.basejava etURLStreamHandler.java
    跟进到getHostAddress方法,在第19行根据主机名确定主机的IP地址,这就相当于一次DNS查询
        /**
         * Get the IP address of our host. An empty host field or a DNS failure
         * will result in a null return.
         *
         * @param u a URL object
         * @return an {@code InetAddress} representing the host
         * IP address.
         * @since 1.3
         */
        protected synchronized InetAddress getHostAddress(URL u) {
            if (u.hostAddress != null)
                return u.hostAddress;
    
            String host = u.getHost();
            if (host == null || host.isEmpty()) {
                return null;
            } else {
                try {
                    u.hostAddress = InetAddress.getByName(host);
                } catch (UnknownHostException ex) {
                    return null;
                } catch (SecurityException se) {
                    return null;
                }
            }
            return u.hostAddress;
        }
    总结一下攻击链大概的流程就是:
    readObject()-->putval()-->hash()-->hashCode()
    URLStreamHandler-->hashCode()-->getHostAddress()-->getByName()

    附:

    putval方法
        /**
         * Implements Map.put and related methods.
         *
         * @param hash hash for key
         * @param key the key
         * @param value the value to put
         * @param onlyIfAbsent if true, don't change existing value
         * @param evict if false, the table is in creation mode.
         * @return previous value, or null if none
         */
        final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                       boolean evict) {
            Node<K,V>[] tab; Node<K,V> p; int n, i;
            if ((tab = table) == null || (n = tab.length) == 0)
                n = (tab = resize()).length;
            if ((p = tab[i = (n - 1) & hash]) == null)
                tab[i] = newNode(hash, key, value, null);
            else {
                Node<K,V> e; K k;
                if (p.hash == hash &&
                    ((k = p.key) == key || (key != null && key.equals(k))))
                    e = p;
                else if (p instanceof TreeNode)
                    e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
                else {
                    for (int binCount = 0; ; ++binCount) {
                        if ((e = p.next) == null) {
                            p.next = newNode(hash, key, value, null);
                            if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                                treeifyBin(tab, hash);
                            break;
                        }
                        if (e.hash == hash &&
                            ((k = e.key) == key || (key != null && key.equals(k))))
                            break;
                        p = e;
                    }
                }
                if (e != null) { // existing mapping for key
                    V oldValue = e.value;
                    if (!onlyIfAbsent || oldValue == null)
                        e.value = value;
                    afterNodeAccess(e);
                    return oldValue;
                }
            }
            ++modCount;
            if (++size > threshold)
                resize();
            afterNodeInsertion(evict);
            return null;
        }

    参考文章:

    https://www.cnblogs.com/ph4nt0mer/p/11994384.html
  • 相关阅读:
    课堂作业(类与对象)
    课堂作业(方法)
    java课堂小作业
    读《大道至简》第二章有感
    课堂作业
    读《大道至简》第一章有感
    "Gun N' Rose" Team学习心得
    Python+Selenium框架版(四)- 框架内封装日志类和浏览器引擎类
    Python+Selenium进阶版(三)- 什么是POM
    Python+Selenium框架版(二)- 简单介绍unittest单元测试框架
  • 原文地址:https://www.cnblogs.com/Excellent-person/p/14165132.html
Copyright © 2011-2022 走看看