数据库提权这事,说起来挺有意思。很多人第一次接触渗透测试,看到“提权”两个字就觉得是高深莫测的漏洞利用,但真上手之后才发现,大部分情况下比的不是谁掌握的黑魔法多,而是谁更理解目标系统本身的“权限设计逻辑”。尤其是数据库,它几乎可以说是整个权限提升链条里最容易被低估的一环。这篇东西我把自己在授权测试里常用的命令和判断思路整理出来,不聊虚的,全是能落到终端里跑一跑、能帮你把“下一步怎么走”想清楚的干货。不管你是刚入行的安全新人,还是被叫去救火的运维老哥,只要你的工作里会碰到数据库这台“权限中转站”,这篇文章都值得你花十分钟认真看一遍。
先说清楚一个前提:所有内容都建立在你有明确授权、或者在自己的实验环境里操作的基础上。没有授权去碰别人的库,那不是渗透测试,是违法,这点没有任何模糊空间。下面聊的所有命令和思路,更多是帮你理解攻击者会怎么想、怎么看,从而知道该在哪些地方堵住缺口。
1. 数据库提权到底在提什么权:先把核心问题想清楚
1.1 为什么数据库会成为权限提升的重点目标
我在之前的文章里反复提过一个观点:渗透测试里,真正决定成败的往往不是那个最炫的漏洞,而是攻击路径上有没有一个“权限放大器”。数据库就是最典型的权限放大器。
原因其实不复杂。首先,数据库是几乎所有业务系统的核心,它必须被应用服务器连接、被运维人员管理、被备份系统读取,所以它天然拥有很多“合法”的高权限入口。其次,很多数据库服务进程本身的运行权限就非常高——Windows下SQL Server常以LocalSystem或NetworkService运行,Linux下MySQL也可能因为历史原因被配置成root启动,这就意味着,一旦你能在数据库里执行命令,你执行的命令实际上带着这个高权限进程的身份。
更关键的是,数据库系统本身提供了大量“扩展能力”:自定义函数、存储过程、CLR程序集、外部脚本、文件读写……这些功能设计的初衷是方便开发者,但在安全视角下,每一个都是通往操作系统的潜在跳板。攻击者拿到一个脆弱的数据库账号,未必能直接读到敏感数据,但如果他能借助这些扩展功能把能力放大到操作系统层面,后续的横向移动和数据窃取就只是时间问题了。
这里可以打个比方。数据库就像大楼的物业管理办公室,表面上你进去只能查点水电记录,但办公室里放着整栋楼的设备图纸、钥匙卡和维修工具。如果物业人员安全意识差,把钥匙卡乱放,那一个本来只该在门厅活动的人,就可能顺着办公室里的工具间一路摸到核心机房。加固数据库,本质就是在管理这间“物业办公室”的钥匙。
1.2 提权路径的本质:从数据权限到操作系统权限
理解了“权限放大器”这个定位,再看提权路径就会发现,不管是什么数据库,路径骨架都出奇一致。
通常的路径是这样的:先是外围突破,攻击者通过Web漏洞、弱口令、备份文件泄漏等方式拿到一个数据库账号;然后是这个账号本身的权限扩张,从只能查数据,到能写文件、能创建函数、能执行系统命令;最后一步是数据库服务进程权限向系统权限的跨越,比如借助服务账号过高权限直接拿到系统Shell甚至域管理员权限。
这里面有三个关键节点值得注意。第一个节点是“从查询到写入”,也就是说攻击者需要获得FILE权限或者能操作文件系统的能力,这是从数据库内部走向外部的物理前提。第二个节点是“从写入到执行”,比如往插件目录写入动态链接库并创建自定义函数,或者启用xp_cmdshell这样的扩展存储过程,这一步完成了从数据操作到命令执行的跨越。第三个节点是“从普通命令到高权限命令”,这一步往往不取决于数据库本身,而取决于数据库服务进程是以什么身份在跑——如果服务本身就是SYSTEM,那执行命令直接就拿到了系统最高权限。
防御方要做的,不是在这三个节点上全部严防死守——那几乎不可能——而是选择成本最低、效果最好的那一两个节点重点布防。比如禁止DIRECTORY写权限、禁用扩展存储过程、确保服务账号最小权限,只要砍断其中一环,这条链就断了。
1.3 一个必须划清的边界:授权测试与非法入侵
写到这里必须停下来,把边界说透。网上很多“提权命令大全”“数据库提权速查”之所以让人反感,是因为它们把攻击步骤写得像菜谱一样,好像对着任何一台机器都能来一遍。但真正的安全从业者都清楚,没有授权边界的“测试”就是不折不扣的攻击。
我在实际工作中见过不少因为边界模糊翻车的案例。有人觉得自己只是“练练手”,找了个公网IP试了试,结果对方系统当场宕机,最后被请去喝茶;也有人明明只被授权测试某个Web应用,却顺手把内网的数据库全部扫了一遍,导致客户直接终止合作并报警。所以无论你看到多少命令、多少技巧,先问自己一句:这台机器我有没有权碰?我的测试范围和动作边界在哪里?
这篇文章里出现的所有命令,在自有环境或者授权测试里怎么跑都行,但拿到生产系统上,每一个命令都可能变成呈堂证供。这是底线,没有商量的余地。
2. 常见数据库提权路径与原理解读:先懂原理,再谈命令
2.1 MySQL:自定义函数(UDF)与文件读写权限是最经典的跳板
MySQL的提权路径里,最经典、也最常被提到的就是UDF(User Defined Function,用户自定义函数)提权。这个机制的初衷是让开发者能自己编写函数扩展MySQL的功能,但问题在于,MySQL加载UDF时需要往插件目录写入动态库文件,如果数据库账号具有FILE权限,且插件目录对MySQL进程可写,那就等于给攻击者开了一扇直接通往系统命令执行的大门。
实际操作中,攻击者的思路大致是这样:先拿到MySQL账号并确认其权限;然后查看插件目录位置和secure_file_priv参数限制;接着尝试通过SELECT ... INTO OUTFILE把构造好的动态库文件写入插件目录;最后创建自定义函数,调用它执行系统命令。整个过程每一步都不复杂,但每一步的基础都是数据库自身把权限放得太宽。
作为防御者,你需要知道几个关键的查询命令。比如查看当前账号权限用 SHOW GRANTS FOR CURRENT_USER();,查看插件目录用 SHOW VARIABLES LIKE 'plugin_dir';,查看文件读写限制用 SHOW VARIABLES LIKE 'secure_file_priv';。这些命令在授权测试中也能帮助你快速判断目标的暴露面有多大。
顺带提一句,MySQL还有一个容易被忽略的高风险点:general_log 和 slow_query_log 的日志文件写入。在旧版本或配置不当的情况下,攻击者可以尝试把日志文件路径指向Web目录,再往日志里写入恶意内容,达到getshell的效果。这个思路与UDF不同,但对配置检测的要求更高,实战中也更隐蔽。
2.2 SQL Server:xp_cmdshell与CLR程序集的“权力通道”
SQL Server的提权故事里,xp_cmdshell是绕不开的角色。这个扩展存储过程的作用就是直接执行操作系统命令,它的本意是给管理员提供方便,但由于历史版本里权限控制不严格,一旦数据库账号具备sysadmin角色,就能轻松启用并调用它执行任意命令。
除了xp_cmdshell,CLR(公共语言运行时)程序集是另一个常见路径。SQL Server允许注册.NET程序集作为存储过程或函数,这意味着只要数据库账号有CREATE ASSEMBLY权限,就可以把一个自己写的.NET代码注册进去,然后在SQL里调用它执行任意操作,包括启动进程、访问文件系统、发起网络请求等。相比xp_cmdshell,CLR的隐蔽性更好,因为它不依赖系统预置组件,而是把恶意逻辑打包成“合法”的数据库对象。
检测方面,你可以用SQL语句直接查系统配置:SELECT name, value_in_use FROM sys.configurations WHERE name = 'xp_cmdshell';,也可以查可疑的CLR程序集:SELECT * FROM sys.assemblies;。在授权测试中,这些命令能快速确认SQL Server是否开启了高危功能,也能帮助你在应急响应时判断这台机器是否已经被“照顾”过。
这里要特别强调一个认知:xp_cmdshell本身不是漏洞,它是功能。真正的问题在于,这个功能被暴露给了不必要的账号,而且服务进程往往以过高的权限运行。所以加固SQL Server的关键不是见到xp_cmdshell就删,而是确保只有真正的管理员才能接触这些能力,服务账号尽可能用最小权限的虚拟账号。
2.3 Oracle与PostgreSQL:Java存储过程与扩展模块是另一片战场
相比MySQL和SQL Server,Oracle和PostgreSQL的提权路径往往更容易被新手忽略,但它们在实际攻防里一点都不少见。
Oracle数据库的提权手段不少,常见的有利用Java存储过程。Oracle内置了Java虚拟机,允许在数据库里执行Java代码,如果攻击者获得了创建Java存储过程的权限,就能借此调用Runtime.exec()执行系统命令。这个过程不需要向文件系统写任何东西,完全在数据库内部完成,隐蔽性很强。另一个方向是利用外部表(External Table)读取操作系统文件,尤其是读取Linux下的/etc/passwd、Web配置等敏感文件。
PostgreSQL的话,最常被人提起的是COPY命令和自定义函数(CVE-2019-9193等历史问题)。默认情况下,数据库账号如果具备超级用户权限,可以借助COPY ... FROM PROGRAM直接执行系统命令。这个思路极其直接,一条SQL就能完成从数据库到系统的跨越。PostgreSQL还有一个特性是支持PL/pgSQL语言,可以编写触发器和函数,在特定条件下执行任意代码,这在权限维持场景中很常见。
所以如果你在授权测试里碰到Oracle或PostgreSQL,千万别只盯着数据库版本和漏洞库,先看看账号角色、函数、触发器、扩展模块这些“常规功能”有没有被滥用。很多时候,真正的风险恰恰来自这些“合法能力”。
2.4 国产数据库与容器化场景:新瓶装旧酒,原理还是那一套
这些年用达梦、人大金仓、GaussDB等国产数据库的政企项目越来越多,很多安全新人会下意识觉得“国产库应该安全些吧”。但接触过之后你就知道,数据库的权限设计逻辑是共通的,换了个壳不代表内核思路就变了。
以达梦为例,它兼容Oracle和MySQL的部分语法,同样支持存储过程、自定义函数、外部文件读写等能力。有些版本在默认配置下,数据库账号对某些目录的访问权限控制并不比商业数据库更严格,这其实就是老熟人换了张新面孔。容器化环境也一样,MySQL跑在Docker里,未必就比裸机更安全——容器内的权限隔离确实削弱了一部分提权路径,但也带来了新的风险面,比如数据库文件挂载目录的权限、容器逃逸、以及镜像里预置的高危配置。
在测试时,我先用常规思路把数据库的权限地图画出来,再根据具体产品文档补充它特有的功能点。国产库往往有更细的“模式”“表空间”“包”概念,搞清楚这些之后,很多提权路径的本质就清晰了。
3. 授权环境下的实操:一份可参考的检测与验证命令清单
3.1 先把实验环境搭起来:没有靶场,一切命令都是纸上谈兵
如果你打算把下面这些命令亲手跑一遍,第一件事不是找目标,而是搭一个完全可控的本地环境。我的习惯是准备两台虚拟机,一台装Windows Server并部署SQL Server,另一台装Linux并部署MySQL和PostgreSQL,网络模式设为仅主机或者NAT加防火墙拦截出站,确保实验环境与真实生产网络彻底隔离。
安装时要注意,数据库版本尽量选接近你在实际工作中会遇到的版本,因为不同版本的安全配置和默认权限差异很大。装好之后记得先做快照,方便随时回滚。我见过不少人在自己环境里测试时把数据库搞坏了,没有快照只能重装,浪费时间。
搭建过程中,你顺便可以体验一下“默认配置有多危险”。比如MySQL刚装完时root账号是空密码还是随机密码,SQL Server的sa账号是否启用了混合认证,这些细节稍后都会变成测试的突破口。
3.2 核心命令块一:MySQL下怎么快速摸清权限底牌
连接上MySQL之后,我通常会按顺序执行下面这组命令,把数据库的权限地图先画出来:
-- 查看当前登录用户和主机 SELECT current_user(), user(); -- 查看当前用户拥有的权限 SHOW GRANTS FOR CURRENT_USER(); -- 查看全局文件读写限制 SHOW VARIABLES LIKE 'secure_file_priv'; -- 查看插件目录位置 SHOW VARIABLES LIKE 'plugin_dir'; -- 查看MySQL版本 SELECT version(); -- 查看已有的自定义函数 SELECT * FROM mysql.func; -- 查看是否开启日志文件写入 SHOW VARIABLES LIKE 'general_log'; SHOW VARIABLES LIKE 'slow_query_log';这些命令本身非常基础,没有任何破坏性,但在授权测试时却能让你快速判断几个关键问题:当前账号有没有FILE权限(决定能否读写文件)、secure_file_priv是空还是NULL还是指定目录(决定文件能写到哪)、插件目录在哪(决定UDF利用是否可行)、mysql.func里有没有可疑函数(判断目标是否已经被打过)。您可以看到,信息收集是整个提权判断的第一步,也是最关键的一步。
3.3 核心命令块二:SQL Server里检测高危配置和扩展存储过程
SQL Server这边,我常用的检测命令集中在系统视图和系统配置上:
-- 查看当前登录账号和服务器角色 SELECT SUSER_SNAME(); SELECT IS_SRVROLEMEMBER('sysadmin'); -- 查看xp_cmdshell是否启用 SELECT name, value_in_use FROM sys.configurations WHERE name = 'xp_cmdshell'; -- 查看所有扩展存储过程(重点关注有命令执行能力的) SELECT * FROM sys.all_objects WHERE type = 'X' ORDER BY name; -- 查看数据库中的CLR程序集 SELECT * FROM sys.assemblies; -- 查看所有数据库账号及角色 SELECT name, type_desc FROM sys.database_principals;这里要提醒一点:sys.configurations里的value_in_use为1只代表当前运行状态已启用,不代表配置已经持久化。在测试环境中如果你想验证XP_cmdshell执行的边界,可以用sp_configure开启后再关闭,操作完后务必确认已经复原。在生产环境里,这类操作哪怕只是临时开启,也要经过审批并留下记录,不能随手就做。
对于CLR程序集,我见过不少运维根本不知道自己的库里有第三方程序集,更不知道这些程序集是干嘛的。所以排查CLR时,遇到看不懂名字的、来源不明的程序集,宁可先把它禁掉,也不要留着当定时炸弹。
3.4 核心命令块三:Oracle与PostgreSQL的信息收集命令
Oracle和PostgreSQL的权限体系各有一套逻辑,我简单列一下我在测试时常用的查询:
Oracle侧:
-- 查看当前用户 SELECT user FROM dual; -- 查看当前用户系统权限 SELECT * FROM session_privs; -- 查看用户角色 SELECT * FROM user_role_privs; -- 查看Java存储过程 SELECT object_name, object_type FROM user_objects WHERE object_type LIKE '%JAVA%'; -- 查看外部表 SELECT table_name FROM user_tables WHERE table_name IN (SELECT table_name FROM user_external_tables);PostgreSQL侧:
-- 查看当前用户和权限 SELECT current_user; SELECT * FROM information_schema.role_table_grants WHERE grantee = current_user; -- 查看是否超级用户 SELECT rolsuper FROM pg_roles WHERE rolname = current_user; -- 查看扩展模块 SELECT * FROM pg_extension; -- 查看自定义函数 SELECT proname FROM pg_proc WHERE pronamespace = 'public'::regnamespace;这些命令背后对应的是攻击者会看的东西:有没有Java存储过程权限、有没有外部表权限、是不是超级用户、能不能创建扩展。如果你在加固时发现某个业务账号拥有超级用户权限,或者PUBLIC角色对这些函数有执行权限,这就是一个值得立即整改的红线问题。
3.5 用日志和审计功能做一次“反向溯源”
除了主动查询配置,我还特别建议在实验环境里把数据库日志审计打开,看看一次完整的提权尝试会在数据库里留下什么痕迹。以MySQL为例:
-- 开启通用日志(仅限实验环境) SET GLOBAL general_log = 'ON'; SET GLOBAL general_log_file = '/tmp/mysql_general.log'; -- 查看binlog状态 SHOW VARIABLES LIKE 'log_bin'; SHOW BINARY LOGS;在SQL Server里可以开启审计功能,或者在测试前把默认跟踪打开。做完一次模拟测试后,回到日志里看看哪些命令被记录、哪些操作能对上号。这个过程能帮你建立“攻击行为痕迹”的敏感度,将来做应急响应时,你会更容易从海量日志里捞出关键线索。
3.6 红线自检:从防御者视角给数据库打分
最后,每次测试结束后我都会做一次“红线自检”,相当于给这台数据库的安全状态打分。打分维度包括:服务账号是否最小权限、是否有非管理员账号拥有FILE或sysadmin等高权限、高危扩展存储过程和自定义函数是否清理干净、数据库补丁是否到位、错误日志和审计日志是否定期备份并留存、数据库配置文件里有没有明文密码或过宽监听地址。
打分不是为了写报告好看,而是让自己形成一套可重复的检查习惯。你不需要一次把所有项目都做到满分,但至少要清楚自己的系统在哪些维度上扣了分,以及这些扣分项如果被攻击者利用,会造成多大影响。
4. 常见问题与排查技巧实录
4.1 明明有权限,为什么文件就是写不进去?
这个问题我很早期测试时遇过多次,明明SHOW GRANTS里带着FILE权限,SELECT ... INTO OUTFILE却总是报错。后来排查多了才发现,大概率是下面几个原因。
头号嫌疑犯是secure_file_priv参数。这个参数在MySQL 5.7及以上版本默认控制很严,如果值是NULL,那就完全禁止文件读写;如果值指定了目录,那么只能往这个目录里写,别的路径一概不行。很多测试者没意识到这个参数的存在,在/tmp或WEB目录下一顿操作,结果全部失败。第二个常见原因是操作系统目录权限,MySQL进程用户对目标目录没有写权限,这跟数据库权限无关,纯粹是文件系统层面的限制。第三个原因就要复杂一点,可能是安全防护软件或EDR对写入行为做了拦截,这种在Windows上尤其常见,杀毒软体会把dll或exe的写入视为恶意行为直接阻止。
遇到这类问题,我的习惯是先确认参数,再确认目录权限,最后才考虑是不是有安全软件拦截。排查顺序反了很容易浪费时间。
4.2 低版本数据库为什么总是提权重灾区?
原因有三层。第一,老版本数据库本身存在大量已公开但未修复的漏洞,比如老版本MySQL的UDF提权漏洞、老版本SQL Server的xp_cmdshell滥用、PostgreSQL的某些COPY命令漏洞等。这些漏洞在网上有大量现成的利用工具和详细的复现教程,攻击门槛极低。第二,老版本数据库通常运行在更老的操作系统上,操作系统本身的补丁可能也不全,数据库漏洞和系统漏洞叠加,问题就成倍放大。第三,历史遗留配置问题,很多数据库是多年前部署的,当年安全意识不强,服务账号直接用了administrator或root,各种扩展功能也开着,后期没人敢动也不敢重启,就一直带病运行。
所以遇到低版本数据库,先别想着怎么利用,把补丁清单拉出来、把服务账号权限捋一遍、把高危功能查一遍,光这“老三样”就能堵住绝大多数已知路径。
4.3 容器化数据库是不是更安全?别高兴太早
Docker里的MySQL或PostgreSQL确实比裸机多一些限制,比如容器默认不会给root权限、文件系统有写保护、网络默认隔离等。但很多团队在实际部署时为了提高便利性,往往会加--privileged参数、挂载宿主机目录、使用root用户运行容器进程,这些操作等于把容器的安全隔离亲手拆掉了。
我见过一个案例,某业务把MySQL丢在Docker里,但挂载了宿主机整个/etc目录用于“方便备份”,结果攻击者通过SQL注入拿到数据库权限后,反向读出了宿主机的SSH私钥。这种风险不是数据库本身的问题,而是使用容器的方式出了问题。所以在容器化场景下,检测重心要从数据库内部延伸到容器外——重点看挂载目录是不是敏感、容器是否以特权模式运行、进程是否以root身份运行、宿主机Docker API是否暴露到了网络上。
4.4 真实排查案例:从一次“高权限数据库账号”看应急响应的完整思路
最后分享一个我处理过的典型案例。某天客户突然发现他们一台应用服务器被上传了Webshell,排查后发现入侵者最早是从一个老旧的业务后台系统注入SQL,拿到MySQL的root权限,然后利用UDF自定义函数执行系统命令,再通过计划任务建立了持久化后门,最终用这台服务器当跳板在内网翻了一圈。
整个排查过程里,数据库相关的工作占了很大比例。我先通过慢查询日志和binlog定位到了恶意SQL语句的执行时间,再检查mysql.func表发现了新增的恶意函数,接着在val插件目录里找到了攻击者留下的动态库文件,最后顺着系统登录日志和计划任务脚本把后门清理干净。处置完成后,给客户的建议也很直接:这套系统已经无法完全信任,建议在迁移数据后彻底重建,同时把数据库升级到最新版本、停止使用高权限业务账号、删除UDF插件目录里的非白名单文件、开启审计日志并异地备份。
这个案例想说明什么?说明数据库层面的提权不是一个孤立的“技术炫技”,它是整个攻击链里非常关键的一环。只有理解这一环,防御者才知道该在哪里设卡、该保留什么证据、该整改哪些系统。
我自己这些年做下来,最大的感受是:提权命令清单这种东西,真正有用的不是那几条具体的SQL命令,而是命令背后的“权限思维”。当你拿到一个数据库连接时,能在脑子里快速画出一条从当前权限到系统权限的路径图,并且知道哪条路已经被堵死、哪条路还开着,那你无论是做攻击侧的验证,还是做防御侧的加固,都会比单纯背命令高效得多。最后再多说一句,实验环境尽量多用快照、多清理现场,测试完之后把日志、文件、临时账号都恢复原状,这既是职业习惯,也是基本的操作素养。