CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



GHSA-7587-4WV6-M68M

Panic at the Keyring: Crashing rPGP with a Single Byte

Amit Schendel
Amit Schendel
Senior Security Researcher

Feb 14, 2026·7 min read·54 visits

Executive Summary (TL;DR)

The `rPGP` library (via the `rsa` crate < 0.9.10) contains a logic flaw in validating RSA private key components. By providing a crafted Secret Key Packet where one of the prime factors is set to 1, the library attempts to calculate modulus inverses using (p-1), resulting in a division-by-zero panic. This crashes the application process, leading to a Denial of Service (DoS) against keyservers, email clients, or signing services using the library.

In the world of safe systems programming, Rust is the golden child. It promises to save us from the memory corruption sins of C and C++. But while Rust protects memory, it doesn't protect logic. A critical denial-of-service vulnerability was discovered in the `rPGP` library (and its dependency, the `rsa` crate) where a mathematically impossible RSA key component triggers a hard panic. By setting a prime factor to '1', an attacker can trick the underlying arithmetic engine into a division-by-zero scenario, crashing any application attempting to parse the key. This is a story about how 'safe' languages still need defensive coding.

The Hook: Even Rust Can Bleed

We love Rust. It gives us warm fuzzy feelings about memory safety. No more use-after-free, no more buffer overflows, just pure, borrow-checked bliss. But here's the dirty secret: panic! is safe. Memory-safe, that is. If a Rust program encounters an unrecoverable state, it unwinds the stack and dies. In a command-line tool, that's annoying. In a high-availability web service processing thousands of PGP keys, that's a disaster.

rPGP is the premier pure-Rust implementation of OpenPGP. It's designed to replace GnuPG in modern stacks. It handles the messy, decades-old RFC 4880 standard so you don't have to. But parsing OpenPGP is like walking through a minefield while blindfolded. The format allows for complex nested packets, and deeply buried within those packets are the cryptographic primitives: RSA, DSA, Ed25519.

This vulnerability isn't about memory corruption. It's about math. specifically, the kind of math that breaks computers. It resides in how the underlying rsa crate handles the components of a private key. It turns out, if you lie to the library about your prime numbers, you can trick it into committing the cardinal sin of arithmetic: dividing by zero. The result? The application vanishes into the void.

The Flaw: The Mathematical Impossibility

To understand this bug, we have to go back to RSA 101. An RSA private key consists of a modulus $n$, a public exponent $e$, and a private exponent $d$. But to optimize calculations (specifically using the Chinese Remainder Theorem, or CRT), the private key usually also stores the two prime factors, $p$ and $q$, such that $n = p \times q$.

When rPGP parses a Secret-Key Packet (Tag 5), it extracts these numbers and passes them to the rsa crate to build an RsaPrivateKey object. The rsa crate, being responsible, tries to validate these numbers. You can't just accept any garbage integers; they have to look like primes.

Here lies the error. In rsa versions prior to 0.9.10, the validation logic checked if the primes were "too small". Specifically, it checked if a prime was effectively zero. But BigUint (unsigned big integers) don't have negative numbers. The check looked something like if prime < 1. This catches 0. But it allows 1.

Why is 1 dangerous? Because nearly every RSA-CRT optimization involves the value $(p-1)$. For example, Euler's totient function $\phi(n) = (p-1)(q-1)$. If $p=1$, then $p-1=0$. Suddenly, you are calculating modulo 0 or dividing by 0. The num-bigint library, which handles the heavy lifting, essentially says "I can't do that" and triggers a thread-terminating panic.

The Code: One Character Difference

Let's look at the smoking gun. This is found in src/key.rs of the rsa crate. The developers intended to ensure the prime was valid, but they missed an edge case.

The Vulnerable Code (rsa < 0.9.10):

// Iterate over the primes provided in the key components
for prime in &self.primes {
    // ALERT: This only errors if prime is 0. 
    // If prime is 1, this check passes!
    if *prime < BigUint::one() {
        return Err(Error::InvalidPrime);
    }
    m *= prime;
}
// ... later, (prime - 1) causes panic ...

Because BigUint is an unsigned integer type, < 1 is strictly equivalent to == 0. The integer 1 slips past the guard. Moments later, the code attempts to compute derived values, likely involving prime - 1 for CRT parameters. When that subtraction results in a BigUint of zero, and that zero is used as a divisor, the program crashes.

The Fix (Commit 2926c91):

for prime in &self.primes {
    // The fix: change '<' to '<='
    // Now, if prime is 0 OR 1, it rejects it.
    if *prime <= BigUint::one() {
        return Err(Error::InvalidPrime);
    }
    m *= prime;
}

It is almost comical that a single equals sign = stands between a stable server and a denial of service. This highlights a classic issue in input validation: checking for the presence of data rather than the semantic validity of data.

The Exploit: Crafting the Poison Packet

Exploiting this is trivial for anyone who understands the OpenPGP packet format (RFC 4880). We don't need to break encryption; we just need to construct a structurally valid but mathematically suicidal packet.

