裸金属固件层加固横评:IPMI 2.0 零凭据吐出管理员密码哈希、Redfish 会话 30 天不失效——从 BMC 隔离到 kernel lockdown 的 6 组实测
云主机的安全加固清单大家都背得出来:SSH 换端口禁 root、fail2ban、SELinux enforcing、一堆 sysctl。但裸金属物理机在操作系统下面还压着三层东西——BMC 固件、UEFI/引导链、PCIe DMA 路径。这三层在云主机上是厂商的责任边界,在裸金属上全是你的,而且出厂默认基本都是敞开状态。
我们拿一台双路 32 核 + LSI RAID + 双口 25G 的裸金属,在同一硬件上跑了 6 组配置对比。结论先放:不做固件层加固的裸金属,攻击面比一台同配置、同 SSH 加固的云主机大得不是一个量级——因为下面那三层的入口根本不在操作系统里。
一、先把多出来的口子摊开
从业务网视角扫这台机器所在的 /24,普通扫法只会看到 22 和 443。但很多机房交付时,BMC 网口和业务网口插在同一个交换机的默认 VLAN 里。
nmap -sU -p 623 --script ipmi-version 10.20.30.0/24
nmap -p 443,5900,5985,5986,623 -sV 10.20.30.0/24
PORT STATE SERVICE
623/udp open asf-rmcp # IPMI 2.0 —— RAKP 哈希泄露入口
443/tcp open ssl/http # BMC WebUI
5900/tcp open vnc # KVM over IP,画面与键鼠直通
5985/tcp open http # Redfish,明文降级可能
5986/tcp open ssl/http # Redfish over TLS
重点看 623/udp。它不需要你先出示任何凭据就能交互。
二、测试组 A:IPMI 2.0 的 RAKP 缺陷,零凭据拿密码哈希
这不是谁配错了,是 IPMI 2.0 协议本身的设计缺陷(CVE-2013-4786)。RAKP 认证的第二阶段,服务器会把用管理员密码参与运算的 HMAC-SHA1 直接吐给你,而这个阶段不要求你先证明身份。任何实现 IPMI 2.0 的 BMC——iDRAC、iLO、Supermicro、国产各家——全中。
msf6 > use auxiliary/scanner/ipmi/ipmi_dumphashes
msf6 auxiliary(scanner/ipmi/ipmi_dumphashes) > set RHOSTS 10.20.30.11
msf6 auxiliary(scanner/ipmi/ipmi_dumphashes) > set OUTPUT_HASHCAT_FILE /tmp/ipmi.hc
msf6 auxiliary(scanner/ipmi/ipmi_dumphashes) > run
[+] 10.20.30.11:623 - IPMI - Hash found: ADMIN:8a3f1c9e...(20 bytes):5b21...(16 bytes)
拿到的哈希喂 hashcat,mode 7300,单张 4090 实测:
hashcat -m 7300 /tmp/ipmi.hc /usr/share/wordlists/rockyou.txt -O -w 3
# ADMIN / 8 位小写+数字 → 41 秒
# 厂商默认密码字典 → 命中率约 27%
# 12 位「业务线缩写+年份」 → 3 小时 12 分
拿到 BMC 密码意味着什么?不是"能看温度"。iDRAC/iLO 里有虚拟介质挂载、KVM 控制台、SOL 串口重定向。你可以挂一个 ISO 上去重启装系统,也可以走 SOL 在 GRUB 阶段敲 init=/bin/sh。从 BMC 到 root,中间没有任何边界。
加固动作:最干脆的是关掉 IPMI over LAN,只留 Redfish over TLS。
curl -sk -u admin:'xxx' -X PATCH \
https://10.20.30.11/redfish/v1/Managers/1/NetworkProtocol \
-H 'Content-Type: application/json' \
-d '{"IPMI":{"ProtocolEnabled":false}}'
# 回读校验(很多固件 PATCH 返回 200 但没落地,重启 BMC 会恢复)
curl -sk -u admin:'xxx' https://10.20.30.11/redfish/v1/Managers/1/NetworkProtocol \
| jq '.IPMI.ProtocolEnabled'
# false
如果业务真的依赖 IPMI(Zabbix 的 ipmi_exporter 就走 623/udp),退一步只开 cipher suite 3 和 17——这两个带认证和完整性校验,0/1/2 全部关掉。但只要 623/udp 在监听,就还有别的路。带外网口必须物理隔离到独立管理 VLAN,ACL 只放跳板机 IP。
三、测试组 B:Redfish 会话模型,token 默认能躺 30 天
关掉 IPMI 之后,Redfish 成了唯一入口,但它的默认会话策略同样松。
curl -sk -i -X POST https://10.20.30.11/redfish/v1/SessionService/Sessions \
-H 'Content-Type: application/json' \
-d '{"UserName":"admin","Password":"xxx"}'
HTTP/1.1 201 Created
Location: /redfish/v1/SessionService/Sessions/1
X-Auth-Token: 5f2b8c1d9e4a7f3b6c0d8e2a1b4f7c9d
这个 token 是 bearer token,抓包能拿到,中间人也能拿到(如果还在用 TLS 1.0/1.1 或弱套件)。三个坑:
- SessionTimeout 名义上 1800 秒,但不少固件实现不主动清理过期会话。我们在一台机器上 POST 完不发 DELETE,挂了 19 天回去 curl 还是 200。
- 会话数默认上限 4,不清理就占满,等于把合法管理员挤出去,顺手做一次 DoS。
- 部分固件对 token 不做源 IP 绑定,同一个 token 换 IP 照样可用。
加固:
# 1. 会话超时压到 5 分钟
curl -sk -u admin:'xxx' -X PATCH https://10.20.30.11/redfish/v1/SessionService \
-H 'Content-Type: application/json' -d '{"SessionTimeout":300}'
# 2. 只留 HTTPS,其他协议逐个 PATCH ProtocolEnabled=false
curl -sk -u admin:'xxx' -X PATCH https://10.20.30.11/redfish/v1/Managers/1/NetworkProtocol \
-H 'Content-Type: application/json' \
-d '{"HTTP":{"ProtocolEnabled":false},"SSH":{"ProtocolEnabled":true}}'
# 3. 定期拉会话列表,清僵尸
curl -sk -u admin:'xxx' https://10.20.30.11/redfish/v1/SessionService/Sessions | jq '.Members'
# 4. 支持 mTLS 的固件尽量上客户端证书,不支持的只能靠网络层 ACL 兜底
顺带说个交付层面的差异。我们在轻云互联订的几台裸金属,交付时 BMC 默认只在独立管理网段可达,业务口上 623/udp 和 5985 全是 filtered 状态,省了一轮补漏。这事看着小,但一批 30 台机器每台手工去改 NetworkProtocol、验一遍 PATCH 有没有落地,就是半天工时。
四、测试组 C:内核侧必须和固件层联动,否则白做
固件层堵住了,还得堵 DMA 和引导链。这两层不做,攻击者拿到一次 root(哪怕只是某个应用漏洞)就能把持久化写进下层,你重装系统都清不掉。
4.1 IOMMU:确认设备真的被隔离了
不要只写 intel_iommu=on 就以为完事。要检查每个设备是否落在独立的 IOMMU group 里——如果一个 group 同时挂着 25G 网卡和 RAID 卡,那 DMA 隔离就是假的。
# /etc/default/grub
GRUB_CMDLINE_LINUX="intel_iommu=on iommu=pt iommu.strict=1"
update-grub && reboot
dmesg | grep -E 'DMAR|IOMMU'
# DMAR: IOMMU enabled
# DMAR: DRHD base: 0x000000fbffc000 flags: 0x1
# 逐组列出设备
for g in /sys/kernel/iommu_groups/*; do
echo "group ${g##*/}: $(ls $g/devices | tr '\n' ' ')"
done
# 揪出隔离不彻底的组
for g in /sys/kernel/iommu_groups/*; do
n=$(ls $g/devices | wc -l)
[ $n -gt 2 ] && echo "$g 设备数=$n <- 隔离不彻底"
done
出现设备数 > 2 的 group,说明上游 PCIe 桥的 ACS 没开。BIOS 里找 "PCIe ACS Override" 或 "ARI/ACS";主机侧可以尝试 pcie_acs_override=downstream,multifunction,但这是下游补丁,主线内核没有,生产环境要单独评估。
4.2 kernel lockdown:把 /dev/mem 和 kexec 一起焊死
lockdown 是内核自带的 LSM,Secure Boot 下会自动激活,但裸金属上手动打开效果一样。
cat /sys/kernel/security/lockdown
# integrity [confidentiality] none ← 当前处于 confidentiality 档
# 手动强制(GRUB 追加)
GRUB_CMDLINE_LINUX="... lockdown=confidentiality"
打开 confidentiality 档后,实测被拒的操作:
# 1. /dev/mem 直接不可读
head -c 16 /dev/mem
# head: cannot open '/dev/mem' for reading: Operation not permitted
# 2. kexec 被关
cat /sys/kernel/kexec_load_disabled
# 1
# 3. 未签名内核模块加载失败
insmod ./evil.ko
# insmod: ERROR: could not insert module: Operation not permitted
dmesg | tail -1
# Lockdown: insmod: unsigned module loading is restricted
# 4. iopl/ioperm 返回 EPERM,硬件端口直读被断
# 5. MSR 只读,写入被拒
# 6. kprobes 对部分函数禁用,/proc/kcore 不可读
代价是什么?第一,DKMS 模块(ZFS、NVIDIA、自研驱动)必须签名;第二,依赖 kprobe 的带外监控 agent 会挂;第三,性能影响基本为零,lockdown 是纯权限检查,不在数据路径上。
mokutil --sb-state
# SecureBoot enabled
/usr/src/linux-headers-$(uname -r)/scripts/sign-file sha256 \
/var/lib/shim-signed/mok/MOK.priv \
/var/lib/shim-signed/mok/MOK.der \
/lib/modules/$(uname -r)/extra/zfs.ko
modinfo -F signer zfs.ko
4.3 GRUB 配置本身:init=/bin/sh 就在你眼皮底下
Secure Boot + lockdown 全开了,但 /boot/grub/grub.cfg 还是 0644。任何拿到 root 的人(或任何一次带外挂载)改一行,加个 init=/bin/sh,重启后直接进 shell——Secure Boot 签的是内核,不签你的启动命令行。
grub-mkpasswd-pbkdf2
# Enter password: ******
# grub.pbkdf2.sha512.10000.9F3A... ← 复制整行
# /etc/grub.d/40_custom
set superusers="root"
password_pbkdf2 root grub.pbkdf2.sha512.10000.9F3A...
export superusers
# 加了 superusers 后,所有菜单项都要密码才能编辑。
# 默认启动项要单独加 --unrestricted,否则每次开机都要输密码。
# 改 /etc/grub.d/10_linux 里的 menuentry 行,追加 --unrestricted
update-grub
chmod 600 /boot/grub/grub.cfg
这一步拦住的是"物理接触 / 带外控制台"这一类攻击,恰好是裸金属和云主机最大的区别所在。
五、六组配置横评
- 配置 1(出厂默认):IPMI 2.0 开 + Redfish HTTP 开 + BMC 与业务网同段。623/udp 可零凭据取哈希,5985 明文,5900 可暴力。从网络到 root 的路径数 ≥ 3。
- 配置 2(只改 BMC 密码):密码复杂度上去了,但 RAKP 哈希照样吐。把 8 位弱口令换成 16 位随机,爆破成本从 41 秒变成理论不可行——可一旦管理员在别处复用,弱口令字典照样打。治标。
- 配置 3(关 IPMI over LAN + Redfish 仅 TLS + 会话超时 300s + 管理 VLAN ACL):623/udp 消失,5985 关闭,BMC 攻击面基本封死。剩余风险是固件自身 0day——这两年 Redfish 实现的未授权信息泄露不止一例。
- 配置 4(3 + IOMMU strict + ACS 校验):堵住恶意 PCIe 设备和外接 Thunderbolt 的 DMA 直写。代价:iommu.strict=1 会让部分网卡中断延迟上升,实测 25G 网卡 P99 尾延迟从 42μs 到 55μs,可接受;100G RoCE 场景要先压测再定。
- 配置 5(4 + lockdown=confidentiality + Secure Boot):内核运行时锁死。代价是运维习惯要改——kexec 快速重启没了,kdump 要重配,所有 DKMS 模块要签名。这一档开始有明显的运维成本。
- 配置 6(5 + GRUB 密码 + /boot 完整性校验):完整信任链。剩下的攻击面基本只剩固件 0day 和物理拆机(BMC 的 SPI 闪存直接夹子重刷,这层软件防不住,只能靠机柜锁和出入审计)。
如果只有半天时间,做配置 3。如果还有两小时,加做 4.3 的 GRUB 密码。这两步挡住了绝大多数真实场景——我们复盘过的案例里,从 BMC 进去的比例远高于从应用打进去的。
六、几个容易踩的坑
- 改完 NetworkProtocol 忘了回读。有的固件 PATCH 返回 200 但不落地,BMC reset 后恢复默认。改完 GET 回读,再 reset 一次复验。
- 关 IPMI 把监控一起关了。ipmi_exporter 走的就是 623/udp。要么换 Redfish exporter,要么给监控单独开 cipher suite 3/17 加源 IP ACL。
- lockdown 和 kdump 冲突。confidentiality 档下 kexec_load_disabled=1,kdump 服务直接起不来。要么降回 integrity 档,要么重配 crashkernel 路径。
- iommu.strict 在不同内核上参数名不一致。5.10 之前是
iommu.passthrough=0,写错就是静默不生效,一定用dmesg | grep -i iommu确认落地。 - Secure Boot 开了但没清 MOK 列表。前一个管理员注册的自定义密钥还挂在里面,等于后门还开着。
mokutil --list-enrolled逐条核对。
七、收尾
裸金属的安全加固,本质是把你从"租户"变成了"平台方"。云主机下面那层是别人的责任,出了事换个实例;裸金属下面那层是你自己的,出了事得带着 U 盘进机房。所以在裸金属上往固件层花的每一小时,回报都比在操作系统层堆 fail2ban 规则高——因为那三层一旦被打穿,你在上面做的 SSH 加固、SELinux 策略、auditd 规则,全都不作数。