一、实验目标
在 Ubuntu 24.04 虚拟机上实现:
| 操作 | 是否需要 GRUB 密码 |
|---|---|
| 正常启动 Ubuntu | 不需要 |
编辑启动参数 (e) | 需要 |
GRUB shell (c) | 需要 |
| recovery mode | 需要 |
目标是:
正常启动无感知,但危险入口必须被 GRUB 口令保护。
二、实验环境
OS: Ubuntu 24.04
Bootloader: GRUB 2.12
三、GRUB 绕过登录的原理
如果没有 GRUB 保护,可以在启动菜单按 e 修改启动参数:
linux ... init=/bin/bash
系统会直接进入 root shell:
whoami
root

攻击者可以:
- 重置密码
- 读取数据
- 拷贝文件
因此需要对 GRUB 菜单进行访问控制。
四、配置 GRUB 密码保护
1 生成 GRUB 密码
执行:
sudo grub-mkpasswd-pbkdf2
输入两次密码。
得到类似输出:
grub.pbkdf2.sha512.10000.xxxxxxxxxxxxxxxxx
复制这段哈希。(不推荐写明文配置)
2 创建 GRUB 用户配置
创建文件:
sudo nano /etc/grub.d/01_users
写入:
#!/bin/sh
cat << EOF
set superusers="admin"
password_pbkdf2 admin grub.pbkdf2.sha512.xxxxxxxxxxxxxxxxx
export superusers
EOF
说明:
admin为 GRUB 用户名- 使用 PBKDF2 哈希存储密码

3 赋予执行权限
这是容易遗漏的一步。
sudo chmod 755 /etc/grub.d/01_users
如果没有执行权限:
update-grub 不会加载该配置
4 修改启动项权限
前置链接:10_linux 其实是一段 生成 GRUB 菜单的脚本模板,改的不是“配置”,而是 菜单生成逻辑。
编辑:
sudo nano /etc/grub.d/10_linux
找到 linux_entry() 函数。
将 menuentry 修改为:
case $type in
recovery)
echo "menuentry '$(echo "$title" | grub_quote)' ${CLASS} \$menuentry_id_option 'gnulinux-$version-$type-$boot_device_id' {"
;;
*)
echo "menuentry '$(echo "$title" | grub_quote)' ${CLASS} --unrestricted \$menuentry_id_option 'gnulinux-$version-$type-$boot_device_id' {"
;;
esac
在 linux_entry() 里面应该是:
if [ x$type != xsimple ] ; then
case $type in
recovery)
title="$(gettext_printf "%s, with Linux %s (%s)" "${os}" "${version}" "$(gettext "${GRUB_RECOVERY_TITLE}")")"
;;
*)
title="$(gettext_printf "%s, with Linux %s" "${os}" "${version}")"
;;
esac
case $type in
recovery)
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)' ${CLASS} --unrestricted \$menuentry_id_option 'gnulinux-$version-$type-$boot_device_id' {" | sed "s/^/$submenu_indentation/"
;;
esac
else
echo "menuentry '$(echo "$os" | grub_quote)' ${CLASS} --unrestricted \$menuentry_id_option 'gnulinux-simple-$boot_device_id' {" | sed "s/^/$submenu_indentation/"
fi

顶层启动项:
echo "menuentry '$(echo "$os" | grub_quote)' ${CLASS} --unrestricted \$menuentry_id_option 'gnulinux-simple-$boot_device_id' {"
效果:
| 菜单 | 权限 |
|---|---|
| normal | unrestricted |
| advanced | unrestricted |
| recovery | protected |
5 生成 GRUB 配置
执行:
sudo update-grub
五、验证配置
检查 grub.cfg:
grep menuentry /boot/grub/grub.cfg
示例:
menuentry 'Ubuntu' ... --unrestricted
menuentry 'Ubuntu, with Linux 6.x.x' ... --unrestricted
menuentry 'Ubuntu, with Linux 6.x.x (recovery mode)'

说明:
- 普通启动项免认证
- recovery 需要认证
六、功能验证
重启系统:
sudo reboot
测试以下场景:
1 正常启动
Ubuntu
结果:
正常启动
无需 GRUB 密码
2 编辑启动参数
按:
e
结果:
Enter username:
3 GRUB shell
按:
c
结果:
需要 GRUB 用户认证
4 recovery mode
进入:
Advanced options → recovery mode
结果:
需要 GRUB 密码

七、安全边界(重要)
GRUB 保护 不能防止离线读取磁盘。
如果攻击者可以:
- 获取虚拟磁盘文件
- 挂载 qcow2 / vmdk
- 使用救援系统启动
仍然可以读取数据。
因此生产环境通常需要配合:
- LUKS 磁盘加密
- 平台权限隔离
- 禁止挂载 ISO
八、实验总结
通过 GRUB 用户认证 + --unrestricted 启动策略,可以实现:
正常启动免认证
危险操作强制认证
这种方式能够有效防止:
init=/bin/bashsingle userrecovery mode
等启动绕过攻击。
启动时按:
Shift (BIOS)
ESC (UEFI)
或修改:
GRUB_TIMEOUT_STYLE=menu
GRUB_TIMEOUT=10
参考
GRUB 官方文档:
https://www.gnu.org/software/grub/manual/grub/
Comments NOTHING