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



CVE-2026-118776

CVE-2026-118776: Improper PKCS#7 Padding Validation in node-forge CBC and ECB Cipher Modes

Amit Schendel
Amit Schendel
Senior Security Researcher

Nov 10, 2026·10 min read·0 visits

Executive Summary (TL;DR)

Versions of node-forge prior to 1.4.1 improperly validate PKCS#7 padding in CBC and ECB decryption modes by checking only the last byte and allowing padding lengths up to four blocks. Attackers can supply malformed ciphertexts that pass decryption validation or silently truncate up to four blocks of plaintext.

CVE-2026-118776 (also tracked as GHSA-2vgq-wchc-25w7) is an improper input and integrity check validation vulnerability affecting the node-forge cryptographic library prior to version 1.4.1. In vulnerable versions, the PKCS#7 unpadding implementations for Cipher Block Chaining (CBC) and Electronic Codebook (ECB) modes only examined the final byte of the decrypted buffer and permitted padding counts up to four times the cipher block size. This defect allowed malformed ciphertexts containing zero-length padding, oversized padding values, or mismatched preceding padding bytes to pass validation, resulting in silent plaintext truncation of up to four blocks and integrity verification bypass in applications relying on unpadding success.

Vulnerability Overview

node-forge (published on npm as node-forge and developed under digitalbazaar/forge) is a native JavaScript implementation of the Transport Layer Security (TLS) protocol and various cryptographic utilities. With over 50 million weekly downloads, the package serves as a foundational dependency across Node.js servers and browser-based applications for symmetric and asymmetric encryption, certificate parsing, and message digest generation.

Within node-forge, the lib/cipherModes.js module defines standard block cipher modes of operation, including Electronic Codebook (ECB) and Cipher Block Chaining (CBC). When block ciphers such as AES or DES encrypt messages whose lengths are not exact multiples of the cipher's block size, a padding scheme must be applied to align the plaintext to a block boundary before encryption, and subsequently stripped during decryption.

Prior to version 1.4.1, node-forge contained an improper input validation (CWE-20) and improper validation of integrity check value (CWE-354) flaw in modes.ecb.prototype.unpad and modes.cbc.prototype.unpad. These functions are invoked automatically when decipher.finish() is called at the conclusion of a symmetric decryption operation to verify and remove PKCS#7 padding from the decrypted output buffer.

Instead of enforcing RFC 5652 Section 6.3 compliance across all padding bytes, the vulnerable routines read only the final byte of the output buffer and verified that its integer value did not exceed four times the cipher block size (this.blockSize << 2). Consequently, applications that treat a true return value from decipher.finish() as confirmation of message integrity accept corrupted or forged ciphertexts and silently discard up to four blocks of valid plaintext.

Root Cause Analysis

According to RFC 5652 Section 6.3, PKCS#7 padding for a block cipher with block size $B$ (where $1 < B < 256$, such as 16 bytes for AES or 8 bytes for DES) appends $k$ padding bytes to the plaintext, where $1 \le k \le B$. Every appended byte must contain the exact integer value $k$. For example, if 4 bytes of padding are required, the final four bytes of the decrypted block must be 0x04 0x04 0x04 0x04. During decryption, a compliant implementation must verify that $1 \le k \le B$, confirm that the output buffer contains at least $k$ bytes, and validate that all $k$ trailing bytes equal $k$ before truncating them.

In node-forge versions prior to 1.4.1, the unpadding routines in lib/cipherModes.js violated three distinct invariants of the PKCS#7 specification. First, the implementation retrieved the last byte via var count = output.at(len - 1) and immediately passed count to output.truncate(count) without inspecting any of the preceding count - 1 bytes. Because bytes at indices len - 2 through len - count were ignored, non-PKCS#7 padding formats (such as ANSI X.923 or ISO 10126) and randomly corrupted trailing cipher blocks were accepted as valid.

Second, the upper bound check tested if(count > (this.blockSize << 2)). Bit-shifting the block size left by two multiplies it by four, allowing count values up to 64 bytes for AES (16 << 2 = 64) and 32 bytes for DES (8 << 2 = 32). Because valid PKCS#7 padding never exceeds a single block ($B$), permitting up to $4B$ allowed a single corrupted or attacker-controlled final byte with a value between $B + 1$ and $4B$ to strip up to three additional full blocks of decrypted plaintext from the output buffer.

