几个月前帮朋友查一台 CentOS 7 机器,Nginx 日志里有个过期域名还在源源不断地产生流量,可解析记录早就删干净了。查到最后,在 /etc/grub2.cfg 里看到启动参数被偷偷塞了一条 rd.break。这手法说穿了不值钱,可一旦有人摸到物理或者 VNC 控制台,你的服务器就跟没锁门一样。

有人绕过防火墙,走到机器前按了个 e

攻击者要动手,前提要么能碰到机房里的机器,要么是通过带外管理口——比如 iLO、iDRAC 或者云服务的 VNC 控制台登上系统。只要拿到控制台,重启,在 GRUB 菜单上按 e 进入编辑模式,就这么简单。没密码拦着的话。

找到以 linux 开头那行,末尾加个 rd.break。这个参数让 systemd 在 initramfs 阶段停住,直接丢给你一个 root shell。这时候硬盘上的根文件系统还没完全挂载,但你已经有权限改它了。更狠一点的,直接加 init=/bin/bash,跳过所有服务进 shell。进去之后 就能读写真正的根分区。

接下来的操作就顺理成章了:改 /etc/resolv.conf 把 DNS 指到自己的服务器,加 iptables 规则做 DNAT 把 80 和 443 的流量转到竞品 IP,或者直接改 Nginx 配置返回一个 302 跳转。

某云厂商的客户没给 GRUB 设密码,攻击者直接从 VNC 控制台进了单用户模式,把指向自家产品的域名悄悄改到了一个仿冒竞品页上。结果呢?用户照旧访问那个老 URL,看到的全是别人家广告——一套操作下来,流量就这么被截走了。

最让人无奈的是,这种修改在操作系统层面几乎不留日志——GRUB 菜单编辑本身不会写 audit log。只有重启后翻 dmesg 才能看到内核启动参数被改过的痕迹。等你发现流量异常,劫持可能已经跑了好几个星期。

那案例里最扎心的不是手法高明,而是攻击者根本不需要黑进系统——他只需要比你更靠近机房。

domain hijacking diagram showing traffic redirection

域名抢注怎么跟启动菜单扯上关系的

很多人以为服务器安全靠防火墙和 WAF 就够了,结果人家直接走到机柜前插显示器。GRUB 菜单默认没密码,按 e 就能改内核行。加个 rd.break,系统停在 initramfs 阶段,根分区还没挂载呢,你已经有 root shell 了。这操作比 SSH 爆破快十倍,还不留痕迹。

改完 resolv.conf 指向恶意 DNS,或者用 iptables 做 DNAT 转发,这台机器就成了流量中转站。过期域名本来就有 SEO 权重,搜索引擎还在收录旧链接,用户一点进来,跳转到竞品页面。攻击者啥都不用管,坐等广告费进账。

更绝的是,这种攻击完全不触发常规监控系统的警报——它发生在启动流程里,不是网络层也不是应用层。等你发现异常流量,对方可能已经换了三波域名了。

GRUB password configuration on Linux terminal

防御从 GRUB 密码开始,但不能停在这里

看完前面那个案例,最让人后背发凉的不是攻击者有多聪明——而是他根本不需要聪明。你堆再多 WAF 规则、配再复杂的 iptables,人家走到机柜前按个 e 键,一切归零。GRUB 菜单默认就是裸奔的,这事在运维圈喊了十几年,但你去查云服务器后台,十台里有八台还是出厂配置。

那怎么补?不是让你去 BIOS 里设个密码就完事——那种密码用螺丝刀拆主板电池就能清。真正管用的是在 GRUB 层面做两层锁:一是加密菜单编辑权限,二是把单用户模式、rd.breakinit=/bin/bash 这些危险入口全部封死。

生成 PBKDF2 加密口令

GRUB2 支持 PBKDF2 哈希,这东西比普通 SHA-512 抗暴力破解能力强得多。一行命令搞定:

运行后会让你输两遍密码,然后吐出一长串类似 的字符串。把这串东西完整复制下来,别漏了末尾的等号。接下来编辑 /etc/grub.d/40_custom,在最顶部写入:

