最近梳理 Linux 安全测试流程时发现很多朋友卡在同一个地方拿到一个低权限 shell 之后怎么都提不到 root。无论是 CTF 靶场还是已获授权的渗透测试提权都是最考验经验积累的环节。我前前后后也踩了不少坑索性把 Linux 下常用的提权思路和具体操作完整整理一遍当作一份随查随用的笔记分享出来。这篇文章适合谁看如果你是刚接触安全测试的入门者可以照着命令理解整体攻击面如果你有一定经验可以把它当成自查清单查漏补缺。所有内容都基于授权测试和 CTF 场景不要在未授权环境里尝试。1. 提权思路的整体拆解1.1 为什么提权方法值得系统梳理很多新手拿到 shell 后的第一反应是乱试到处翻目录、看版本、跑现成 exploit。运气好的话一次成功但更多时候是卡死在“内核版本对不上”或者“根本没找到可利用点”这种尴尬局面。问题出在缺少一个系统化的排查框架。提权的本质很简单找到系统里某个权限校验不严的环节把现有用户权限放大到更高权限。Linux 系统里这类环节其实很固定翻来覆去就是内核漏洞、sudo 配置、SUID 文件、定时任务、环境变量、敏感凭据这几类。如果把它们整理成一套排查流程每一步都知道该执行什么命令、该看什么输出效率会高很多。以我的经验完整的提权排查可以分为三个阶段。第一阶段是信息收集搞清楚系统是什么版本、当前用户在哪个组、有哪些可写目录、有哪些服务在跑第二阶段是逐项排查把收集到的信息对应到具体提权路径上第三阶段才是利用也是风险最高的一步。很多教程直接把第三阶段拿出来讲搞得新手以为提权就是跑个 exploit其实前两步才是决定成败的关键。我自己常用的思路是从“权限来源”入手当前用户能调用哪些高权限命令、当前用户能读写哪些高权限文件、系统里有哪些定时执行的任务、内核和关键软件存在哪些已知漏洞。按照这个顺序排查基本不会漏掉大部分常见提权点。1.2 攻击面的分类与优先级判断把常见的提权路径按利用难度和成功概率排个序心里就有数了。我平时的优先级是这样安排的优先级提权路径利用难度成功概率前置条件高sudo 配置错误低高sudo -l 有可执行项高SUID 文件利用低高存在可利用的 SUID 文件中定时任务中的可写脚本中较高cron 任务引用了可控路径中内核漏洞中波动大内核版本命中公开漏洞中环境变量劫持中中高权限程序调用可控命令低敏感凭据收集低中等系统里留存了密码或密钥先说结论sudo 和 SUID 是最优先看的方向因为这两个方向上系统管理者容易留下错误配置利用条件相对明确不需要依赖内核版本等外部因素。内核漏洞虽然威力大但成功率受系统补丁状态、防护机制影响很大实际利用时往往需要反复调试。优先级的判断背后有个朴素逻辑先找系统主动暴露给用户的权限口子再去看能不能通过漏洞打穿系统本身的隔离。前者是“系统管理员配置疏忽”后者是“软件自身缺陷”两类问题的排查思路完全不同放在一起对比是为了方便理解实际动手时确实也是按这个顺序来的。2. 内核漏洞利用提权2.1 内核提权的基本原理内核提权在所有提权方法里算是“最硬核”的一种。它的原理是内核中存在某个漏洞使得普通用户能够执行一段在内核上下文运行的代码而这个上下文本来只属于 root。只要成功执行就能拿到 root 权限。简单类比一下内核像一栋大楼的总配电室普通用户只能通过楼层电表获取一定配额的电量。如果发现配电室的某个门禁系统有缺陷普通用户可以溜进去拨动总闸整个大楼的供电就任你掌控。内核提权就是找这个门禁系统的缺陷。但这里有个必须说清楚的点内核提权很多时候不是一个“跑一下就能成功”的过程。内核版本、发行版补丁情况、内核防护机制比如某些系统上默认启用的防护都会影响同一个 exploit 能否正常工作。我做测试时同一台服务器可能因为某个内核防护机制开启导致原本能用的 exploit 直接崩溃或者不生效。2.2 实操中的信息收集与利用流程内核提权的第一步永远是确认内核版本。这一步出错后面所有工作都是白费。命令是uname -a cat /proc/version拿到版本号之后去漏洞库或者已经公开的利用代码库中搜索对应的提权漏洞。这一步可以在有网络的环境下进行也可以在本地维护一份离线漏洞库方便在隔离网络环境里离线比对。搜索到疑似可利用的漏洞后不要急着下载 exploit 开跑。先做两件事第一确认目标内核版本的完整版本号是否精确匹配第二检查目标系统是否已经修补了该漏洞。因为有些系统即使内核版本显示陈旧也可能通过安全补丁修复了漏洞只是版本号没有同步更新。这个坑我栽过不止一次。确认匹配之后下载符合版本的利用代码上传到目标机器编译执行。利用代码通常是 C 语言写的目标机器上需要具备编译环境有 gcc或者可以上传静态编译好的二进制文件。大部分 CTF 靶机自带编译器但真实环境的机器可能没有这时候就需要提前准备好预编译版本。我在实际操作中的步骤是# 收集内核信息 uname -a # 查看系统发行版信息 cat /etc/os-release # 确认是否有编译环境 which gcc gdb # 上传利用代码后编译 gcc -o exploit exploit.c # 执行利用 ./exploit执行利用代码时建议先在有调试环境的情况下跑通再上目标机器。因为很多内核 exploit 的第一次尝试会失败甚至导致系统重启在真实环境反复试探风险很大。2.3 内核提权的几个避坑点第一个避坑点不要在无法承受重启的机器上反复测试不明来源的 exploit。内核 exploit 崩溃经常导致整个系统死机重启相当于直接暴露了行为还可能影响业务。授权测试时也应该先对目标系统做备份或快照或者明确告知客户存在宕机风险。第二个避坑点注意防护机制的干扰。一些系统环境中常见的防护机制会直接影响 exploit 的稳定性和执行效果最好在测试前确认目标系统是否开启了相关防护必要时预编译时做相应适配。第三个避坑点不要盲目相信“内核版本匹配就能成功”的判断。我遇到过不止一次内核版本完全一致但 exploit 跑起来就是不行最后发现是发行版在编译内核时改了部分结构布局。所以能否成功还是取决于实测。第四个避坑点内核提权成功后要尽快重置与修复环境。从防御角度讲发现内核漏洞提权成功意味着必须第一时间更新内核版本并加固系统而不是留着这个漏洞继续观察否则同类攻击随时会再次发生。3. sudo与环境变量配置缺陷3.1 sudo配置错误的利用逻辑sudo 提权是我在所有提权方法里最偏爱的方向因为它的利用条件最明确不需要猜测版本兼容性只需要目标管理员做了一个错误的配置。检查方法非常简单sudo -l这条命令会列出当前用户能执行的 sudo 命令。如果输出的内容里有几行是(root) NOPASSWD: /usr/bin/vim那基本等于把 root 权限送上门了。因为这说明当前用户可以在不输入密码的情况下以 root 身份执行vim。而 vim 这类具有“执行外部命令”能力的程序完全可以用来获取 shellsudo vim -c !bash执行后vim 会弹出一个 shell而由于 vim 是以 root 身份运行的这个 shell 也是 root 权限。类似的思路适用于很多命令比如less、more、man、awk、find、python等凡是能在程序内部调用 shell 或解释执行代码的都有可能被利用。我遇到过一个很有意思的场景某台测试机上sudo -l显示当前用户可以 root 身份运行/bin/su。看起来好像没什么用毕竟 su 本来就要密码。但如果 sudo 配置的目标命令不带参数限制可以把su和--help结合或者利用 shell 的通配符特性来绕过限制。这类问题的根源是管理员配置 sudo 时只限制了命令名没有限制参数甚至没有限制默认的 shell。3.2 PATH劫持与调用链注入PATH 劫持是另一种非常隐蔽的提权方式。它的原理是当程序执行某个外部命令时系统会在 PATH 环境变量定义的目录列表里逐个查找这个命令。如果攻击者能控制环境变量或者能往 PATH 中的某个目录写入同名命令就可以让程序执行自己的恶意命令。场景是这样的目标系统里存在一个 root 权限定时执行的脚本脚本内部调用了某个外部命令比如ps或ls。但这个命令是用相对路径调用的脚本作者没有写绝对路径例如#!/bin/bash ps aux | grep java如果这个脚本是由 root 定时执行的而我们又能在脚本运行时修改 PATH让 PATH 里的某个目录优先于系统目录被查找那么可以在这个目录里放一个假的ps脚本。root 执行定时脚本时调用到的就不是系统的ps而是我们的假脚本脚本里的恶意代码就以 root 身份运行了。实际操作中最常见的写法是echo cp /bin/bash /tmp/bash chmod s /tmp/bash /tmp/ps chmod x /tmp/ps export PATH/tmp:$PATH这里的逻辑不复杂把/tmp放在 PATH 最前面root 执行定时任务时第一个命中的ps就是/tmp/ps我们的代码就执行了。如果当前用户对脚本所在目录有写权限甚至可以直接修改脚本本身那就不需要劫持 PATH直接改脚本内容更直接。这个提权路径的关键在于需要确认调用外部命令的方式是相对路径同时脚本以更高权限运行。大部分现代脚本会写明绝对路径但实际环境里总有偷懒的写法这恰恰是机会所在。3.3 值得留意的 sudo 可执行命令组合结合我的经验有几个常见命令组合非常值得关注凡是在sudo -l里看到它们都要当作潜在提权路径看待命令利用方式风险描述vim / less / more / man内部执行 !bash直接获得 shellpython / perl / ruby / lua解释执行系统调⽤直接获得 shellawk / mawk / gawk调用 system()直接获得 shellfind-exec 参数执行任意命令tcpdump-z 参数postrotate 命令执行tee / tee -a配合写文件写定时任务或公钥env / setenv覆盖变量利用后续命令提权这里的规律是什么凡是有“执行外部程序”能力、或者能通过自身语法去调用系统命令的解释器类程序都具备利用价值。看到一个 sudo 条目之后我一般会先搜索这个程序是否具备“命令执行”的能力有能力就实验一下这比盲目跑 exploit 靠谱得多。4. SUID与定时任务提权4.1 SUID文件的查找与判断SUID 提权是另一个经典且常考的方向。SUID 是 Linux 文件权限的一种特殊位当一个可执行文件设置了 SUID 位后任何用户运行这个文件时进程的有效用户 ID 会变成文件属主的用户 ID。如果文件属主是 root那么普通用户运行它时就临时拥有了 root 的权限。查找系统中设置了 SUID 位的文件我常用的命令是find / -perm -4000 -type f 2/dev/null注意这里的-perm -4000表示匹配任何以 SUID 位为基础的文件-type f限定为普通文件加2/dev/null是为了把无权限访问目录的报错信息过滤掉让输出干净一些。找到 SUID 文件之后逐个判断它是否存在利用价值。判断标准很简单这个程序本身有没有“调用外部命令”或者“读取/写入敏感文件”的能力。这里首先排除掉系统自带的正常程序比如ping、su等本身就是设计成可以以 root 身份运行特定功能的。要特别注意那些非标准路径下出现的 SUID 文件例如/tmp下突然冒出来的 SUID 文件或者某家公司自研的工具被设置了这个位。一个经典的 SUID 例子是-rwsr-xr-x root root /usr/bin/find如果 find 被设置了 SUID那么利用方式就很直接find / -exec whoami \;这条命令会以 root 身份执行whoami。同样的逻辑可以换成find / -exec /bin/bash -p \;直接获取 root shell。注意-p参数在 bash 里的作用以有效用户 ID 启动而不是重置为真实用户 ID。4.2 定时任务利用的五步排查法定时任务提权在真实环境中的出现频率远超想象因为很多人会用 crontab 设置一个备份脚本然后忘记给脚本设置合适的权限和路径保护。利用定时任务提权我习惯按五步走第一步查看定时任务列表。当前用户的直接crontab -l系统级的看/etc/crontab和/etc/cron.*目录cat /etc/crontab ls -la /etc/cron.daily/ /etc/cron.hourly/ /etc/cron.weekly/第二步找出当前用户可写的脚本。逐个查看脚本的文件属主和写权限用ls -l判断。如果某个脚本对当前用户可写且这个脚本以 root 身份定时执行那就意味着可以把恶意命令写进脚本。第三步确认脚本的执行方式。如果脚本是通过相对路径被调用的并且当前用户能控制 PATH就可以优先尝试 PATH 劫持。如果脚本使用了绝对路径那就只能寻找脚本本身的可写漏洞或者脚本内部命令的可控点。第四步结合通配符和参数特性实现更巧妙的利用。例如脚本里如果写了tar czf /backup/*之类的命令通配符会被 shell 展开成具体的文件名列表。如果攻击者能在目录里创建以--checkpoint1和--checkpoint-actionexecsh script.sh命名的文件tar 在执行时就会把文件名当成参数从而触发命令执行。第五步也是最重要的等待触发。定时任务可能有自己的执行频率等一轮成功的时机需要耐心。在靶场里可以把系统时间改短来加速测试但在真实环境里只能等待。这一步要是没有耐心整套利用就原地白费。4.3 SUID和定时任务利用的权宜判断不是所有 SUID 文件都值得利用也不是所有定时任务脚本都值得改写。动手之前我习惯做一次“权宜判断”这个入口利用之后会留下多少痕迹是否会影响系统稳定性以 SUID 为例如果直接替换一个系统自带程序或者给 bash 复制一份并加上 SUID 位会留下非常明显的痕迹。相比之下利用现有的 SUID 程序特性进入 shell整个过程不会产生新的可疑文件隐蔽性更好。有经验的安全管理员会用find / -perm -4000做例行检查一个多出来的 SUID 文件比一次登录记录更容易引起警觉。定时任务同理。如果改写 root 的备份脚本虽然简单粗暴但正确的做法是记录原始脚本内容测试完成后恢复原状。我自己的习惯是测试过程中尽量挑选“利用现有功能”而非“植入新组件”的方式因为后者在测试结束后清理起来非常麻烦。5. 敏感信息与凭据收集提权5.1 常见敏感文件的位置清单很多时候提权根本不需要漏洞利用只需要找到系统里存放的密码和密钥。系统管理者为了方便运维经常把密码写在脚本、配置文件或者文档里尤其是数据库密码、应用系统密码。这些信息一旦落到低权限用户手上就可能成为通向 root 的大门。我收集敏感信息时优先检查的位置路径/文件可能包含的内容/etc/passwd用户列表、可登录用户、home目录/etc/shadow加密密码哈希需 root 读取/etc/group用户组信息/home/*/.bash_history历史命令可能含密码/root/.bash_historyroot 的历史命令需 roo权限/var/spool/cron/crontabs/*各用户的定时任务/var/log/各类日志可能记录密码应用配置目录数据库连接密码、API Key备份文件打包后的系统文件可能含密钥/opt、/srv 下的脚本脚本中可能硬编码密码最典型的案例是我在某次授权测试中遇到的拿到一个普通用户权限后发现/opt/backup目录下有个数据库备份脚本脚本里直接写了 MySQL 的 root 密码。用这个密码将当前用户加入 sudo 组后一步登顶。整个过程没有任何漏洞利用纯粹是硬编码凭据导致的权限失控。所以我的建议是拿到任何低权限 shell 的第一时间除了跑常规枚举脚本之外一定要手动翻一遍常见敏感文件尤其是.bash_history、应用配置和备份脚本。有时候惊喜就藏在某一行的注释里。5.2 从日志、命令历史与应用配置中提炼有效凭据提炼有效凭据这件事有点“考古”的感觉核心思路是从蛛丝马迹中还原管理员的操作习惯。命令历史是首选目标。.bash_history通常会记录下账号切换、SSH 登录、数据库连接等场景中输入的命令里面可能包含明文密码。但这里有个坑如果管理员不习惯使用历史记录或者配置了不记录某些命令的选项这个文件的价值会大幅降低。不能把全部希望押在这里。应用配置文件的提取价值更高。Web 应用的配置通常包含数据库连接串$db_host localhost; $db_user root; $db_pass Pssw0rd123;拿到数据库凭据后进入数据库系统就可能读取到更多信息包括用户表、口令哈希、其他服务的密码等。某些情况下数据库本身就是高权限运行配合数据库自身的扩展功能还能进一步提权到操作系统层面。日志文件是个容易被忽略的宝藏。如果有应用在日志里打印了命令执行记录或者某个脚本在调试阶段把密码打进了日志这些内容都可以作为下一步利用的凭据。5.3 从凭据到权限的再利用链路收集到凭据之后如何将其转化为权限提升需要一条清晰的利用链路。以下是我常用的思考路径拿到密码后先尝试当前用户是否可以切换到其他用户。su命令是最直接的尝试路径。如果密码是某个应用系统的密码尝试复用为 SSH 密钥或者 sudo 密码因为很多管理员喜欢所有系统用同一个密码。拿到 SSH 私钥后看能否用于登录其他机器。私钥通常存放在~/.ssh/id_rsa或~/.ssh/authorized_keys关联的位置。如果私钥没有设置 passphrase那么拿到私钥文件就等于拿到了 SSH 登录权限。如果是 root 的私钥直接登录 root。拿到数据库 root 密码后进入数据库尝试通过用户自定义函数或者特定数据库版本的已知缺陷来获取操作系统权限。某些数据库系统支持从文件系统读取文件或者通过SELECT ... INTO OUTFILE写入文件这些都能帮助落地到更高权限。我特别强调一点复用密码是凭据利用中最容易成功的路径也是现实中大量案例的根源。我先用收集到的密码在当前主机上尝试切换到 root 或 admin 用户再考虑横向移动因为很多管理员只在特定服务器上高强度设置了密码其他机器的密码还是老样子。5.4 敏感凭据利用的合规边界这里必须多说一句。从日志、历史命令、配置文件中提取密码是一件极度敏感的事情。这些密码可能包含业务系统的生产密码、用户个人密码一旦泄露或者被挪作他用后果非常严重。所以这个方向的测试必须在授权范围内进行测试完成后要及时清理和销毁获取到的敏感凭据不能留存。我个人的实践是在测试报告中只描述“通过配置文件中存放的数据库密码成功提升权限”这类分析结论不会把真实的密码明文直接写在截图或报告里如需记录也是脱敏后的形式。这是测试人员的职业底线也是对客户负责的态度。6. 提权排查速查与经验总结6.1 一步到位的排查命令汇总经过多次实践之后我整理了一份“一条龙”排查命令清单。拿到低权限 shell 后按顺序执行大部分场景都能快速定位到可利用方向。# 当前用户与组信息 id # 内核与系统信息 uname -a cat /etc/os-release # sudo 权限 sudo -l # SUID 文件 find / -perm -4000 -type f 2/dev/null # 环境变量中的可疑路径 echo $PATH # 定时任务 ls -la /etc/cron.* cat /etc/crontab ls -la /var/spool/cron/crontabs 2/dev/null # 可写的系统目录 find / -writable -type d 2/dev/null | grep -E ^(/etc|/opt|/var|/tmp) # 敏感文件 cat /etc/passwd find / -name *.bak -o -name *.conf -o -name *.sql 2/dev/null # 历史命令 cat ~/.bash_history这套命令的核心逻辑是把系统里与权限相关的关键信息全部汇总出来。每一条命令输出后我都习惯先看一眼结果里有没有“不寻常”的地方比如奇怪的 SUID 文件、可写的 root 脚本、明显的密码明文。只要出现一个亮点就可以顺着往下深挖。6.2 常见问题与踩坑实录遇到最多的第一类问题是sudo -l命令执行后没有任何输出或者提示当前用户不在 sudoers 文件中。这说明当前用户没有 sudo 权限直接跳到 SUID 和定时任务排查。第二类高频问题是r找到了 SUID 文件但利用时执行的 shell 权限没有提升到 root。这通常是因为 bash 在检测到有效用户 ID 与真实用户 ID 不一致时会主动放弃特权解决办法是使用bash -p或者选择其他解释器来启动 shell。第三类问题是定时任务利用完成后任务并没有按照预期执行。原因通常有两个一是脚本的调用路径写的是绝对路径PATH 劫持不生效二是定时任务配置了nohup等包装命令导致环境变量在子进程中被重置。排查时建议先确认任务真实执行了可以通过修改脚本内容时留下痕迹来确认。第四类高频问题是内核 exploit 编译报错。很多公开的利用代码是针对特定内核版本的在版本不对或者缺少特定头文件的系统上编译一定会失败。此时优先考虑低版本替代方案或者换一个利用代码版本不要在同一个代码上死磕。6.3 防御视角下的加固建议站在防御者角度结合这些提权路径反向推演其实可以总结出几条简单有效的加固策略。给 sudo 配置做最小化授权能不给的命令就不给必须给的情况下使用-e编辑器模式限制参数禁止sudo vim、sudo python这类高风险命令。这个规则落地之后一大类 sudo 提权问题直接被切断。定期扫描 SUID 文件和定时任务脚本的权限情况把非正常路径的 SUID 文件列入黑名单并自动告警。这中间最重要的动作是及时发现/tmp、/var/tmp等目录下突然出现的可执行文件通常意味着系统被入侵过。把所有版本更新和管理员密码管理纳入日常。内核补丁及时更新管理员密码定期更换且不重复使用。这些听起来是老生常谈但实际做到的机构少之又少否则不会有大量凭据复用导致的提权案例。总的来说提权是攻防知识里最需要发散思维的环节之一。每次拿到一个低权限 shell我仍然会像第一次一样按顺序排查一遍因为管理员的一次配置变更就可能引入全新的风险点。希望这份整理能帮你节省一些试错时间少踩几个我已经踩过的坑。