Third, the routine lacked a lower bound check for zero (count === 0) and a buffer length check (count > len). When the final decrypted byte evaluated to 0x00, the condition 0 > (this.blockSize << 2) evaluated to false, causing output.truncate(0) to execute and unpad to return true. Under RFC 5652, a padding byte of 0x00 is strictly invalid because padding is always applied, even when the plaintext length is already a multiple of the block size.

Code Analysis

The vulnerability affected both modes.ecb.prototype.unpad (lines 82–94) and modes.cbc.prototype.unpad (lines 183–195) in lib/cipherModes.js. Both functions shared identical unpadding logic prior to the patch.

// Vulnerable implementation in lib/cipherModes.js (prior to v1.4.1)
modes.cbc.prototype.unpad = function(output, options) {
  if(options.decrypt && !this.unpad) {
    return false;
  }
 
  // ensure padding byte count is valid
  var len = output.length();
  var count = output.at(len - 1);
  // DEFECT 1: Allows count === 0
  // DEFECT 2: Allows count up to 4x blockSize (e.g., 64 bytes for AES)
  // DEFECT 3: Does not check if count > len
  if(count > (this.blockSize << 2)) {
    return false;
  }
 
  // DEFECT 4: Preceding (count - 1) padding bytes are never inspected
  // trim off padding bytes
  output.truncate(count);
  return true;
};

In commit 81774ff99e2da64aec9a43591df45a360040c136, authored by David I. Lehn based on recommendations from Brian Lee, both modes.ecb.prototype.unpad and modes.cbc.prototype.unpad were updated to enforce strict RFC 5652 Section 6.3 validation.

// Patched implementation in lib/cipherModes.js (v1.4.1)
modes.cbc.prototype.unpad = function(output, options) {
  if(options.decrypt && !this.unpad) {
    return false;
  }
 
  // nothing to unpad, e.g. the caller already read all output
  var len = output.length();
  if(len === 0) {
    return true;
  }
 
  // ensure padding byte count is valid
  var count = output.at(len - 1);
  // FIX: Reject zero count, counts exceeding a single blockSize, or counts exceeding buffer length
  if(count === 0 || count > this.blockSize || count > len) {
    return false;
  }
 
  // FIX: Ensure all padding bytes match the padding count (RFC 5652 6.3)
  for(var i = 2; i <= count; ++i) {
    if(output.at(len - i) !== count) {
      return false;
    }
  }
 
  // trim off padding bytes
  output.truncate(count);
  return true;
};

The patch introduces an explicit early return (if(len === 0) { return true; }) to handle streaming workflows where the caller has already drained the output buffer before invoking finish(). It then constrains count strictly to the range $[1, \min(\text{blockSize}, \text{len})]$ and iterates backward from len - 2 to len - count to verify that every padding byte matches count.

> [!NOTE] > While the for loop in the patch terminates immediately via return false upon encountering the first mismatched byte (which is not constant-time), the unpad check itself executes in memory after block decryption. However, as noted in the advisory, CBC and ECB modes without a Message Authentication Code (MAC) are inherently susceptible to padding oracle attacks if decryption failure states are exposed to remote callers.

Exploitation

Exploiting CVE-2026-118776 requires an target application that decrypts untrusted ciphertexts using node-forge in AES-CBC, AES-ECB, DES-CBC, or DES-ECB mode without verifying a cryptographic MAC (such as HMAC-SHA256) prior to decryption. In such architectures, developers frequently rely on the boolean return value of decipher.finish() to determine whether a ciphertext was encrypted under the valid key and arrived intact.

Under standard PKCS#7 validation, flipping bits in the initialization vector (IV) or the second-to-last ciphertext block ($C_{n-1}$) in CBC mode scrambles the final plaintext block ($P_n = D_K(C_n) \oplus C_{n-1}$). The probability that a randomly corrupted final block ends in valid PKCS#7 padding is approximately $\frac{1}{256} + \frac{1}{256^2} + \dots \approx 0.39%$. In node-forge versions prior to 1.4.1, because any final byte in the range 0x00 to 0x40 (for AES) was accepted regardless of preceding byte values, a randomly corrupted or tampered final block had a $\frac{65}{256} \approx 25.39%$ probability of passing decipher.finish() without error.

