ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MySQL UDF与启动项提权:从原理剖析到检测加固实践

MySQL UDF与启动项提权:从原理剖析到检测加固实践 我维护过不少业务数据库也在应急响应里翻过好几回车所以看到“mysql数据库UDF、启动项提权”这个组合第一反应就是这又是一个典型的“数据库权限放大”套路。MySQL在很多人眼里就是个存数据的容器但一旦让它跟UDF、启动项挂上钩事情就完全不一样了——数据库账号可能变成系统命令执行低权限进程可能变成管理员权限。这篇东西我不打算教你怎么去打而是从原理、条件、排查、加固四个角度把这两种提权方式彻底拆开让做运维、做安全、做开发的同学都能看懂攻击路径长什么样以及真正遇到的时候该往哪儿查。1. 提权原理拆解为什么MySQL成了提权跳板1.1 提权本质从“库内操作”到“系统命令”很多人第一次接触提权只觉得是个很高深的词。其实本质特别简单你本来只能操作数据库里的表现在想办法让系统帮你执行外部命令你本来是个普通权限用户现在想办法让系统给你管理员权限。MySQL本身并不存在“提升权限”这种说法它只是一个被利用的跳板。UDF提权的核心是MySQL允许加载外部函数库也就是用户自定义函数。这类函数由C或C写成编译成soLinux或dllWindows之后放进MySQL的插件目录再用CREATE FUNCTION注册成库内函数。正常业务场景下DBA会用这种方式扩展数据库能力比如写一个自定义聚合函数做特殊统计但同样的机制也可以被用来构造一个能执行系统命令的函数。你可以这么理解MySQL原本只听得懂SQLUDF相当于往数据库里塞了一个“翻译官”让系统命令也能被数据库调起来。启动项提权的逻辑更简单。Windows系统登录后会自动加载启动目录和注册表启动项里的内容如果当前账号对这些位置有写入权限就相当于拥有了“系统登录后自动执行程序”的能力。低权限拿到这种写权限后就可以放一个脚本或可执行文件进去等管理员登录、服务重启、机器重启时代码就跟着跑起来了。数据库在这里的作用往往是“投递工具”——通过MySQL的文件写入能力或者UDF命令执行能力把恶意文件送到启动目录里。这两条路径本质上是同一个思维先找到一个能“写文件/执行命令”的入口再把权限放大到系统层。理解了这层后面所有细节都顺了。1.2 适用场景与判断条件不是所有MySQL都能玩UDF我在实际排查里见过不少误判有人一看到MySQL就说“可以UDF提权”其实条件相当苛刻。UDF提权要成立至少得同时满足几个硬性条件拿到了MySQL的root权限或者至少拥有FILE和INSERT权限。知道插件目录的位置且该目录对MySQL进程可写。secure_file_priv参数没做严格限制默认允许导出文件到任意目录。目标MySQL版本支持加载外部函数且目标系统允许写入so/dll文件。这几个条件缺一不可。secure_file_priv如果被设置为空字符串或NULL导出功能受限UDF文件根本放不进插件目录插件目录只读就算有SQL权限也白搭MySQL 8.0之后部分版本对自定义函数的校验更严格加载路径和符号解析都可能出问题。启动项提权相比之下“门槛低一些”但它依赖的是操作系统层面的权限判断——当前用户能不能写启动文件夹、能不能写注册表Run键。很多情况下即便拿下了数据库权限也不代表就有系统目录的写权限这就涉及到服务运行账号到底是什么身份。MySQL服务如果以普通权限运行它写不进管理员用户的启动目录如果以SYSTEM身份运行那写什么位置基本都是畅通无阻的。这也是我在排查时最关心的一点先搞清楚服务跑在哪个账号下面。2. UDF机制与启动项注册机制深度解析2.1 UDF到底怎么工作一个插件引发的系统调用要搞清楚UDF提权先得明白MySQL的插件加载机制。MySQL从很早就支持在运行时加载外部函数库函数库文件被放在plugin_dir参数指定的目录下通过CREATE FUNCTION命令把库里的某个符号注册成SQL函数。这个机制的初衷是让开发者扩展MySQL的能力比如实现自定义的排序规则、聚合函数、全文解析器等等。问题出在“扩展能力”的边界上。如果注册的函数只是做一些纯计算那最多算逻辑扩展但如果函数内部直接调用了system()或popen()这类系统接口它就从“计算函数”变成了“命令执行器”。注册这样的函数其实就是把操作系统的命令框搬到了SQL语法里SELECT一条语句就能让目标机执行任意命令。这里有个关键点UDF的dll/so文件必须放在插件目录里MySQL启动或调用时才会去加载。而MySQL只要具备FILE权限就能用SELECT ... INTO DUMPFILE把数据写入到指定路径——于是“上传UDF文件”这个动作理论上可以完全通过SQL语句完成不需要额外的文件传输通道。这就是为什么数据库提权特别喜欢UDF入口窄但一旦打通链路是完整的。另一个容易忽略的细节是UDF文件本身要跟MySQL版本和操作系统匹配。32位/64位、编译时的glibc版本、MySQL版本差异都会影响加载是否成功。实战中经常出现UDF文件传上去了但函数创建失败的情况多半就是版本或位数不对。这也是我建议做防御时重点监控plugin_dir新增文件的原因——这类文件往往特征明显时间戳可疑、文件名不按惯例、大小异常一眼就能抓出来。2.2 启动项机制Windows登录自启的几类位置启动项提权的“启动项”不是一个单一位置而是Windows系统里所有能在登录时自动加载程序的机制统称。排查时必须把下面几类位置都过一遍漏一个就可能有漏网之鱼。第一类是启动文件夹。系统级启动目录对所有用户生效用户级启动目录只对当前用户生效。只要能写入这些目录放进去的exe、bat、vbs、ps1脚本会在对应用户登录时自动运行。普通用户对自己的启动目录天然有写权限所以如果能控制一个普通用户身份的进程这一步几乎无门槛。第二类是注册表启动项。重点看HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run和RunOnce以及HKEY_LOCAL_MACHINE下的对应键。RunOnce的特点是执行后会自动删除隐蔽性很强Run键则是每次登录都会执行。攻击者经常用reg add或者通过脚本修改这里指向一个位于临时目录或隐藏目录的payload。第三类容易被忽略的是计划任务和服务。计划任务可以设置“用户登录时触发”或“系统启动时触发”而且还能指定以最高权限运行隐蔽性和可控性比启动文件夹更强。服务则通过注册服务的方式实现自启服务路径如果被篡改系统启动时就会加载恶意dll或exe。我排查启动项时会把上面四类全部查完再结合进程命令行、父进程链和网络连接一起看。单纯看启动项文件列表经常只能看到表象真正的payload早就被释放到别的目录启动项里只是一句简短的加载指令。2.3 为什么这两个套路经常组合使用UDF和启动项看起来是两条独立的路但在实际攻击链条里经常串在一起。MySQL UDF负责“执行系统命令”但这个命令是在MySQL进程上下文里跑的如果想提升到管理员权限或者获得更稳定的控制就需要启动项这样的持久化手段配合。举个例子拿到MySQL root权限后通过UDF写入一个系统命令把payload投递到启动目录或者注册表Run键然后等待管理员登录或重启机器。这样即便数据库被发现、账号被改、插件被删恶意代码已经通过启动项“生根”了。这就是典型的“打入一个点扩散到一片”的思路。反过来讲做防御的人必须把这两个点同时看住。只盯着UDF的dll文件不关注启动项等于把后门的前半段堵住了后半段仍然活跃只盯着启动项不关注MySQL插件目录下次人家换个方式又能写进来。3. 现场排查如何发现UDF和启动项后门3.1 快速定位MySQL是否被植入UDF排查UDF后门第一步不是去翻文件系统而是先查MySQL本身。我习惯按照下面的顺序来先查函数注册表。运行SELECT * FROM mysql.func看看有没有异常的函数名。正常业务场景下自定义函数的数量很少名字也通常是跟业务相关的如果看到一个叫sys_exec、sys_eval、cmd_exec之类明显跟命令执行相关的函数基本可以断定有问题。有些攻击者会故意把函数改成不显眼的名字比如system_fn、tmp_x所以光看名字不够还要看dl列指向的库文件名。接着查插件目录。执行SHOW VARIABLES LIKE plugin_dir拿到目录路径然后去文件系统里看该目录下的所有文件。重点关注so/dll后缀的文件特别是那些名字跟常规插件对不上的、时间戳在入侵时间窗内的、大小在几十KB到几百KB之间的文件。我之前见过一种隐蔽做法不新建文件而是复用系统自带的合法插件库但这种操作难度高实际遇到的不多。再查日志和审计记录。MySQL的binlog里如果有CREATE FUNCTION的记录能直接定位到植入时间和来源IP。general_log如果开着甚至能看到当时执行的完整语句。很多被入侵的库general_log是关闭的那就只能靠文件时间戳和进程活动来倒推了。最后看MySQL进程加载了哪些库。Linux下可以看/proc/ /mapsWindows下可以用Process Explorer或listdlls把进程实际加载的dll/so列出来对比插件目录。有些攻击者会把恶意UDF文件放在临时目录或/tmp下如果MySQL进程加载了插件目录之外的so文件就要格外留意了。3.2 启动项后门的完整排查路径排查Windows启动项后门我一般按照从易到难的层次来避免一开始就陷进杀毒软件的误报里。第一步看启动文件夹。分别检查系统启动目录和用户启动目录把目录下所有文件列出来用文件签名和hash比对的方式过滤一遍。正常的启动项基本是厂商的更新程序、输入法、同步盘之类名字清楚、路径固定可疑样本往往文件名随机、路径在AppData或Temp下、数字签名缺失或异常。第二步查注册表启动项。reg query把HKCU和HKLM下的Run、RunOnce都列出来再看Winlogon的Userinit和Shell值有没有被改动。这里有个细节要特别注意RunOnce会在执行后删除自己单纯看当前状态可能什么都看不到需要结合注册表事务日志或者备份软件的快照来分析。第三步查计划任务和服务。计划任务用schtasks /query /fo LIST /v查看重点找触发条件为“登录时”或“启动时”的任务尤其是那些“创建者”不是系统管理员、可执行文件路径在用户目录下的。服务部分用services.msc配合命令行查重点看ImagePath是否被改成了非系统目录下的可执行文件。第四步查持久化关联项。WMI事件订阅、COM劫持、AppInit_DLLs这类高级持久化手段在真实入侵中也会出现但频率比启动项低很多。先快速的启动项和计划任务如果没发现明显问题再往这些深层位置追。我排查时遵循一个原则优先找“正在运行的可疑进程”再倒推它的启动来源比纯看静态配置更高效。3.3 检测中的关键特征和踩坑经验UDF和启动项后门都有一些共性特征可以作为入侵指标来用。插件目录出现新文件且时间戳与业务变更时间对不上。mysql.func表出现来源不明的函数dl列指向的不是常规库名。MySQL进程加载了非插件目录的so/dll。登录后短时间内出现陌生进程父进程是explorer.exe或winlogon.exe。注册表Run键或计划任务指向的文件不在系统目录下且文件签名缺失。同一时间段内数据库出现大量SELECT ... INTO DUMPFILE操作或者文件写入行为。踩过的坑也顺便说下。有一次排查时杀毒软件把启动目录里的某输入法升级助手报成风险项差点误导了方向实际上真正的后门藏在计划任务里文件用了白加黑手法加载了一个有签名的合法exe恶意dll是后放的。所以只看文件名、只看签名都不可靠必须结合行为链条来判断。另一次是MySQL服务器上发现了sys_eval函数但查binlog没有记录后来才发现攻击者手工删除了binlog文件最后是靠插件so文件的时间戳跟网站日志的访问时间交叉比对才锁定的入侵时间点。4. 加固与常见问题避坑清单4.1 我建议的数据库侧加固方案防UDF提权最核心的是收窄“文件写入”和“插件加载”两条路。secure_file_priv设为空字符串且指向一个受控目录是很多安全基线的标配但注意这个参数在MySQL 5.7之后的版本才支持目录级别的限制老版本可能只能设NULL来禁用导入导出。如果业务确实需要LOAD DATA那就保持目录绑定的方式绝对不要设为空。FILE权限的回收也非常重要。MySQL的root账号常年裸奔是很多企业的通病业务账号根本不需要FILE权限就应该从授权里拿掉。我还建议把mysql.func表的DML权限单独管控只允许DBA账号操作防止普通账号注册恶意函数。插件目录方面Linux下我把plugin_dir的属主改成root权限设为755这样MySQL进程只能读不能写Windows下要单独给插件目录设置ACL禁用NETWORK SERVICE或LOCAL SERVICE的写入权限。另外开启general_log或审计插件定期回溯对检测UDF植入有奇效。4.2 针对启动项的加固建议系统侧的加固比数据库侧复杂因为启动项本身就是Windows的正常机制不能一刀切禁用。我常用的做法是通过组策略或安全基线关闭不必要的自动播放、脚本执行降低payload被自动启动的概率。用Sysmon或EDR监控注册表Run键、启动文件夹、计划任务的新增/修改事件实时告警。对启动目录和Run键设置更严格的ACL普通用户对HKLM的Run键只读对系统启动目录只读。定期导出计划任务列表对比基线版本新增任务要人工确认。这些措施不能完全阻止提权但能把攻击者的成本拉高好几个档次。数据库被拿下后如果还要费劲绕过Sysmon的监控和ACL限制很多攻击者会直接放弃这个点。4.3 常见问题速查与排错思路我在帮朋友排查时经常遇到一些重复出现的问题整理成表格方便查阅问题现象可能原因处理思路CREATE FUNCTION提示already exists函数名已被占用先查mysql.func表确认是业务函数还是后门插件so/dll文件无法加载版本位数不匹配或依赖缺失用ldd查看依赖库确认glibc版本一致UDF执行命令无回显函数用的是sys_exec而不是sys_eval改为sys_eval或通过写入临时文件回显启动项文件被杀软查杀payload特征太明显从历史快照中恢复文件重新分析行为链计划任务看不到异常但机器已被控制使用了WMI或COM持久化查WMI事件订阅和COM对象劫持项注册表Run键被改但找不到恶意文件文件被删除或下载器模式检查网络请求和下载器后续行为binlog没有CREATE FUNCTION记录攻击者清理了日志靠文件时间戳、插件目录文件、进程快照交叉定位UDF提权失败的原因排第一位的是secure_file_priv做了限制插件文件根本写不进去第二位的是MySQL进程对plugin_dir没有写权限第三位的是UDF文件跟目标系统位数/版本不匹配。很多人一上来就找编译环境的问题其实前面几个更基础的条件没过才是常态。启动项提权失败的原因最常见的是目标用户跟当前运行权限不一致——你写的是普通用户的启动目录但管理员根本不登录这个账号其次是UAC虽然很多时候不会拦启动项但某些高完整性级别的payload会直接触发权限提升弹窗还有杀软实时防护在文件落地的瞬间就清掉了。所以我一直强调技术细节再熟环境适配和权限上下文才是决定成败的关键。最后分享几个个人体会。排查这类问题时一定要以时间线为核心去梳理入侵总会在某个层面留下痕迹平时就做好基线采集服务器上什么东西是正常的、新增了什么文件有基线才能谈异常。我见过太多被入侵后无从下手的案例不是没留日志而是根本没建立正常状态的参照物。安全这件事防守方要做的不是把所有漏洞堵死而是让攻击者的每一步都留下痕迹让每一次提权都需要付出代价。这个思路比你学会一万种利用技巧都管用。
返回列表