Linux 账号与文件权限基线抽查:UID 0 账号、SUID/SGID 与全局可写项的核查和回滚

枫少@KillBoy
枫少@KillBoy
枫少@KillBoy
管理员
390
文章
0
粉丝
安全运维1 26字数 2614阅读8分42秒阅读模式
AI智能摘要
生产机上做安全基线抽查,时间窗口往往只有一个小时,与其把上百条清单刷到超时,不如先挑一组高价值、低误报的项做透。UID 0 异常账号、SUID/SGID、全局可写目录和无属主文件,命中即意味着有人动过手脚或历史遗留未清。本文按一个晚上能做完的节奏,给出可回滚快照、逐项盘点、小步变更和验证的完整操作路径,并明确区分日常抽查与入侵排查的边界。面对这些命中项,你该如何判断、处置并留好退路?
— AI 生成的文章内容摘要

安全基线清单动辄上百条,真坐到一台跑着业务的生产机前,低峰期窗口往往只有一个小时。与其把 CIS、等保条目从上到下刷一遍、刷到一半超时,不如先挑一组高价值、低误报的项做透:账号侧的 UID 0 异常账号,文件权限侧的 SUID/SGID、全局可写目录、无属主文件。这几项的共同点是不会误报——命中就意味着有人动过手脚或历史遗留没清,值得当场确认;而且命令几分钟就能跑完,结论可以复核、可以回滚。

这篇文章按一个晚上能做完的节奏组织:先做可回滚快照,再逐项盘点、判断、小步变更、验证,最后留一份下轮可对比的记录。所有命令默认以 root 在一台主机本地执行,通过跳板机批量下发请自行分批。需要说清楚边界:这是日常基线抽查,不替代完整等保测评,也不等于入侵排查——如果命中项看起来像被人刻意留下的,先取证、后处置,不要急着删。

运维工作台上终端屏幕显示账号与权限检查列表,旁边是手写的检查项便签

先划范围:这一轮动什么、不动什么

抽查前先把影响面写下来,后面每一步都对照这张表判断“要不要动”。执行位置均为目标主机本地 shell,预期现象是“干净主机上输出很少”。

检查项执行命令要点干净主机预期命中后的动作上限
UID 0 账号/etc/passwd 第三字段只有 root 一行先锁定,不删除
可登录账号shell 为 bash/sh/zsh与人员清单一致记录,交业务确认
SUID 文件find / -xdev -perm -4000 -type f数十个,均属基础组件先摘位观察
SGID 文件与目录-perm -2000同上,目录多为协作目录确认归属后改
全局可写目录-perm -0002 且无 sticky基本为 0补 sticky 或收权限
无属主文件-nouser -o -nogroup基本为 0确认后 chown 或归档

需要观察的业务路径提前列清楚:SSH 登录与 su/sudo、密码修改、crontab 与 systemd timer、Nginx/PHP-FPM 的读文件与上传目录写入、备份任务的读取权限。这几条路径覆盖了本轮绝大多数变更可能踩到的点,验证阶段会逐条过。

第 0 步:留下能一键回滚的快照

这一步不做完,后面任何 chmod、usermod 都不要敲。所有输出放进同一个带时间戳的目录,方便事后比对。

mkdir -p /root/baseline-$(date +%F) && cd /root/baseline-$(date +%F)
cp -a /etc/passwd /etc/shadow /etc/group /etc/gshadow .
cp -a /etc/sudoers /etc/sudoers.d ./ 2>/dev/null
visudo -c          # 预期:parsed OK,顺手确认 sudoers 本身没坏
stat -c '%a %U %G %n' /etc/passwd /etc/shadow /etc/group

文件权限侧,把关键目录当前的 mode、属主、ACL 和扩展属性一并存下来,这是回滚的唯一依据:

for d in /etc /bin /sbin /usr/bin /usr/sbin /var/www /tmp /var/tmp /home; do
  find "$d" -xdev -printf '%m %u %g %pn' 2>/dev/null