The Attack Chain

  1. Packet Header: Create a Secret-Key Packet (Tag 5).
  2. Version: Use Version 4 (standard).
  3. Algorithm: Select RSA (ID 1).
  4. Public Key Data: Generate a valid Modulus ($n$) and Exponent ($e$). These can be real or junk, as long as they parse as MPIs (Multiprecision Integers).
  5. Encrypted Secret Data: Standard OpenPGP keys encrypt the secret parts (d, p, q, u) with a passphrase. However, we can set the "String-to-Key Usage" byte to 0, indicating unencrypted secret data. This bypasses the need for a password and forces the library to parse the raw integers immediately.
  6. The Payload: When writing the MPIs for the secret components:
    • d: Arbitrary integer.
    • p: Set this to 1.
    • q: Arbitrary integer.
    • u: Arbitrary integer.

When rPGP reads this packet, it deserializes the MPIs. It sees that the key is unencrypted, so it attempts to construct the RsaPrivateKey struct immediately to validate it. It calls RsaPrivateKey::from_components(n, e, d, vec![p, q]).

Inside that function, p (which is 1) passes the Check < 1. Then, the math engine attempts a modular inverse or reduction. BOOM. The panic unwinds the stack. If the calling application hasn't explicitly wrapped this in a catch_unwind (and most don't, because libraries shouldn't panic on inputs), the process terminates.

The Impact: Availability is the 'A' in CIA

Why does this matter? "It's just a crash," you say. In the modern cloud era, a crash is a vulnerability.

Consider the following scenarios:

  • Keyservers: Public OpenPGP keyservers (like keys.openpgp.org or internal enterprise ones) accept key uploads from anonymous users. An attacker can upload this poisoned key. The moment the server tries to index or validate the packet, it crashes.
  • Email Gateways: Secure email gateways that automatically parse incoming PGP-signed emails to verify signatures or decrypt content. An attacker sends an email with this key attached. The gateway process parses the attachment and dies, stopping mail flow.
  • CI/CD Pipelines: Many automated release tools use rPGP to sign artifacts. If a malicious dependency or config file injects this key, the build pipeline collapses.

This is an unauthenticated remote DoS. It requires zero privileges. The attacker just needs to get the bytes into the parser. While it doesn't leak data (Confidentiality) or allow code execution (Integrity), it completely compromises Availability.

The Fix: Remediation

The fix is straightforward but requires action across the dependency tree. The vulnerability is technically in rsa, but pgp (rPGP) is the vector.

For Rust Developers:

  1. Check your lockfile: Run cargo tree -i rsa. If you see rsa v0.9.9 or lower, you are vulnerable.
  2. Update: Run cargo update -p rsa. This should pull in v0.9.10 or later, which contains the fix (if *prime <= BigUint::one()).
  3. Verify: Ensure your Cargo.lock shows the new version.

For System Administrators:

If you are running compiled binaries (like rpgp-cli or custom internal tools), you must recompile them with the updated dependencies. There is no runtime configuration workaround for this, as the panic happens deep inside the compiled logic.

Lesson Learned:

Never assume your cryptographic primitives are doing semantic validation for you. If you are parsing complex structures, wrap them in panic handlers or, better yet, audit the libraries you rely on to ensure they return Result::Err rather than panic! on bad user input.

Official Patches

RustCryptoRSA Crate v0.9.10 Release Notes
rPGPGitHub Security Advisory

Fix Analysis (2)

Technical Appendix

CVSS Score
7.5/ 10
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
EPSS Probability
0.06%
Top 88% most exploited

Affected Systems

Rust applications using `rPGP` crateRust applications using `rsa` crate directly (< 0.9.10)OpenPGP Keyservers based on rPGPSecure email gateways using Rust backends

Affected Versions Detail

Product
Affected Versions
Fixed Version
rsa
RustCrypto
< 0.9.100.9.10
pgp
rPGP
< 0.14.0Dependent on rsa update
AttributeDetail
CVE IDCVE-2026-21895
CWE IDCWE-703 (Unhandled Exception)
CVSS7.5 (High)
Attack VectorNetwork
ImpactDenial of Service (Process Crash)
Root CauseDivision by Zero Panic

MITRE ATT&CK Mapping

T1499Endpoint Denial of Service
Impact
T1499.003Application or System Exploitation
Impact
CWE-703
Improper Check or Handling of Exceptional Conditions

The software does not handle or incorrectly handles an exceptional condition.

Known Exploits & Detection

GitHubOriginal issue report demonstrating the panic with p=1

Vulnerability Timeline

Patch committed to RustCrypto/RSA
2026-01-06
rPGP updates dependency
2026-01-07
GHSA-7587-4WV6-M68M Published
2026-01-08

References & Sources

  • [1]RustSec Advisory
  • [2]rPGP Repository

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•20 minutes ago•CVE-2026-115895
8.8

