CVE-2026-102820

CVE-2026-102820 is a medium-severity out-of-bounds read vulnerability in pageant (rust), affecting versions <= 0.2.2. It is fixed in 0.2.3.

Does this CVE actually affect you?

Kodem shows which CVEs are reachable and running in your applications, so you fix what's exploitable, not just what's listed.

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Runtime intelligence, not another scanner.

Summary

pageant: Out-of-bounds read / oversized allocation in pageant MemoryMap::read via a malicious Pageant agent (Windows)

MemoryMap::read in the pageant crate (part of the russh workspace, used by
russh's SSH-agent client on Windows via AgentClient::connect_pageant) copies a
peer-controlled number of bytes out of an 8192-byte shared-memory view with
no bounds check, unlike the sibling MemoryMap::write, which correctly
rejects oversize access with Error::Overflow. The byte count comes straight
from a u32 length prefix that the responding "Pageant" process writes into the
shared mapping. A malicious local process that answers as the Pageant agent can
therefore cause:

  • an out-of-bounds read past the 8 KiB view (access violation → process
    crash; or disclosure of adjacent process memory if the following page is
    committed)
  • an allocation of up to ~4 GiB from a single u32 (vec![0; n]).

This was reproduced end-to-end against the real, unmodified pageant crate
(not a model) on x86_64-pc-windows-gnu under Wine; see "Proof of concept".

Affected component

  • pageant/src/wmmessage.rs
    • MemoryMap::read (:160-171), no bound (contrast MemoryMap::write
      :139-158, which returns Error::Overflow when pos + len > length).
    • query_pageant_direct (:199-237), reads a 4-byte u32 size from the
      shared mapping (:233) and calls map.read(size) (:234) with no check
      against _AGENT_MAX_MSGLEN (8192).
  • Reached from russh via AgentClient::connect_pageant → PageantStream →
    query_pageant_direct.

Platform: Windows only (cfg(windows)), local attacker. Verified against
the pageant crate v0.2.2 as shipped in russh v0.63.1 (d3ae702, the
latest release). read has never had a bound in any revision
(git log -p -- pageant/src/wmmessage.rs).

Details

fn write(&mut self, data: &[u8]) -> Result<(), Error> {
    if self.pos + data.len() > self.length {      // :140  BOUND PRESENT
        return Err(Error::Overflow);
    }
    ... copy_nonoverlapping(&data[0], view+pos, data.len()) ...
}

fn read(&mut self, n: usize) -> Vec<u8> {         // :160  NO BOUND
    let out = vec![0; n];                         // n up to 0xFFFF_FFFF (CWE-789)
    unsafe {
        std::ptr::copy_nonoverlapping(
            self.view.Value.add(self.pos) as *const u8,   // view is length==8192
            out.as_ptr() as *mut u8,
            n,                                    // reads n bytes, may run past the view (CWE-125)
        );
    }
    self.pos += n;
    out
}

query_pageant_direct creates the mapping at _AGENT_MAX_MSGLEN = 8192, writes
the request, sends the WM_COPYDATA, then reads the response the peer wrote:

map.seek(0);
let mut buf = map.read(4);
let size = u32::from_be_bytes([buf[0],buf[1],buf[2],buf[3]]) as usize; // :233 peer-controlled
buf.extend(map.read(size));                                            // :234 unbounded

Nothing checks 4 + size <= 8192, so map.read(size) runs past the 8 KiB view.

Proof of concept

Because the bug is Windows-only (WM_COPYDATA + MapViewOfFile), the PoC is a
Windows cross-build (x86_64-pc-windows-gnu) driven under Wine, entirely
inside a Linux container. It exercises the real, unmodified crate: the PoC
takes a path dependency on pageant and calls
pageant::wmmessage::query_pageant_direct, exactly what
AgentClient::connect_pageant uses. A second thread impersonates Pageant
(registers the window class + title "Pageant") and, on WM_COPYDATA, opens
the shared mapping russh created and writes an attacker-chosen 4-byte
big-endian length.

poc/run.sh builds and runs it. Full log in results/e2e-wine-run.log:

======== LEG 1, ATTACK: fake agent reports length 0x00080000 (512 KiB) >> 8192-byte view ========
[attacker] impersonating Pageant window is up (class+title "Pageant")
[victim ] calling pageant::wmmessage::query_pageant_direct() over the 8192-byte view ...
[attacker] WM_COPYDATA received; shared mapping = "PageantRequestpoc"
[attacker] wrote hostile response length = 524288 (0x00080000) into the 8192-byte view
wine: Unhandled page fault on read access to 0000000002092000 at address 00000002282CFDC4 ...
WINE-EXIT=5

======== LEG 2, CONTROL: fake agent reports length 0x00000010 (16 B), in-bounds ========
[victim ] returned 20 bytes, request+response fit inside the 8192-byte view (in-bounds control); no fault
WINE-EXIT=0
  • LEG 1 (attack): an oversized size makes MemoryMap::read read past the
    8192-byte view; Wine reports an unhandled page fault at a page-aligned address
    (0x…2092000), the out-of-bounds read as an access violation (crash). Exit
    code 5 = STATUS_ACCESS_VIOLATION.
  • LEG 2 (control): an in-bounds size returns cleanly. Same code path; the
    only difference is whether the peer's length exceeds the view, isolating the
    missing bound.

Fix validation. With patch/pageant-read-bound.patch applied and the PoC
rebuilt against the patched crate, the identical attack input is rejected
(results/patched-wine-run.log):

[attacker] wrote hostile response length = 524288 (0x00080000) into the 8192-byte view
[victim ] query_pageant_direct error: Overflow
WINE-EXIT=0

No fault; the read is refused at the bound, exactly as write already refuses
oversize writes. (A pure-logic Linux model of the same control flow is also
included as poc/pageant_read_oob_demo.rs / results/logic-demo-run.log.)

Impact

  • Availability / DoS (reliable). MemoryMap::read(size) walks off the end of
    the 8192-byte view and faults on the next, unmapped page, an
    EXCEPTION_ACCESS_VIOLATION that crashes the russh SSH client. Independently,
    a size near u32::MAX drives a ~4 GiB vec![0; n] before any copy.
  • Confidentiality (conditional). If memory immediately after the mapped view
    happens to be committed, read returns those adjacent bytes to russh as the
    "agent response", which russh then parses as agent identities/signatures. This
    arm depends on process memory layout, so it is opportunistic; the crash/alloc
    is the deterministic outcome.
  • Trust boundary. russh locates the agent with
    FindWindowW("Pageant", "Pageant") and passes the shared-mapping name inside
    the WM_COPYDATA COPYDATASTRUCT. Any local process can register a window
    of class + title "Pageant", receive that name, open the same mapping, and
    write a hostile size. So an unprivileged local process impersonating Pageant
    can attack every russh-based SSH client that uses the Pageant agent.

A read operation accesses a memory location beyond the intended buffer boundary. Typical impact: sensitive data disclosure or crash.

CVE-2026-102820 has a CVSS score of 6.2 (Medium). The vector is requires local access, no privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (0.2.3); upgrading removes the vulnerable code path.

Affected versions

pageant (<= 0.2.2)

Security releases

pageant → 0.2.3 (rust)

Kodem intelligence

Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.

Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.

Already deployed Kodem?

See it in your environmentNew to Kodem? Get a demo →

Remediation advice

Mirror write's guard in read and validate the response length before
allocating/copying. patch/pageant-read-bound.patch:

  • MemoryMap::read(n) returns Result<Vec<u8>, Error> and returns
    Error::Overflow when self.pos + n > self.length;
  • query_pageant_direct propagates that Result and additionally rejects
    size > _AGENT_MAX_MSGLEN - 4 before map.read(size).

Frequently Asked Questions

  1. What is CVE-2026-102820? CVE-2026-102820 is a medium-severity out-of-bounds read vulnerability in pageant (rust), affecting versions <= 0.2.2. It is fixed in 0.2.3. A read operation accesses a memory location beyond the intended buffer boundary.
  2. How severe is CVE-2026-102820? CVE-2026-102820 has a CVSS score of 6.2 (Medium). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
  3. Which versions of pageant are affected by CVE-2026-102820? pageant (rust) versions <= 0.2.2 is affected.
  4. Is there a fix for CVE-2026-102820? Yes. CVE-2026-102820 is fixed in 0.2.3. Upgrade to this version or later.
  5. Is CVE-2026-102820 exploitable, and should I be worried? Whether CVE-2026-102820 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
  6. What actually determines whether CVE-2026-102820 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
  7. How do I fix CVE-2026-102820? Upgrade pageant to 0.2.3 or later.

Stop the waste.
Protect your environment with Kodem.