Furthermore, an attacker who knows the original padding length of a multi-block CBC ciphertext can deterministically truncate trailing plaintext blocks. In CBC mode, the final byte of the last plaintext block $P_n[15]$ is computed as $D_K(C_n)[15] \oplus C_{n-1}[15]$. If the attacker knows that the message is padded with a full dummy block (P_n[15] = 0x10), they can XOR C_{n-1}[15] with 0x10 ^ 0x30 (0x20). When decrypted by a vulnerable version of node-forge, the final byte of $P_n$ becomes 0x30 (48 bytes). Although the rest of $P_n$ and the decrypted block $P_{n-1}$ are modified or mismatched, unpad() only checks the final byte (0x30 <= 64) and calls output.truncate(48), cleanly stripping the 16-byte padding block plus two complete 16-byte plaintext blocks ($P_{n-1}$ and $P_{n-2}$) without raising an error.

const forge = require('node-forge');
 
const key = forge.util.hexToBytes('db4f3e5e3795cc09a073fa6a81e5a6bc');
const iv  = forge.util.hexToBytes('23468aa734f5f0f19827316ff168e94f');
 
function decryptHex(ciphertextHex) {
  const decipher = forge.cipher.createDecipher('AES-CBC', key);
  decipher.start({ iv: iv });
  decipher.update(forge.util.createBuffer(forge.util.hexToBytes(ciphertextHex)));
  // Returns true on <= 1.4.0 for all malformed vectors below; returns false on >= 1.4.1
  return decipher.finish() ? decipher.output.toHex() : false;
}
 
// Wycheproof test vectors demonstrating malformed padding accepted in <= 1.4.0:
const malformedVectors = {
  // Final decrypted byte is 0x00 (Zero padding byte)
  zeroPadding: 'aa62606a287476777b92d8e4c4e53028',
 
  // Final decrypted byte > 16 (Padding count exceeds 1 AES block, truncates plaintext)
  oversizedPadding: 'd17ccbb26f0aa95f397b20063547349bac24c5429cbea591e96595cccc11451b',
 
  // Preceding padding bytes are 0x00 instead of matching the count byte (ANSI X.923)
  ansiX923Padding: 'ca5dd2d09bd56eec9e8acaeca20af68e',
 
  // Preceding padding bytes do not match the final padding count byte
  mismatchedBytes: '87ff6a2fc6920ce4769cbf6532f84dde389de7c3b693c5e0ceff182842411005bde40966f0eb8b4f598c61158aebc9d3'
};

Impact Assessment

CVE-2026-118776 is scored at 3.7 (Low) under CVSS v3.1 (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N). The High Attack Complexity (AC:H) and Low Integrity impact (I:L) reflect that exploitation is contingent on an application processing unauthenticated CBC or ECB ciphertexts and relying on padding validation for integrity enforcement.

There is no direct loss of confidentiality (C:N) or availability (A:N) resulting solely from the lax unpadding check. However, in protocols or token formats where unauthenticated AES-CBC is used to encapsulate structured payloads (such as JSON objects, session parameters, or command strings), the ability to silently truncate up to 48 bytes of trailing plaintext (three 16-byte blocks plus a 16-byte padding block) or inject corrupted blocks with a 25.4% acceptance rate enables message tampering.

From a Threat Intelligence perspective, the Exploit Prediction Scoring System (EPSS) assigns CVE-2026-118776 a probability score of 0.00136 (0.14%), placing it in the 3.10th percentile of all tracked vulnerabilities. The vulnerability is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog, and no active in-the-wild exploitation campaigns have been recorded.

Remediation

To remediate CVE-2026-118776, upgrade node-forge to version 1.4.1 or later. Version 1.4.1 enforces full RFC 5652 Section 6.3 validation on all padding bytes during CBC and ECB decryption and rejects padding counts that are zero, exceed a single block size, or exceed the available output buffer length.

npm install node-forge@^1.4.1

> [!WARNING] > Backward Compatibility Impact: Applications that previously relied on node-forge's permissive unpadding to decrypt ciphertexts padded with non-PKCS#7 schemes (such as ANSI X.923, ISO 10126, or W3C XML Encryption padding, which populate preceding padding bytes with zeros or random values) will experience decryption failures (decipher.finish() returning false) after upgrading to 1.4.1. To support non-PKCS#7 padding schemes in version 1.4.1+, disable built-in unpadding by setting decipher.mode.unpad = false (or passing custom unpadding logic) and validate the scheme explicitly in application code.

