Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-53360-POC — PoC for CVE-2026-53360: guest-triggered heap out-of-bounds read/write in KVM SEV-SNP Page State Change (PSC) handling. | Kitploit
Tools/GitHubGitHub/0xcyberstan/cve-2026-53360-poc
Memory ForensicsVulnerability AnalysisExploitationHardware SecurityBinary Exploitation
GitHub0xcyberstan/cve-2026-53360-poc

CVE-2026-53360-POC

PoC for CVE-2026-53360: guest-triggered heap out-of-bounds read/write in KVM SEV-SNP Page State Change (PSC) handling.

View Repository
1112 months agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-53360: KVM SEV-SNP PSC heap out-of-bounds

Proof of concept for a heap out-of-bounds read and write in KVM's SEV-SNP Page State Change (PSC) handling. A malicious SEV-SNP guest makes the host kernel walk a PSC entry array off the end of its slab allocation. That leaks the layout of neighbouring kmalloc-cg-32 objects and writes a controlled small value into them, and the guest can repeat it as often as it likes.

Full writeup: https://cyberstan.co.uk/sev-snp-oob/

CVECVE-2026-53360
ComponentKVM SNP host support, arch/x86/kvm/svm/sev.c
Introduced9b54e248d264 (first KVM SNP PSC handling, May 2024, ~v6.10)
Fixeddb3f219 (mainline, May 2026, Cc: stable), tagged Fixes: 4af663c
Reported[email protected], 8 April 2026
AffectedSEV-SNP host path only. KVM does not enable PSC for plain SEV-ES guests.

Impact

Any SEV-SNP guest can corrupt the host kernel's heap and read back information about its layout by sending a malformed PSC request. This is the guest to host direction: SEV-SNP is built to protect the guest from an untrusted host, but the host still has to defend itself against a malicious guest, and this handler does not.

Requirements

This needs real SEV-SNP hardware. It cannot be reproduced on Intel, and nested virt will not give you an SNP guest.

Hardware:

  • An AMD EPYC server chip with SEV-SNP: Milan (7003) or newer, meaning Genoa (9004), Bergamo, Siena, or Turin. SEV-SNP is EPYC-only silicon. It is not on Ryzen or Threadripper, and there is no Intel equivalent here (Intel uses TDX).
  • Bare metal. A bare-metal cloud instance works too (Vultr, AWS *.metal, Hetzner AX, and similar).
  • In BIOS, enable SEV, SEV-ES, SEV-SNP, SME, IOMMU, and SVM. Managed bare-metal clouds usually ship these on already.

Host kernel:

  • Build it with KASAN so the out-of-bounds accesses get reported. Without KASAN the bug still corrupts host memory, it just is not printed. Tested on 6.11.11.
    CONFIG_KASAN=y
    CONFIG_KASAN_GENERIC=y
    CONFIG_KVM=y
    CONFIG_KVM_AMD=y
    CONFIG_KVM_AMD_SEV=y
    CONFIG_CRYPTO_DEV_SP_PSP=y
    
  • Boot with SNP enabled and KASAN in multi-shot mode so every hit is logged:
    kvm_amd.sev=1 kvm_amd.sev_es=1 kvm_amd.sev_snp=1 kasan_multi_shot
    
  • Confirm the host is ready:
    cat /sys/module/kvm_amd/parameters/sev_snp     # Y
    ls /dev/sev                                     # /dev/sev
    

Host userspace:

  • An SNP-capable QEMU. Stock QEMU does not do SNP, so build the AMD fork:
    git clone https://github.com/AMDESE/qemu.git
    cd qemu && git checkout snp-latest
    mkdir build && cd build
    ../configure --target-list=x86_64-softmmu && make -j$(nproc)
    
  • SNP OVMF firmware from https://github.com/AMDESE/AMDSEV/releases.

Guest:

  • Any Linux guest that boots under SNP, with build-essential and linux-headers-$(uname -r) installed so you can build the module in it.

The bug

SEV-SNP guests talk to the host through the GHCB, a 4 KB shared page. A PSC request sets SW_EXITCODE to SVM_VMGEXIT_PSC (0x80000010), points SW_SCRATCH at a descriptor, and puts the descriptor length in SW_EXITINFO2.

