GRUB引导加密是防止物理接触攻击的最后一道防线。当攻击者能接触到服务器物理机或通过带外管理卡(如IPMI、iDRAC)控制启动流程时,只需在GRUB菜单按“e”键编辑内核启动参数,添加“single”或“init=/bin/bash”,就能在数秒内获得root权限而不需要任何密码。解决这个安全漏洞的核心方法就是给GRUB设置加密密码,让修改启动参数必须先通过身份验证。
GRUB引导漏洞的攻击原理
CentOS默认安装完成后,GRUB引导加载程序对任何能接触到控制台的人都完全开放。攻击者在服务器启动到GRUB菜单界面时按下“e”键,就能看到类似这样的启动配置:
load_video set gfx_payload=keep insmod gzio linux /vmlinuz-3.10.0-1160.el7.x86_64 root=/dev/mapper/centos-root ro crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet initrd /initramfs-3.10.0-1160.el7.x86_64.img
攻击者只需在以“linux”开头的行末添加“init=/bin/bash”,系统启动后就会直接进入一个bash shell,并且这个shell拥有root权限。或者添加“rd.break”参数,可以进入initramfs的紧急shell,在那里挂载sysroot后同样能修改root密码。更直接的做法是添加“single”或数字“1”,让系统进入单用户维护模式,这个模式默认也不需要密码验证。
这种攻击方式不需要任何工具,不需要拆机拔硬盘,只需要一个键盘和显示器,或者通过服务器管理口的虚拟控制台就能完成。对于托管在IDC机房的服务器,虽然物理访问有一定门槛,但IDC运维人员、机房其他客户、甚至通过社会工程学进入机房的人,都可能利用这个漏洞。对于企业内部服务器,这个风险更加突出,保洁人员、访客、离职员工都可能成为威胁来源。
GRUB加密的完整实施步骤
第一步,生成加密密码哈希。GRUB使用PBKDF2算法存储密码,需要先生成密码哈希值。在已安装CentOS的系统上执行grub2-mkpasswd-pbkdf2命令:
[root@server ~]# grub2-mkpasswd-pbkdf2 Enter password: Reenter password: PBKDF2 hash of your password is grub.pbkdf2.sha512.10000.5A1B2C3D4E5F...(长哈希字符串)
这个哈希字符串需要完整记录下来,后续配置中会用到。注意这里的迭代次数是10000次,这是PBKDF2算法的默认值,能在安全性和启动耗时之间取得较好平衡。如果对安全性要求极高,可以手动调整迭代次数,但会增加启动时验证密码的等待时间。
第二步,编辑GRUB配置文件。在CentOS 7及以上版本中,需要修改/etc/grub.d/40_custom文件,这个文件是专门留给用户自定义配置的,系统更新不会覆盖它:
[root@server ~]# vi /etc/grub.d/40_custom
在文件末尾添加以下内容:
set superusers="admin" password_pbkdf2 admin grub.pbkdf2.sha512.10000.5A1B2C3D4E5F...
这里“admin”是超级用户名,可以自定义为任何你想要的用户名。密码哈希部分粘贴第一步生成的完整字符串。如果有多个管理员,可以添加多行password_pbkdf2,每行一个用户。
第三步,限制未授权用户编辑启动项。仅仅设置超级用户还不够,还需要告诉GRUB哪些启动菜单项需要密码保护。编辑/etc/grub.d/10_linux文件,找到所有menuentry行,在它们后面添加“--unrestricted”参数可以让正常启动不需要密码,但编辑启动参数需要密码。更严格的策略是完全不加“--unrestricted”,这样连正常启动都需要输入用户名和密码。
推荐的做法是在/etc/grub.d/40_custom中统一控制权限,添加以下配置:
set superusers="admin" password_pbkdf2 admin grub.pbkdf2.sha512.10000.5A1B2C3D4E5F... export superusers
然后编辑/etc/grub.d/10_linux,在CLASS定义部分确保没有“--unrestricted”标记被自动添加。对于CentOS 7,通常需要修改/etc/grub.d/10_linux中的以下行:
echo "menuentry '$(echo "$title" | grub_quote)' ${CLASS} \$menuentry_id_option 'gnulinux-$version-$type-$boot_device_id' {" | sed "s/^/$submenu_indentation/"改为:
echo "menuentry '$(echo "$title" | grub_quote)' --users admin ${CLASS} \$menuentry_id_option 'gnulinux-$version-$type-$boot_device_id' {" | sed "s/^/$submenu_indentation/"这样所有内核启动项都只允许admin用户操作。普通用户看到启动菜单但无法编辑,按回车能正常启动,按“e”键则提示输入用户名和密码。
第四步,重新生成GRUB配置。修改完成后必须重新生成/boot/grub2/grub.cfg文件:
[root@server ~]# grub2-mkconfig -o /boot/grub2/grub.cfg
对于使用UEFI启动的系统,路径可能不同:
[root@server ~]# grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg
执行后检查生成的grub.cfg文件,确认password_pbkdf2配置已经写入,menuentry条目中包含了“--users”限制。
不同CentOS版本的差异处理
CentOS 6使用GRUB Legacy(版本0.97),配置方式完全不同。需要编辑/boot/grub/grub.conf文件,在hiddenmenu行之前添加:
password --md5 $1$randomsalt$encryptedpasswordhash
生成MD5密码哈希可以使用grub-md5-crypt命令。然后在每个title段落中添加“lock”指令,这样选择该启动项就需要密码。CentOS 6的GRUB加密强度远低于GRUB2的PBKDF2,建议尽快升级到CentOS 7或更高版本。
CentOS 8和CentOS Stream使用GRUB2,配置方法与CentOS 7基本一致。但CentOS 8引入了BLS(Boot Loader Specification),启动菜单项由/boot/loader/entries/目录下的文件动态生成,编辑10_linux的方式可能不生效。此时需要在/etc/grub.d/40_custom中添加完整的menuentry定义,或者使用grub2-set-password命令快速设置密码。
使用grub2-set-password简化配置
CentOS 7和8提供了grub2-set-password工具,可以一键完成密码设置:
[root@server ~]# grub2-set-password Enter password: Confirm password:
这个命令会自动创建/boot/grub2/user.cfg文件,存储超级用户配置,并在重新生成grub.cfg时自动包含。但需要注意,grub2-set-password默认只保护编辑启动参数和进入命令行模式,正常启动仍然不需要密码。如果需要连正常启动都要求密码,还是需要手动修改10_linux或40_custom文件。
验证配置是否生效,可以重启服务器,在GRUB菜单出现时按“e”键。如果弹出用户名和密码提示框,说明加密生效。输入设置的用户名和密码后,才能进入编辑模式。
加密后的运维注意事项
GRUB加密后,远程KVM或IPMI操作时需要先通过GRUB认证。如果忘记密码,恢复起来会比较麻烦。必须通过挂载系统盘到其他系统、使用救援模式或Live CD来清除GRUB密码配置。具体操作是进入救援模式后,挂载/boot分区,删除或编辑user.cfg文件,或者直接重新生成grub.cfg。
对于大规模服务器部署,建议将GRUB密码哈希纳入配置管理。可以使用Ansible、Puppet等自动化工具统一部署user.cfg文件,确保所有服务器使用相同的安全策略。密码哈希可以统一管理,但每台服务器最好使用不同的密码,或者至少定期轮换。
内核升级时需要特别注意。当yum更新内核后,新内核的启动项会自动添加,如果10_linux配置正确,新启动项会自动继承密码保护设置。但建议在每次内核更新后检查grub.cfg,确认新menuentry包含了预期的“--users”限制。
加固效果的多层验证
配置完成后,应该从攻击者视角进行测试验证。重启服务器,在GRUB菜单尝试以下操作:
按“e”键编辑当前启动项,确认弹出认证提示。
按“c”键进入GRUB命令行模式,确认需要密码。
尝试直接按回车启动默认项,确认正常启动流程不受影响(如果配置了正常启动不需要密码)。
在启动参数中添加“init=/bin/bash”或“single”,确认即使通过某种方式绕过了GRUB编辑限制,系统启动后仍然需要root密码才能操作。这涉及到另一个安全配置:单用户模式也需要密码验证。编辑/etc/sysconfig/init文件(CentOS 6)或使用systemd的rescue.service配置(CentOS 7+),设置SINGLE=/sbin/sulogin,强制单用户模式要求root密码。
GRUB加密的局限性
GRUB加密只能防止通过GRUB引导流程的攻击。如果攻击者能物理接触到硬件,仍然可以通过以下方式绕过:
直接拔下硬盘挂载到其他系统读取数据。这需要配合磁盘加密(如LUKS)来防护。
使用U盘或光盘启动其他操作系统,然后挂载本地硬盘。这需要在BIOS/UEFI中设置启动顺序,禁用外部设备启动,并给BIOS设置密码。
重置BIOS/CMOS清除BIOS密码。部分服务器主板有防篡改检测,但消费级硬件通常没有。
因此,GRUB加密应该作为纵深防御体系中的一层,配合BIOS密码、磁盘加密、机箱锁、机房访问控制等措施,形成完整的安全链条。
自动化部署脚本示例
对于需要批量部署的场景,可以编写脚本自动化完成GRUB加密配置:
#!/bin/bash
# GRUB加密自动化部署脚本
# 适用于CentOS 7/8
GRUB_USER="admin"
GRUB_PASS="YourSecurePassword123!"
# 检查是否为root用户
if [ "$EUID" -ne 0 ]; then
echo "请使用root权限运行此脚本"
exit 1
fi
# 备份原始配置
cp /etc/grub.d/40_custom /etc/grub.d/40_custom.bak.$(date +%Y%m%d)
# 生成密码哈希
HASH=$(echo -e "$GRUB_PASS\n$GRUB_PASS" | grub2-mkpasswd-pbkdf2 | grep "grub.pbkdf2" | awk '{print $NF}')
if [ -z "$HASH" ]; then
echo "密码哈希生成失败"
exit 1
fi
# 写入配置
cat >> /etc/grub.d/40_custom << EOF
# GRUB密码保护配置
set superusers="$GRUB_USER"
password_pbkdf2 $GRUB_USER $HASH
export superusers
EOF
# 重新生成GRUB配置
if [ -d /sys/firmware/efi ]; then
grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg
else
grub2-mkconfig -o /boot/grub2/grub.cfg
fi
echo "GRUB加密配置完成"
echo "用户名: $GRUB_USER"
echo "请妥善保管密码,重启后生效"这个脚本会自动完成密码哈希生成、配置文件修改和GRUB配置重新生成,适合在初始化部署时集成到自动化流程中。使用前需要根据实际环境修改密码变量,并在测试环境验证后再推送到生产环境。
GRUB引导加密是一项基础但关键的安全配置,实施成本低、技术门槛不高,却能有效阻止大量针对物理访问的攻击场景。在等保2.0测评中,主机安全层面明确要求对引导程序进行访问控制,GRUB加密正是满足这一要求的标准做法。结合BIOS密码、磁盘加密、安全启动(Secure Boot)等技术,可以构建起从固件到操作系统的完整信任链,让攻击者在物理接触条件下也难以轻易突破。