Beyond upgrading the library, development teams should audit their cryptographic implementations to ensure that unauthenticated block cipher modes (AES-CBC, AES-ECB, DES-CBC) are never used as standalone integrity mechanisms. Migrate encryption workflows to Authenticated Encryption with Associated Data (AEAD) modes such as AES-GCM, which is natively supported in node-forge (AES-GCM), or ChaCha20-Poly1305 via Node.js built-in node:crypto APIs. If AES-CBC must be retained for legacy protocol compatibility, implement an Encrypt-then-MAC construction using HMAC-SHA256 and verify the authentication tag in constant time before passing the ciphertext to decipher.update().

Official Patches

Digital Bazaar, Inc.Official GitHub Security Advisory for GHSA-2vgq-wchc-25w7 / CVE-2026-118776
Digital Bazaar, Inc.node-forge v1.4.1 Release Notes
Digital Bazaar, Inc.Git commit fixing PKCS#7 padding validation in lib/cipherModes.js

Fix Analysis (1)

Technical Appendix

CVSS Score
3.7/ 10
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N
EPSS Probability
0.14%
Top 97% most exploited

Affected Systems

node-forge (npm package)digitalbazaar/forge

Affected Versions Detail

Product
Affected Versions
Fixed Version
node-forge
Digital Bazaar, Inc.
< 1.4.11.4.1
AttributeDetail
CVE IDCVE-2026-118776 (GHSA-2vgq-wchc-25w7)
Weakness ClassCWE-354 (Improper Validation of Integrity Check Value) / CWE-20
Affected Componentnode-forge < 1.4.1 (lib/cipherModes.js: modes.cbc.prototype.unpad & modes.ecb.prototype.unpad)
Attack VectorNetwork (AV:N) / High Attack Complexity (AC:H)
CVSS v3.1 Score3.7 Low (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N)
EPSS Score0.14% (3.10th Percentile)
Primary ImpactAcceptance of malformed PKCS#7 ciphertexts and silent truncation of up to 4 plaintext blocks
CISA KEV StatusNot Listed

MITRE ATT&CK Mapping

T1190Exploit Public-Facing Application
Initial Access
CWE-354
Improper Validation of Integrity Check Value

The software does not validate or incorrectly validates the integrity check value (such as checksums, hashes, or padding bytes) for data, allowing modified or forged data to be treated as legitimate.

Known Exploits & Detection

GitHubProject Wycheproof test vectors integrated into tests/unit/aes.js demonstrating malformed PKCS#7 padding ciphertexts accepted prior to 1.4.1.
GitHubPublic repository tracking CVE-2026-118776 advisory and proof-of-concept details.

Vulnerability Timeline

Fix authored and committed to digitalbazaar/forge repository (81774ff99e2da64aec9a43591df45a360040c136).
2026-10-27
CVE-2026-118776 identifier reserved by GitHub CNA.
2026-10-28
GitHub Security Advisory GHSA-2vgq-wchc-25w7, CVE-2026-118776, and node-forge v1.4.1 officially published.
2026-10-29
CVE record metadata updated in the official CVEList v5 repository.
2026-11-04

References & Sources

  • [1]CVE-2026-118776 Official CVE Record
  • [2]GitHub Security Advisory GHSA-2vgq-wchc-25w7
  • [3]node-forge Patch Commit (81774ff99e2da64aec9a43591df45a360040c136)
  • [4]node-forge v1.4.1 Release Tag
  • [5]CVEList v5 JSON Record for CVE-2026-118776
  • [6]National Vulnerability Database (NVD) - CVE-2026-118776
  • [7]zebbernCVE Repository for CVE-2026-118776
  • [8]Project Wycheproof Cryptographic Test Vectors

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

•about 1 hour ago•CVE-2026-118774
3.7

CVE-2026-118774: Improper Verification of Cryptographic Signature in node-forge X.509 Certificate Validation

