一、实验目标

在 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' {"

效果:

菜单权限
normalunrestricted
advancedunrestricted
recoveryprotected

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/bash
  • single user
  • recovery mode

等启动绕过攻击。



启动时按:

Shift (BIOS)
ESC (UEFI)

或修改:

GRUB_TIMEOUT_STYLE=menu
GRUB_TIMEOUT=10

参考

GRUB 官方文档:

https://www.gnu.org/software/grub/manual/grub/

此作者没有提供个人介绍。
最后更新于 2026-03-11