1 原理
Mysql服務端反向讀取客戶端的任意文件
利用LOAD DATA INFILE
這個語法,這個語法主要用於讀取一個文件的內容並且放到一個表中。
load data infile "/data/data.csv" into table TestTable;
load data local infile "/home/lightless/data.csv" into table TestTable;
第一行是讀取服務端本地的文件,第二行是讀取客戶端本地的文件
而反向讀取就是利用了第二行用法。
正常流程:
- s1 客戶端:hi~ 我將把我的 data.csv 文件給你插入到 test 表中!
- s2 服務端:OK,讀取你本地 data.csv 文件並發給我!
- s3 客戶端:這是文件內容:balabal!
當s2時,服務端返回給客戶端的不是data.csv,而是其他文件時,就會發生如下事情
- s1 客戶端:hi~ 我將把我的 data.csv 文件給你插入到 test 表中!
- s2 服務端:OK,讀取你本地的 / etc/passwd 文件並發給我!
- s3 客戶端:這是文件內容:balabal(/etc/passwd 文件的內容)!
為什么會這樣?
Mysql官方文檔:
“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 statement.”
客戶端讀取哪個文件其實不是自己說了算,是服務端說了算,也就是以s2時服務端返回的文件目標為准!
到了這里有個問題,如果客戶端不主動使用LOAD DATA INFILE,是不是就沒事了呢?
還是官方文檔:
"A patched server could in fact reply with a file-transfer request to any statement, not just LOAD DATA LOCAL"
意思就是,偽造的服務端可以在任何時候回復一個 file-transfer 請求,不一定非要是在LOAD DATA LOCAL的時候。
一些Mysql客戶端,比如python的MySQLdb和mysqlclient,php的mysqli和PDO,java的JDBC Driver以及原生mysql客戶端等等,在連接MySQL的時候,基本上在連接成功之后都會發出一些SELECT語句來查詢一些版本號、編碼之類的數據,這時就可以回復一個 file-transfer 請求,讀取客戶端上的文件。
這里有一點要說明的是,如果想要利用此特性,客戶端必須具有 CLIENT_LOCAL_FILES
屬性,所以可能要添加--enable-local-infile
。
總結一下漏洞的成因:
LOAD DATA INFILE
讀哪個文件是由服務端返回包的file-transfer 請求決定,而不是客戶端- 不管客戶端發出什么請求,只要服務端回復一個 file-transfer 請求,客戶端就會按照
LOAD DATA INFILE
的流程讀取文件內容發給服務端
總結一下整個攻擊流程:
- 受害者向攻擊者提供的服務器發起請求,並嘗試進行身份認證
- 攻擊者的MySQL接受到受害者的連接請求,攻擊者發送正常的問候、身份驗證正確,並且向受害者的MySQL客戶端請求文件。
- 受害者的MySQL客戶端認為身份驗證正確,執行攻擊者的發來的請求,通過LOAD DATA INLINE 功能將文件內容發送回攻擊者的MySQL服務器。
- 攻擊者收到受害者服務器上的信息,讀取文件成功,攻擊完成。
做個補充:
甚至不需要真的搞個Mysql服務,在mysql客戶端連接時,模仿服務端返回Server Greeting
等待 Client 端發送一個Query Package后
回復一個file transfer請求
即可讀取到文件
復現用的腳本既是這樣實現
2 條件
- 受害者的Mysql客戶端有權限讀文件
- 受害者的Mysql客戶端設置中,
local_infile
非0或連接時有用--enable-local-infile
,默認是能夠讀取的
3 利用場景
- 利用重裝漏洞,讀取目標服務器上的任意文件
- 利用目標的數據遷移等,能連接外部數據庫的功能點,讀取目標服務器上的任意文件
- 搭建在蜜罐上讀取攻擊者的信息,藍隊溯源/反制
4 復現
大佬寫好的腳本
https://github.com/allyshka/Rogue-MySql-Server
roguemysqlserver.py
的filelist
為讀取受害者的文件地址,讀Win注意路徑
python2運行roguemysqlserver.py
,服務器開放端口和防火牆即可
4.1 mysql原生client
ubuntu18/Win10
-
服務端運行
roguemysqlserver.py
-
客戶端連接
其實無需密碼即可連接上,即mysql -h
即可連接上
-
讀取文件
4.2 navicat premium12
ubuntu18/Win10
-
客戶端
-
讀取文件
4.3 公網測試
嘗試讀取多個文件時,發現原腳本所讀文件不存在時,並不是繼續讀下一個文件,而是停了下來。。。。
能力有限,不會修改,希望有大佬賜教
一晚上只有幾台機器連上來,還讀不到文件就很難受
幾天了,很多IP連進來,但是沒一個讀到的,有一上海的IP,好像不信邪,試了好幾次。。。
4.4 其他大佬寫的利用工具
https://github.com/fnmsd/MySQL_Fake_Server
https://github.com/rmb122/rogue_mysql_server
5 防御/修復
- 使用
--ssl-mode=VERIFY_IDENTITY
來建立可信的連接 - 從配置上防御
https://dev.mysql.com/doc/refman/8.0/en/load-data-local-security.html
6 參考
https://lightless.me/archives/read-mysql-client-file.html
https://xz.aliyun.com/t/3973