done > perms-snapshot.txt
getfacl -R -p /var/www > acl-www.bak 2>/dev/null

-xdev 是必须的,否则 find / 会爬进挂载的 NFS 或对象存储,跑几十分钟还带出一堆与主机无关的路径。快照文件本身按敏感件处理,/etc/shadow 副本别留在 web 目录或家目录里。

第 1 步:UID 0 账号,只应该有 root 一个

awk -F: '$3==0 {print $1, $3, $7}' /etc/passwd
awk -F: '($2==""){print $1}' /etc/shadow
getent group 0

预期输出只有 root 0 /bin/bash(或 /bin/sh),空口令账号一行都没有。多出来的 UID 0 账号无论名字多像系统账号,都要当成待确认项处理:攻击者留后门时经常这么干,因为登录进去直接就是 root。

判断顺序:先查它是不是配置管理工具、监控 agent、容器基础镜像带入的账号,再查 shell 历史、last、/var/log/secure(或 auth.log)里有没有它的登录记录。确认是历史遗留且无人使用,走锁定而不是删除:

passwd -S  backupadmin          # 记录当前状态,回滚时对照
usermod -L backupadmin          # 锁定密码
usermod -s /sbin/nologin backupadmin

回滚用 usermod -U backupadmin 并恢复原 shell。之所以不直接 userdel,是因为删除会连同家目录、邮件、以及被其他服务引用的 UID 一起带走,出问题时没有退路。真要删,放在观察一到两周之后、并确认 find / -uid <UID> 无结果再说。

并排终端窗口示意变更命令与对应的回滚命令

第 2 步:SUID/SGID 清单,和包记录对一遍

SUID 位让程序以文件属主身份运行,属主是 root 时进程就带 root 权限;SGID 作用于文件是以属组身份运行,作用于目录是新建文件继承目录属组。正常系统里这类文件不多,且都来自软件包。

find / -xdev -type f -perm -4000 -printf '%m %u %g %pn' 2>/dev/null | sort -k4 > suid.txt
find / -xdev ( -type f -o -type d ) -perm -2000 -printf '%m %u %g %pn' 2>/dev/null | sort -k4 > sgid.txt

预期现象:输出几十行,路径集中在 /usr/bin、/usr/sbin、/usr/libexec,属主是 root,典型的有 su、sudo、passwd、mount、umount、newgrp、gpasswd、chsh、crontab、pkexec、ssh-keysign、unix_chkpwd、fusermount 等。具体名单随发行版和安装的包不同,别拿别人的清单硬套。

判断最省力的方式是问包管理器“这个文件的权限是不是被改过”:

# RHEL/CentOS/Rocky
rpm -Va 2>/dev/null | awk 'substr($0,2,1)=="M"'
# Debian/Ubuntu:能查内容校验,权限变化覆盖有限,需人工比对
dpkg --verify 2>/dev/null | head

rpm -Va 输出 9 位标记,第二位是 M 表示权限位相对包内记录发生了变化——这类文件优先看。反过来,一个 SUID 文件如果 rpm -qf 报“not owned by any package”,那就不是装出来的,是后加的,必须查清来源再处置。

命中非基线 SUID 文件时的最小动作是先摘位、保留文件,而不是删:

stat -c '%a %U %G %n' /path/to/suspect        # 记下原 mode,例如 4755
chmod u-s /path/to/suspect                    # 或 chmod 0755

回滚就是 chmod 4755 /path/to/suspect(按记下的原值)。摘位后要立刻验证依赖它的功能:自己写的小工具摘位后可能直接报权限不足,那就说明它确实在被业务调用,回到确认环节。SGID 目录(如团队协作目录)摘位会影响新建文件的属组继承,改之前确认目录里有没有跨组共享的写入需求。

第 3 步:全局可写目录与无属主文件

全局可写本身不致命,缺了 sticky 位才致命——没有 t 位,任何用户都能删改别人在该目录下的文件。