The descriptor is a struct psc_buffer: an 8-byte header followed by an array of 8-byte entries. There is no explicit count field. The host processes entries from hdr->cur_entry to hdr->end_entry, both guest controlled.

struct psc_hdr {
        u16 cur_entry;
        u16 end_entry;
        u32 reserved;
} __packed;                     /* 8 bytes */

struct psc_entry {
        u64 cur_page    : 12;
        u64 gfn         : 40;
        u64 operation   :  4;
        u64 pagesize    :  1;
        u64 reserved    :  7;
} __packed;                     /* 8 bytes */

A GHCB v2+ guest is supposed to keep its scratch area inside the GHCB's 2032-byte Shared Buffer, so the host can reuse its existing mapping. (2032 - 8) / 8 = 253 entries fit there, which is where the protocol maximum VMGEXIT_PSC_MAX_COUNT (253) comes from. That number only makes sense when the buffer really is the Shared Buffer.

If the guest points the scratch area outside the GHCB, the host cannot use its mapping, so setup_vmgexit_scratch() allocates a separate buffer of the size the guest asked for. SNP should never take this path, but nothing stops it:

scratch_va = kvzalloc(len, GFP_KERNEL_ACCOUNT);   /* len == exit_info_2, guest-controlled */

len comes straight from the guest, and GFP_KERNEL_ACCOUNT puts the allocation in the cgroup-accounted kmalloc-cg-N caches. Ask for exit_info_2 = 24 and you get a 24-byte allocation in the 32-byte kmalloc-cg-32 slot: room for the header plus two entries. Everything past entries[1] is another object's memory.

Then snp_begin_psc() checks the entry count against the protocol constant, not against the buffer it actually allocated:

idx_end = hdr->end_entry;

if (idx_end >= VMGEXIT_PSC_MAX_COUNT) {   /* checks 253, NOT the buffer size */
        snp_complete_psc(svm, ...);
        return 1;
}

for (idx = idx_start; idx <= idx_end; idx++) {
        entry_start = entries[idx];       /* OOB once idx >= 2 */
        ...
}

With a 24-byte buffer only two entries exist, but the check allows end_entry up to 252. Set it to 252 and the loop walks about 2 KB past the allocation, across neighbouring slab objects.

Primitives

Each step past the end reinterprets the next 8 bytes of slab memory as a psc_entry and runs it through the PSC code. That gives three things:

  1. Read oracle. The host reads the adjacent qword just to decode it, pulling entry.gfn and entry.operation out of memory the buffer never owned. This is the slab-out-of-bounds read KASAN catches.
  2. Constrained write. If the decoded entry looks valid and gets dispatched as a KVM_HC_MAP_GPA_RANGE, the completion code writes back into the same OOB slot: entries[idx].cur_page = entry.pagesize ? 512 : 1. One of two small values into the low 12 bits of a word the guest picks, repeatable.
  3. Failure oracle. If the entry does not validate, the response in SW_EXITINFO2 reports the index it stopped at. Bumping end_entry one at a time leaks, slot by slot, whether adjacent memory decoded to a no-op or to something that failed, which is enough to find object boundaries and tell zero from non-zero.

Each VMGEXIT re-allocates the scratch buffer, so repeated requests land in different freelist slots and let the guest sweep across neighbours rather than being stuck with one. Put together this yields heap layout disclosure, the constrained write above, and use-after-free across requests.

What the PoC does

trigger.c is a guest kernel module. Load it inside an SEV-SNP guest and it drives four stages against the host from a single insmod:

  • Stage 1 probes 48 out-of-bounds entries one at a time and builds a map of the host heap (zero vs non-zero neighbouring memory).
  • Stage 2 proves the OOB write persists across VMGEXITs by writing cur_page into a zero neighbour and confirming a later request skips it.
  • Stage 3 fires one request with end_entry=200 and measures how far the OOB read reaches before hitting non-zero data.
  • Stage 4 fires 200 requests with entries[3..10] out of bounds, each of which trips a KASAN report on the host.

The module allocates a page, marks it decrypted with set_memory_decrypted(), uses it as the scratch area, and hand-builds the GHCB PSC request. It exits with -EAGAIN so it does not stay loaded.

Building and running

1. Launch an SNP guest

Adjust the OVMF and disk paths to your setup:

Download Tool