CVE-2026-102820
pageant: Out-of-bounds read / oversized allocation in `pageant` MemoryMap::read via a malicious Pageant agent (Windows)
pageant provides a [PageantStream] type that implements [AsyncRead] and [AsyncWrite] traits and can be used to talk to a running Pageant instance. Prior to pageant 0.2.3, the Windows pageant crate's pageant/src/wmmessage.rs MemoryMap::read function trusts a peer-controlled u32 response length supplied through the 8192-byte Pageant shared-memory mapping reached by AgentClient::connect_pageant. A local process that impersonates the Pageant window can make query_pageant_direct allocate up to approximately 4 GiB and copy beyond the mapped view, reliably crashing a russh client and conditionally exposing adjacent committed memory. This issue is fixed in pageant 0.2.3.
Attack profile
How easy it is to exploit
- Attack vector
- Local
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- None
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
6.2
pageant: Out-of-bounds read / oversized allocation in `pageant` MemoryMap::read via a malicious Pageant agent (Windows)
pageant provides a [PageantStream] type that implements [AsyncRead] and [AsyncWrite] traits and can be used to talk to a running Pageant instance. Prior to pageant 0.2.3, the Windows pageant crate's pageant/src/wmmessage.rs MemoryMap::read function trusts a peer-controlled u32 response length supplied through the 8192-byte Pageant shared-memory mapping reached by AgentClient::connect_pageant. A local process that impersonates the Pageant window can make query_pageant_direct allocate up to approximately 4 GiB and copy beyond the mapped view, reliably crashing a russh client and conditionally exposing adjacent committed memory. This issue is fixed in pageant 0.2.3.
- Record
CVE-2026-102820- CVSS 3.1
- 6.2 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-125, CWE-789
- Updated
- 2026-11-02
EUVDEUVD-2026-89203
6.2
pageant provides a [PageantStream] type that implements [AsyncRead] and [AsyncWrite] traits and can be used to talk to a running Pageant instance. Prior to pageant 0.2.3, the Windows pageant crate's pageant/src/wmmessage.rs MemoryMap::read function trusts a peer-controlled u32 response length supplied through the 8192-byte Pageant shared-memory mapping reached by AgentClient::connect_pageant. A local process that impersonates the Pageant window can make query_pageant_direct allocate up to approximately 4 GiB and copy beyond the mapped view, reliably crashing a russh client and conditionally exposing adjacent committed memory. This issue is fixed in pageant 0.2.3.
- Record
EUVD-2026-89203- CVSS 3.1
- 6.2 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-g4mp-vgx3-xrvm
6.2
pageant: Out-of-bounds read / oversized allocation in `pageant` MemoryMap::read via a malicious Pageant agent (Windows)
Summary
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".
Impact
- Availability / DoS (reliable).
MemoryMap::read(size)walks off the end of the 8192-byte view and faults on the next, unmapped page — anEXCEPTION_ACCESS_VIOLATIONthat crashes the russh SSH client. Independently, asizenearu32::MAXdrives a ~4 GiBvec![0; n]before any copy. - Confidentiality (conditional). If memory immediately after the mapped view
happens to be committed,
readreturns 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 theWM_COPYDATACOPYDATASTRUCT. Any local process can register a window of class + title"Pageant", receive that name, open the same mapping, and write a hostilesize. So an unprivileged local process impersonating Pageant can attack every russh-based SSH client that uses the Pageant agent.
Affected component
pageant/src/wmmessage.rsMemoryMap::read(:160-171) — no bound (contrastMemoryMap::write:139-158, which returnsError::Overflowwhenpos + len > length).query_pageant_direct(:199-237) — reads a 4-byteu32size from the shared mapping (:233) and callsmap.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
…
- Record
GHSA-g4mp-vgx3-xrvm- CVSS 3.1
- 6.2 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-125, CWE-789
- Updated
- 2026-11-02
NVD
6.2
pageant provides a [PageantStream] type that implements [AsyncRead] and [AsyncWrite] traits and can be used to talk to a running Pageant instance. Prior to pageant 0.2.3, the Windows pageant crate's pageant/src/wmmessage.rs MemoryMap::read function trusts a peer-controlled u32 response length supplied through the 8192-byte Pageant shared-memory mapping reached by AgentClient::connect_pageant. A local process that impersonates the Pageant window can make query_pageant_direct allocate up to approximately 4 GiB and copy beyond the mapped view, reliably crashing a russh client and conditionally exposing adjacent committed memory. This issue is fixed in pageant 0.2.3.
- Record
CVE-2026-102820- CVSS 3.1
- 6.2 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-125, CWE-789
- Updated
- 2026-11-02
OSV
6.2
pageant: Out-of-bounds read / oversized allocation in `pageant` MemoryMap::read via a malicious Pageant agent (Windows)
pageant provides a [PageantStream] type that implements [AsyncRead] and [AsyncWrite] traits and can be used to talk to a running Pageant instance. Prior to pageant 0.2.3, the Windows pageant crate's pageant/src/wmmessage.rs MemoryMap::read function trusts a peer-controlled u32 response length supplied through the 8192-byte Pageant shared-memory mapping reached by AgentClient::connect_pageant. A local process that impersonates the Pageant window can make query_pageant_direct allocate up to approximately 4 GiB and copy beyond the mapped view, reliably crashing a russh client and conditionally exposing adjacent committed memory. This issue is fixed in pageant 0.2.3.
- Record
CVE-2026-102820- CVSS 3.1
- 6.2 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-125, CWE-789
- Updated
- 2026-11-02
OSVDEBIAN-CVE-2026-102820
6.2
pageant provides a [PageantStream] type that implements [AsyncRead] and [AsyncWrite] traits and can be used to talk to a running Pageant instance. Prior to pageant 0.2.3, the Windows pageant crate's pageant/src/wmmessage.rs MemoryMap::read function trusts a peer-controlled u32 response length supplied through the 8192-byte Pageant shared-memory mapping reached by AgentClient::connect_pageant. A local process that impersonates the Pageant window can make query_pageant_direct allocate up to approximately 4 GiB and copy beyond the mapped view, reliably crashing a russh client and conditionally exposing adjacent committed memory. This issue is fixed in pageant 0.2.3.
- Record
DEBIAN-CVE-2026-102820- CVSS 3.1
- 6.2 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
OSVGHSA-g4mp-vgx3-xrvm
6.2
pageant: Out-of-bounds read / oversized allocation in `pageant` MemoryMap::read via a malicious Pageant agent (Windows)
Summary
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".
Impact
- Availability / DoS (reliable).
MemoryMap::read(size)walks off the end of the 8192-byte view and faults on the next, unmapped page — anEXCEPTION_ACCESS_VIOLATIONthat crashes the russh SSH client. Independently, asizenearu32::MAXdrives a ~4 GiBvec![0; n]before any copy. - Confidentiality (conditional). If memory immediately after the mapped view
happens to be committed,
readreturns 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 theWM_COPYDATACOPYDATASTRUCT. Any local process can register a window of class + title"Pageant", receive that name, open the same mapping, and write a hostilesize. So an unprivileged local process impersonating Pageant can attack every russh-based SSH client that uses the Pageant agent.
Affected component
pageant/src/wmmessage.rsMemoryMap::read(:160-171) — no bound (contrastMemoryMap::write:139-158, which returnsError::Overflowwhenpos + len > length).query_pageant_direct(:199-237) — reads a 4-byteu32size from the shared mapping (:233) and callsmap.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
…
- Record
GHSA-g4mp-vgx3-xrvm- CVSS 3.1
- 6.2 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-125, CWE-789
- Updated
- 2026-11-02
OSVUBUNTU-CVE-2026-102820
6.2
pageant provides a [PageantStream] type that implements [AsyncRead] and [AsyncWrite] traits and can be used to talk to a running Pageant instance. Prior to pageant 0.2.3, the Windows pageant crate's pageant/src/wmmessage.rs MemoryMap::read function trusts a peer-controlled u32 response length supplied through the 8192-byte Pageant shared-memory mapping reached by AgentClient::connect_pageant. A local process that impersonates the Pageant window can make query_pageant_direct allocate up to approximately 4 GiB and copy beyond the mapped view, reliably crashing a russh client and conditionally exposing adjacent committed memory. This issue is fixed in pageant 0.2.3.
- Record
UBUNTU-CVE-2026-102820- CVSS 3.1
- 6.2 Medium
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
pageantcrates.io |
through 0.2.2 | 0.2.3 |
rust-russhDebian:14 |
before 0.64.1-1, plus listed versions 0.57.1-10.57.1-2 |
0.64.1-1 |
pageantcrates.io |
before 0.2.3 | 0.2.3 |
rust-russhUbuntu:26.10 |
All versions, plus listed versions 0.57.1-2 |
None known |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-102820 is a medium vulnerability (CVSS 6.2) in Eugeny Russh, published September 29, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.19%, the 8th percentile. Fixed in pageant 0.2.3.
References
github.com/Eugeny/russh/security/advisories/GHSA-g4mp-vgx3-xrvm
github.com/Eugeny/russh/commit/5d566989ebabfdebfe6b33243d31765a0812260b
github.com/Eugeny/russh/releases/tag/v0.63.2
nvd.nist.gov/vuln/detail/CVE-2026-102820
github.com/advisories/GHSA-g4mp-vgx3-xrvm
github.com/CVEProject/cvelistV5/tree/main/cves/2026/102xxx/CVE-2026-102820.json
Left out here: the rest of 2 source descriptions. The JSON record has them.