CVE-2026-115895: Code Injection via Unsanitized Import Alias in Happy DOM ECMAScript Module Compiler

CVE-2026-115895 is a high-severity code injection vulnerability (CWE-94) in the happy-dom npm package prior to version 21.0.4. The vulnerability exists within the ECMAScriptModuleCompiler component, which transforms ECMAScript module syntax into executable Node.js functions when JavaScript evaluation is enabled. Due to an incomplete fix for CVE-2026-33943, the compiler failed to validate local named import aliases against legal JavaScript identifier rules. Attackers supplying crafted module imports—specifically utilizing destructuring default initializers alongside JSON module attributes—can inject arbitrary JavaScript expressions directly into the host Node.js runtime context.

Amit Schendel
Amit Schendel
1 views•10 min read
•about 1 hour ago•CVE-2026-115883
6.0

CVE-2026-115883: Cross-Namespace Access via Dot-Delimited Labels in LangGraph Postgres and SQLite Stores

An improper input validation and authorization bypass vulnerability exists in the LangGraph storage layer affecting langgraph, langgraph-checkpoint-postgres, and langgraph-checkpoint-sqlite. Because relational store implementations serialize namespace tuples into dot-delimited strings without validating labels on read, search, delete, and batch operations, an attacker who controls a namespace label can inject periods (.) to traverse namespace boundaries and access, modify, or delete data belonging to other namespaces.

Amit Schendel
Amit Schendel
3 views•9 min read
•about 2 hours ago•CVE-2026-115886
6.5

CVE-2026-115886: Unauthenticated Server-Side Request Forgery and Unsafe Reflection in MLflow Scorer Evaluation Endpoints

CVE-2026-115886 (GHSA-2wqp-q3c6-ccj5) is a Server-Side Request Forgery (SSRF) and Unsafe Reflection vulnerability in MLflow versions 3.12.0rc0 prior to 3.17.0. Unauthenticated REST API endpoints accept serialized third-party scorer JSON payloads that are dynamically deserialized and reflectively instantiated without sanitizing constructor keyword arguments. When optional dependencies such as trulens and trulens-providers-litellm are installed, attackers can inject egress-steering arguments into the scorer payload, forcing the MLflow server to dispatch outbound HTTP requests to arbitrary internal destinations or cloud metadata services.

Alon Barad
Alon Barad
3 views•10 min read
•about 3 hours ago•CVE-2026-113872
7.5

CVE-2026-113872: Unauthenticated Process Crash in Express Multer via Late Busboy Stream Resumption

CVE-2026-113872 (GHSA-q7qp-rjg7-q26m) is a high-severity denial-of-service vulnerability affecting the multer multipart/form-data upload middleware for Node.js and Express in versions 2.0.0 through 2.4.0. When an endpoint is configured to accept multiple files using an asynchronous fileFilter callback, a crafted, truncated multipart HTTP request can trigger an unhandled error event on the underlying busboy stream parser after request teardown has already completed. Because all error listeners are stripped during teardown, Node.js treats the resulting error event as an uncaught exception and immediately terminates the server process.

Amit Schendel
Amit Schendel
5 views•12 min read
•about 4 hours ago•CVE-2026-115897
2.1

CVE-2026-115897: Cross-Site Scripting via Function Body Boundary Bypass in serialize-javascript

CVE-2026-115897 (also tracked as GHSA-m5mm-p8h7-m29v and aliased as CVE-2026-115754) is a Cross-Site Scripting (XSS) vulnerability in the Yahoo serialize-javascript npm package prior to version 7.1.3. The flaw exists in the regular expression used by escapeFunctionBody() to neutralize HTML script closing tags within serialized JavaScript functions. Because function bodies are sanitized in isolation before structural formatting is applied, a function whose string representation terminates directly with the sequence '</script' bypasses the positive lookahead check. When combined with serialization formatting options that introduce whitespace delimiters and a subsequent property that supplies a closing bracket, an attacker can prematurely terminate an enclosing HTML <script> block and inject arbitrary HTML or JavaScript into the rendered document.

Amit Schendel
Amit Schendel
5 views•13 min read
•about 5 hours ago•CVE-2026-109841
5.9

CVE-2026-109841: Subresource Integrity Verification Bypass in Node.js Undici

CVE-2026-109841 is a Subresource Integrity (SRI) verification bypass vulnerability in undici, the official HTTP/1.1 and HTTP/2 client library for Node.js that powers the global fetch() implementation. The flaw stems from improper handling of case sensitivity (CWE-178) when validating cryptographic hash algorithm tokens within SRI metadata. Because undici compared incoming algorithm identifiers against a strictly lowercase set without prior ASCII-lowercase normalization, valid uppercase or mixed-case tokens such as SHA256- or Sha384- were treated as unrecognized algorithms and discarded. Under SRI forward-compatibility rules, discarding all provided tokens results in an empty integrity list, causing undici to fail open and accept tampered HTTP response bodies without cryptographic verification.

Alon Barad
Alon Barad
3 views•9 min read