CVE-2026-118774 (GHSA-9wg5-mj3p-v49q) is an improper cryptographic signature verification vulnerability (CWE-347) in node-forge versions prior to 1.4.1. When verifying an X.509 subordinate certificate via certificate.verify(child) or verifyCertificateChain(), the library erroneously selects the RSA padding scheme (e.g., PKCS#1 v1.5 vs. RSASSA-PSS) and RSASSA-PSS parameters from the issuer certificate rather than from the child certificate being verified. This enables an attacker to relabel a PKCS#1 v1.5-signed certificate as an RSASSA-PSS certificate without invalidating the signature check, bypassing downstream security policies that mandate RSASSA-PSS.

Amit Schendel
Amit Schendel
2 views•12 min read
•about 2 hours ago•CVE-2026-118670
8.8

CVE-2026-118670: DOM-Based Cross-Site Scripting in OHIF Viewers dicom-pdf Extension

CVE-2026-118670 (GHSA-xwgp-w3pj-q5h3) is a high-severity DOM-based Cross-Site Scripting (XSS) vulnerability in OHIF Viewers affecting the dicom-pdf extension module. When processing DICOM Encapsulated Document objects, the viewer extracts the untrusted MIMETypeOfEncapsulatedDocument attribute (tag 0042,0012) and uses it directly to instantiate an in-memory Blob. This Blob is then converted to a same-origin Object URL and embedded into the document via an HTML <object> element. Because browsers prioritize the resource's underlying MIME type over the <object> element's type attribute, an attacker who supplies a crafted DICOM file with a MIME type such as text/html can execute arbitrary JavaScript within the application's origin, compromising session tokens, local storage, and Patient Health Information (PHI).

Alon Barad
Alon Barad
2 views•9 min read
•about 3 hours ago•CVE-2026-103022
5.3

CVE-2026-103022: Denial of Service via Unhandled Unicode Exception and File Descriptor Leak in Urwid Web Display

A Denial of Service (DoS) vulnerability exists in the web display module (urwid/display/web.py) of the Urwid console user interface library for Python in versions prior to 4.0.7. The handle_short_request() function unconditionally encodes HTTP POST input as ASCII without exception handling and opens a FIFO pipe file descriptor without a guaranteed cleanup block. Submitting non-ASCII characters triggers an unhandled UnicodeEncodeError that terminates CGI request processing and leaks the open file descriptor. Additionally, the screen update polling routine decodes Unix domain socket streams iteratively as UTF-8 without exception handling or deterministic socket closure, enabling uncaught UnicodeDecodeError exceptions and socket leaks.

Alon Barad
Alon Barad
3 views•10 min read
•about 4 hours ago•CVE-2026-115770
9.6

CVE-2026-115770: Email Verification Bypass and Downstream Account Takeover in Pocket ID

CVE-2026-115770 is a critical vulnerability (CVSS 9.6) in Pocket ID, an OpenID Connect (OIDC) provider that uses passkey authentication. In versions 2.2.0 through 2.11.0, email verification tokens are bound exclusively to the user account identifier (user_id) rather than the specific email address for which the token was issued. In addition, updating the account email address does not invalidate existing verification tokens. An authenticated user can request a verification token for an attacker-controlled address, modify their profile email to an arbitrary unverified target address, and redeem the previously issued token. This marks the target email address as verified (email_verified = true), enabling account takeover on downstream OIDC relying parties that link or provision accounts based on verified email claims.

Alon Barad
Alon Barad
3 views•11 min read
•about 5 hours ago•CVE-2026-102895
9.8

CVE-2026-102895: Unauthenticated Administrative Access and Remote Code Execution via Loopback Spoofing in GPUStack

CVE-2026-102895 is an improper authentication and origin validation vulnerability in GPUStack versions prior to 2.2.0. By default, the API authentication dependency in gpustack/api/auth.py grants built-in administrator privileges to unauthenticated requests resolved as originating from 127.0.0.1 when the force_auth_localhost flag is false. Because client IP resolution trusts same-host reverse proxy TCP peers and unvalidated X-GPUStack-Real-IP headers, remote unauthenticated attackers can bypass authentication, obtain full administrative control, extract sensitive credentials, and achieve remote code execution via the /v2/inference-backends/from-yaml endpoint.

Alon Barad
Alon Barad
5 views•9 min read
•about 6 hours ago•CVE-2026-87008
7.1

CVE-2026-87008: Out-of-Root Arbitrary File Read and Overwrite via Symlink Following in code-graph-rag

CVE-2026-87008 (GHSA-85gg-2gfq-q95m) is a high-severity symbolic link following and path traversal vulnerability in the `code-graph-rag` Python package prior to version 0.0.589. The flaw resides in the `should_skip_path` path validation routine inside `codebase_rag/utils/path_utils.py`, which performs directory containment checks using unresolved lexical paths rather than canonicalized filesystem paths. Consequently, symbolic links committed to an untrusted repository that point to targets outside the project root bypass boundary enforcement. Attackers who supply a crafted repository containing symbolic links can leverage the `AstGrepService` tool (`structural_search` and `structural_replace`) to read or overwrite arbitrary files on the host filesystem within the privileges of the running process.

Alon Barad
Alon Barad
4 views•9 min read