系统账号为什么不该有 shell
bin、daemon、mail、ftp、nobody 这类账号是给服务用的,它们不需要有人登录进去。如果这些账号保留了 /bin/bash 或 /bin/sh,一旦口令被猜到、被写成空口令,或者某个服务漏洞让攻击者能以该身份执行命令,就能直接拿到一个交互式 shell,在机器上稳定驻留。把系统账号的 shell 收敛为 nologin,是成本极低、收益明确的一条基线。
基本信息
| 加固项 | 系统账号 shell 收敛为 nologin |
|---|---|
| 适用平台 | Linux(RHEL/CentOS/Debian/Ubuntu) |
| 风险等级 | 中 |
| 判定标准 | UID 小于 1000 的账号,shell 应为 nologin 或 false |
| 注意 | 系统账号 UID 门槛随发行版不同,现代发行版为 1000 |
典型使用场景
- 基线核查:一次列出所有可登录的系统账号,输出量小、结论直接
- 老系统清理:早期手工创建的服务账号常直接沿用 bash
- 安全事件后复核:攻击者给服务账号加 shell 是维持访问的常用手法
- 批量加固:用循环脚本一次性收敛多个账号的 shell
- 新服务上线前检查:安装脚本创建的服务账号要确认默认 shell
核查命令
核查命令
awk -F: '($3 < 1000 && $7 !~ /(nologin|false)$/) { print $1 ":" $7 }' /etc/passwd
awk -F: '($1=="root") || ($3 >= 1000) { next } { print $1, $3, $7 }' /etc/passwd | head -40
command -v nologin || echo /usr/sbin/nologin # 先确认本机 nologin 路径
getent passwd nobody # 逐个确认账号用途与来源加固:批量改为 nologin
加固:批量改为 nologin
NOLOGIN=$(command -v nologin || echo /usr/sbin/nologin) # 自动探测路径
for u in bin daemon adm lp sync shutdown halt mail operator games ftp; do
usermod -s "$NOLOGIN" "$u" 2>/dev/null || true # 不存在的账号直接跳过
done
awk -F: '($3 < 1000 && $7 !~ /(nologin|false)$/) { print $1 }' /etc/passwd # 复核常见系统账号对照
| 账号 | 常见默认 shell | 处置 |
|---|---|---|
| bin、daemon、adm、lp | nologin 或历史遗留的 sh | 统一改为 nologin |
| sync、shutdown、halt | 历史上分别为专用命令 | 基线要求一并收敛为 nologin |
| mail、operator、games | nologin | 确认无业务依赖后保持 nologin |
| ftp | nologin 或 shells 文件控制 | 确认 FTP 服务已停用或使用受限 shell |
| nobody | nologin | 用于匿名映射,绝不改为可登录 |
批量修改前用 getent 逐个确认用途:部分工业软件、备份系统会以某个系统账号身份执行任务,改 shell 可能让任务失败。root 与真正的运维账号不在收敛范围内。
不同发行版 nologin 路径不同(/sbin/nologin 与 /usr/sbin/nologin),脚本里用命令探测最稳。另外只把 shell 改成 nologin 并不等于锁定口令,两件事要一起做才算完整收敛;顺带确认非运维账号是否被加入特权组。
收敛口径要写清楚
核查报告里要说明两件事:一是采用的 UID 门槛(是否按发行版的 1000 判断),二是白名单账号(root 与实名运维账号)。口径不明会让同一条输出在两次核查里得出不同结论,也让复核的人无从判断哪些是预期内的例外。