find / -xdev -type d -perm -0002 ! -perm -1000 -printf '%m %u %g %pn' 2>/dev/null
find / -xdev -type f -perm -0002 -printf '%m %u %g %pn' 2>/dev/null
find / -xdev ( -nouser -o -nogroup ) -printf '%m %u %g %pn' 2>/dev/null

预期:/tmp、/var/tmp、/dev/shm 是全局可写但带 sticky(mode 1777),第一条命令应该输出为空;第二条在干净主机上通常也只有 /tmp 下运行中的临时文件。Web 上传目录、缓存目录、session 目录是常见的“合理例外”,但它们应该由服务账号持有,而不是 nobody 或 777。

处置分两类:

  • 缺 sticky 的共享目录:chmod +t /path,回滚 chmod -t /path。这一条对业务几乎无感,可以先做。
  • 全局可写的业务目录:先确认写入方是谁,再收成 chmod o-w,或改成属组可写 chmod 2775 并 chown root:webteam。回滚用快照里记下的原 mode。收紧后必须验证上传、缓存写入、session 落盘,Nginx/PHP 报 Permission denied 就是收过头了。

无属主文件说明创建它的账号已经被删(或 UID 变更),这类文件在 find / -uid 类排查里会成为噪声,也可能被任意新账号“继承”。确认内容无价值就移到归档目录,有价值则 chown 给明确的账号,别直接 chown root 了事——那只是把问题藏起来。

第 4 步:小步变更、逐项验证、留好回滚

变更别一次性推完。按下面的节奏走,每一项之间留出观察窗口,出问题能立刻定位到是哪一步。

顺序变更内容观察窗口必验路径
1共享目录补 sticky即时无业务影响,抽查即可
2锁定可疑 UID 0 账号10 分钟SSH 登录、sudo、密码修改
3摘掉非基线 SUID 位30 分钟对应命令执行、cron、备份脚本
4收紧全局可写业务目录1 小时Web 上传、缓存、session

验证清单固定四条,每条都给出预期现象:

  • 登录:新开一个 SSH 会话(别关现有会话),su - 和 sudo -l 正常,普通用户 passwd 能改自己密码——验证 SUID 摘位没误伤 passwd 和 sudo。
  • 计划任务:crontab -l 对业务账号可读,systemctl list-timers 里的任务在窗口内正常触发,/var/log/cron 或 journal 无 Permission denied。
  • Web:nginx -t 通过,站点首页 200,上传一张测试图成功,PHP 错误日志无新告警。
  • 备份:手动触发一次备份任务,确认能读到被收紧的目录。

任何一条不符合预期,按快照回滚该单项,不要连带回滚整批——这正是分步做的意义。回滚命令提前写进变更单,写的时候就用快照里记下的原始 mode,别靠回忆。

复盘:把这一轮变成下一轮的对照

收尾时把三份东西归档到同一个目录:perms-snapshot.txt、suid.txt/sgid.txt、以及一份变更记录(谁、什么时候、改了哪个路径、原值、现值、验证结论)。下一轮抽查可以直接和新快照 diff,命中项会明显变少,窗口也能压得更短。

频率上,账号与 SUID 这组适合按月跑一次全量,全局可写目录按季度,业务目录权限在每次发版或装新插件后补一次抽查。WordPress 站点尤其要注意插件安装目录——上传目录被改成 777 是极常见的“临时修一下”,事后没人改回来。

最后提醒一句:本轮所有动作都建立在业务确认之上。删账号、改系统文件权限这类不可逆或影响面大的操作,无论如何都要有明确的授权记录和可执行的回滚命令,否则宁可先只记录、不处置。

热门话题

linux capabilities 与 SUID 的权限边界

 
枫少@KillBoy
    • 拾旧梦
      拾旧梦 1

      先做快照这点很关键

    匿名

    发表评论

    匿名网友

    拖动滑块以完成验证