set superusers="admin"
password_pbkdf2 admin grub.pbkdf2.sha512.10000.8A2B...

这里 superusers 定义了哪个用户有编辑菜单的权限——你可以设多个,但生产环境建议只留一个,少一个入口少一分风险。编辑完别忘跑 ,不然配置不会生效。CentOS、Anolis OS、Ubuntu 都适用,只是 Ubuntu 上命令叫 (没有数字2),路径也可能在 /boot/grub/grub.cfg

有个坑:很多人改完 40_custom 忘了执行 ,然后重启后发现密码没生效,还以为自己写错了。这文件不会自动触发重新生成主配置,必须手动走一遍。

锁定危险内核参数

密码只能拦住按 e 改菜单的人,但如果你自己配的内核启动参数里还允许 rd.break,那等于给攻击者留了后门。这个参数的作用是让系统在 initramfs 阶段停下来,给你一个 shell——此时磁盘还没挂载、LUKS 还没解密,但你已经有 root 权限了。

封堵方法:编辑 /etc/default/grub,在 这一行末尾加上两个参数:

GRUB_CMDLINE_LINUX="... rd.break=disabled systemd.unit=multi-user.target"

rd.break=disabled 直接告诉 dracut 不要进入急救 shell。systemd.unit=multi-user.target 则确保系统启动后进入多用户模式而非单用户。这里有个细节:有些人以为设了 systemd.unit=emergency.target 更安全,但 emergency 模式还是单用户 shell——攻击者拿到键盘照样能干坏事。multi-user.target 才是正常的无图形界面运行级别。

改完后同样执行 刷新配置。想验证是否生效?重启后按 e 看看内核行里有没有你加的参数——如果看不到,说明要么命令没跑成功,要么路径错了。

别忘了 BIOS/UEFI 这一层

GRUB 密码只防本地键盘操作。攻击者如果带着 Live USB 来,直接从 U 盘启动,你硬盘上的 GRUB 压根没被加载,密码形同虚设。所以这一步必须跟 UEFI Secure Boot、固件密码、LUKS 全盘加密配合着用。Secure Boot 能阻止未签名的启动介质,固件密码防别人改启动顺序,LUKS 加密保证即使硬盘被拔走也读不出数据。

之前看到一篇技术文章说得直白:GRUB2 密码能防的是“你同事趁你上厕所时溜到服务器前改内核参数”,但防不住“有人拿着螺丝刀拆了硬盘走人”。物理安全永远是一层层叠加的——少一层就有破绽。

这三步配下来,至少那些按个 e 键就能劫持流量的低级攻击就被挡在门外了。但别以为设完密码就高枕无忧——真正的硬仗还在后面:内核提权漏洞、残留的 audit 日志、还有你压根没注意到的 initramfs 钩子脚本。

在 CentOS 和 Anolis OS 上实操一把

理论说破天,不如直接敲键盘验证。两台机器走完流程——CentOS 7.9 一台,Anolis OS 8.6 另一台。踩坑的地方顺手记下来,下次你碰到能少绕点弯路。

CentOS 7:一条命令搞定,但有个小陷阱

CentOS 7 自带的 grub2-tools 版本够新,直接跑 就行。它会交互式让你输两遍密码,然后自动把加密哈希写进 /boot/grub2/user.cfg。完事记得 重新生成配置。

但我第一次重启后按 e 居然没弹密码框——排查发现 /etc/grub.d/01_users 文件里的 set superusers="root" 没写对。这文件是 自动生成的,但如果你之前手动改过 40_custom 或其他 grub.d 脚本,优先级冲突会导致 superusers 变量被覆盖。修复很简单:确认 01_usersset superusers="admin" 存在且唯一,然后在每个你想保护的 menuentry 块头部加 --users admin,比如:

menuentry 'CentOS Linux (3.10.0-1160.el7.x86_64) 7 (Core)' --users admin {
    load_video
    set gfxpayload=keep
    insmod gzio
    linux /vmlinuz-3.10.0-1160.el7.x86_64 root=/dev/mapper/cl-root ro crashkernel=auto
    initrd /initramfs-3.10.0-1160.el7.x86_64.img
}

这样按 e 才认账。不加 --users 的话,superusers 设了也等于白设——GRUB 2.02 就是这么设计的,只锁菜单不锁条目。

Anolis OS 8:看似兼容,实则路径藏雷

Anolis OS 官方说跟 CentOS 8 完全兼容,但实际操作时 的目标路径得写成 /boot/grub2/grub.cfg,不是 /boot/efi/EFI/anolis/grub.cfg(后者是 UEFI 模式下的备份路径)。我一开始对着 CentOS 8 的文档用 -o /boot/efi/EFI/anolis/grub.cfg,结果重启后配置没生效——Anolis 的 UEFI 固件读的是 /boot/grub2/grub.cfg 这个符号链接指向的文件。正确的做法是:

# 生成密码哈希
grub2-mkpasswd-pbkdf2
# 输入密码,复制输出的 grub.pbkdf2.sha512.xxxx 字符串

# 编辑 /etc/grub.d/40_custom,追加:
set superusers="admin"
password_pbkdf2 admin grub.pbkdf2.sha512.10000.8A2...[你的哈希]

# 重新生成配置,目标路径一定是这个:
grub2-mkconfig -o /boot/grub2/grub.cfg

另外 Anolis 8 默认启用了 grub2-efishim,如果你之前配置过 Secure Boot, 会报 efibootmgr 相关的警告——不影响密码功能,但看着膈应。装个 efibootmgr 包就行。

验证:重启后按 e 才是真验收

配置完别信终端输出。重启,在 GRUB 菜单出现时狂按 e。如果弹出一个黑底白字的对话框,要求输入用户名和密码——恭喜,防御生效了。用户名填 admin(就是 superusers 设的那个),密码输你刚才设的。通过后才能看到内核启动参数编辑界面。

要是直接进了编辑界面没弹框,八成是 01_users40_custom 里的 superusers 变量被后面的脚本覆盖了。排查方法:进系统后跑 grep -r 'superusers' /etc/grub.d/ 看有没有重复 superusers 记录,然后检查 /etc/grub.d/ 下所有脚本的 set superusers= 行——只保留一份。

CentOS 8 还有个坑:如果系统是 UEFI 模式但 /boot 挂载在独立分区, 可能把配置写到 /boot/efi/EFI/centos/grub.cfg 而非 /boot/grub2/grub.cfg。确认方法: 看看它是否指向 EFI 分区内的文件。指向对了才算完。

怎么知道 GRUB 有没有被动过

机房巡检时顺手跑一遍 并不费电,关键是要把输出固定下来当基线。一旦哪天比对发现 Modify 时间不对,立刻用 diff -u 看看到底谁动了手脚——通常黑客会塞进去一段 linuxefi 指向临时内核,或者在 initrd 后面补个 debug 开关。把这些差异记到值班手册里,下次碰见同类特征就能秒识别。

# 提取当前 grub.cfg 的 sha256sum
sha256sum /boot/grub2/grub.cfg > /var/log/grub_$(date +%Y%m%d).hash
# 跟上周的做比较
diff -u /var/log/grub_20261129.hash /var/log/grub_20261206.hash

光靠人工终究会漏,上 AIDE 才算稳妥。装完先改配置文件 /etc/aide/aide.conf,把 /boot/grub2/grub.cfg 单独拎出来设成 p+i+u+g——权限、 inode、用户、组全都校验。初始化数据库之后把 cron 塞进每夜任务,第二天早晨翻邮件,看见 ALERT 再深挖,误报率几乎为零。

若提示 ,八成是攻击者试图抹掉脚印;若看到 ,那就得考虑整台机器重建了。

服务器这东西,防得住防不住,说到底就看你愿不愿意在启动参数上多盯两眼。攻击者可不会嫌自己手脏,他只嫌你太好欺负。