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-6VJ9-MWQ6-2F5V

GHSA-6VJ9-MWQ6-2F5V: Cross-Tenant SMTP Credential Disclosure via Shared-State DNS Cache Pollution in Nodemailer

Alon Barad
Alon Barad
Software Engineer

Sep 28, 2026·7 min read·19 visits

Executive Summary (TL;DR)

A shared-state vulnerability in Nodemailer's global DNS cache allows attackers to poison connection-specific TLS metadata. This redirects victim SMTPS sessions to an attacker-controlled virtual host, disclosing plaintext SMTP credentials.

Nodemailer versions 5.0.0 up to 10.0.1 are vulnerable to process-global state contamination inside the DNS caching subsystem. When SMTPS connections are established in a multi-tenant Node.js process targeting a shared gateway, a lower-privilege attacker can seed the global DNS cache with a malicious TLS servername. When a victim subsequently resolves the same gateway host, Nodemailer retrieves the polluted servername, overwrites the victim's connection settings, redirects the TLS session to the attacker's virtual host, and transmits the victim's cleartext SMTP credentials directly to the attacker.

Vulnerability Overview

Nodemailer is a widely adopted Node.js module designed for email transmission. To optimize performance and reduce latency during SMTP handshakes, the library implements an internal, process-global DNS cache (dnsCache). This cache mitigates the overhead associated with repeated resolution of target SMTP hosts.

In long-running Node.js processes, such as server-side SaaS platforms or centralized microservices, multiple isolated mail transports often run within a single execution space. If these transports route emails through a shared SMTP relay, they query the same hostname but configure distinct TLS options tailored to their respective domains. This configuration introduces a shared-state attack surface.

The vulnerability designated as GHSA-6VJ9-MWQ6-2F5V arises from an architectural failure to separate concerns within this global DNS cache. Specifically, connection-specific TLS configuration options are stored alongside network-specific DNS records. When multiple independent tenants utilize the shared cache, this design flaw allows a low-privilege attacker to contaminate the cache, leading to cross-tenant connection hijacking and credential disclosure.

Root Cause Analysis

The root cause of GHSA-6VJ9-MWQ6-2F5V is the storage of connection-specific TLS metadata within a process-global cache keyed only by the target hostname. Nodemailer maintains this cache inside src/shared/index.ts using a standard JavaScript Map object named dnsCache. This cache maps host string keys directly to DnsCacheValue structures.

The structural vulnerability lies in the definition of the DnsCacheValue interface, which encapsulates the TLS servername option. When a transport resolves a hostname using resolveHostname(), Nodemailer retrieves the dynamic TLS options provided by that specific connection attempt. If a cache miss occurs, the library populates the global dnsCache with the resolved IP addresses and binds the connection-specific servername to that entry.

const value: DnsCacheValue = {
    addresses: allAddresses,
    servername: options.servername || host
};

Upon subsequent connection attempts to the same host within the five-minute Time-To-Live (TTL), Nodemailer triggers a cache hit. The formatDNSValue() utility extracts the cached value, copying the stale, tenant-specific servername directly into the returned lookup result.

const formatDNSValue = (value: DnsCacheValue | undefined, extra?: Partial<ResolvedHostname>): ResolvedHostname | undefined => {
    return Object.assign(
        {
            servername: value.servername,
            host,
            _addresses: addresses
        },
        extra || {}
    );
}

This behavior corrupts the target parameters for any subsequent connection using the same host. During connection initialization inside src/smtp-connection/index.ts, Nodemailer's _resolveAndConnect() loop iterates over the keys of the returned DNS resolution object and blindly overwrites the existing connection options.

for (const key of Object.keys(resolved!)) {
    if (key.charAt(0) !== '_' && (resolved as { [key: string]: any })[key]) {
        (opts as { [key: string]: any })[key] = (resolved as { [key: string]: any })[key];
    }
}

This routine replaces the correct, transport-specific opts.servername configuration of the second tenant with the stale, poisoned servername from the cache.

Code Analysis & Patch Diff

To remediate this architectural vulnerability, the developers decoupled connection-level metadata from the shared network cache. The security patch was applied in commit a6512dbcb3c6e7f2f70d3acccc5752defe3c61fe.

Below is the code modification in src/shared/index.ts illustrating the removal of the state-sharing property.

// Vulnerable structure definition
export interface DnsCacheValue {
    addresses: string[];
-   servername: string | false;
}
 
// Patched formatting helper
const formatDNSValue = (value: DnsCacheValue | undefined, extra?: Partial<ResolvedHostname>): ResolvedHostname | undefined => {
    return Object.assign(
        {
-           servername: value.servername,
            host,
            _addresses: addresses
        },
        extra || {}
    );
}

The patched implementation dynamically computes the TLS servername at the invocation of resolveHostname() instead of serializing it into the global cache. This guarantees that each calling transport maintains its independent server name metadata.

const host = options.host;
+ // The TLS server name belongs to the connection asking, not to the host it resolves.
+ // The cache is shared by every transport of the process and keyed by host alone.
+ const servername = options.servername || host;

When returning cached values, the dynamic, connection-specific servername is passed back on-the-fly to prevent any cross-tenant leakage.

return callback(
    null,
    formatDNSValue(cached.value, {
+       servername,
        cached: true
    })
);

This fix is architecturally complete. By purging the servername attribute entirely from the stored cache schema, the library eliminates the attack vector. There is no residual shared-state channel remaining in the DNS resolution path that can leak connection-specific TLS configuration details.

Exploitation Methodology

Exploitation of GHSA-6VJ9-MWQ6-2F5V requires a multi-tenant Node.js process where separate users configure distinct SMTPS transports targeting a shared gateway. The attack progresses in five logical phases: cache priming, victim lookup, connection routing, certificate validation, and credential extraction.

First, the attacker creates an SMTPS transport pointing to the target relay (e.g., smtp.sharedrelay.test) and sets the tls.servername property to an attacker-controlled domain (e.g., attacker.test). The attacker triggers a connection, forcing Nodemailer to resolve the host and write { addresses, servername: 'attacker.test' } into the global dnsCache.

Second, a victim tenant initiates a connection to the same relay using their legitimate credentials and expected server name (victim.test). Due to the global scope of dnsCache, Nodemailer retrieves the entry primed by the attacker and overwrites the victim's connection options, setting opts.servername to attacker.test.

Third, the victim's transport invokes the native tls.connect(opts) function. This transmits a TLS Client Hello containing the Server Name Indication (SNI) extension configured for attacker.test. The shared gateway parses this SNI and routes the TLS connection directly to the attacker's virtual server.

Fourth, the attacker's server completes the TLS handshake by presenting a valid certificate for attacker.test. Because the victim's validation options were overwritten to expect attacker.test, Node's native validation subsystem successfully validates the certificate chain against the trusted system root authorities.

Fifth, once the secure channel is established, the victim's Nodemailer client initiates the SMTP protocol flow. The client sends an EHLO command, reads the AUTH PLAIN capability from the attacker's server, and transmits its plain SMTP authentication credentials directly across the hijacked session.

Impact Assessment

The security impact of GHSA-6VJ9-MWQ6-2F5V is classified as high confidentiality loss. An attacker who successfully exploits this state pollution can acquire cleartext authentication credentials for other tenants operating within the same Node.js execution environment.

The Common Vulnerability Scoring System (CVSS) v3.1 vector is CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:L/A:N, yielding a score of 5.9 (Medium). Although the impact on confidentiality is high, the overall rating is moderated by the high complexity of the attack. Exploitation depends on precise timing within the DNS TTL window, shared infrastructure routing via SNI, and co-location of transports in a single process.

Despite the Medium rating, this vulnerability represents a severe threat to SaaS applications, shared email routing infrastructure, and multi-tenant microservices. If credentials for an enterprise mail relay are compromised, the attacker can leverage them to bypass spam filters, send malicious mail from legitimate domains, and access sensitive corporate communications.

Detection & Mitigation Guidance

Security teams should immediately audit internal Node.js applications to identify instances of the nodemailer package. Applications with dependency versions between 5.0.0 and 10.0.1 must be considered vulnerable if they establish concurrent SMTPS connections to shared hosts.

The primary remediation strategy is upgrading the project's dependency structure to nodemailer version 10.0.2 or higher. This release resolves the underlying caching logic and ensures complete separation of connection-level metadata.

If immediate upgrading is not feasible, organizations can implement several mitigation workarounds. First, developers can bypass the global DNS resolver by using the direct IP address of the SMTP gateway (e.g., 192.0.2.1) in the host parameter, preventing Nodemailer from querying or writing to the global cache. Second, deploying separate Node.js processes for each tenant isolates the memory space and prevents cross-tenant state pollution.

Additionally, monitoring network logs for anomalies in SNI headers—specifically instances where the target IP matches a legitimate SMTP server but the SNI header contains an unrecognized or third-party domain—can help detect ongoing exploitation.

Official Patches

NodemailerOfficial patch commit removing servername from DNS cache
NodemailerRelease v10.0.2 notes

Fix Analysis (1)

Technical Appendix

CVSS Score
5.9/ 10
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:L/A:N

Affected Systems

Node.js applications running multi-tenant Nodemailer instances.SaaS platforms employing shared SMTP relay gateways.

Affected Versions Detail

Product
Affected Versions
Fixed Version
nodemailer
Nodemailer
>= 5.0.0, < 10.0.210.0.2
AttributeDetail
CWE IDCWE-295
Attack VectorNetwork
CVSS5.9 (Medium)
ImpactHigh (Cleartext Credential Disclosure)
Exploit StatusPoC Available
KEV StatusNot Listed

MITRE ATT&CK Mapping

T1557Adversary-in-the-Middle
Credential Access
T1556Modify Authentication Process
Credential Access
CWE-295
Improper Certificate Validation

The product does not validate or incorrectly validates a certificate, which can allow an attacker to spoof a trusted entity by interfering with the connection path.

Vulnerability Timeline

Vulnerable caching logic introduced in version 5.0.0
2018-12-28
Vulnerability remediated by developer
2026-09-09
Patched version 10.0.2 released
2026-09-09
GitHub Security Advisory GHSA-6VJ9-MWQ6-2F5V published
2026-09-28

References & Sources

  • [1]GitHub Security Advisory GHSA-6VJ9-MWQ6-2F5V

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

•36 minutes ago•CVE-2026-126072
5.9

CVE-2026-126072: Thread Deadlock and Denial of Service in Ruby on Rails ActionController::Live

CVE-2026-126072 (GHSA-rwvh-fw6r-pwvp) is a concurrency deadlock vulnerability in the Ruby on Rails Action Pack framework affecting applications that utilize ActionController::Live response streaming. The defect occurs due to improper synchronization within ActionController::Live::Buffer when handling full bounded queues during stream closure and connection abortion. An unauthenticated remote attacker can trigger this condition by stalling TCP socket reads on streaming endpoints—including Active Storage proxy mode—resulting in permanent worker thread exhaustion and application-wide denial of service.

Amit Schendel
Amit Schendel
2 views•10 min read
•about 2 hours ago•CVE-2026-126192
6.3

CVE-2026-126192: Quadratic Algorithmic Complexity Denial of Service in Soup Sieve CSS Selector Engine

CVE-2026-126192 is an algorithmic complexity vulnerability in Soup Sieve (soupsieve), the default CSS selector engine for Beautiful Soup 4 in Python. Evaluating a CSS selector that contains a general sibling combinator (~) nested inside a :has() relational pseudo-class against attacker-controlled HTML markup triggers O(n^2) quadratic evaluation complexity when the right-hand sibling target is absent. An unauthenticated attacker can supply small HTML documents containing thousands of sibling elements to exhaust CPU resources and stall application worker threads.

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

CVE-2026-126190: Polynomial-Time Denial of Service in Soup Sieve CSS Descendant Combinator

CVE-2026-126190 (GHSA-mmhr-3c5m-f64f) is an inefficient algorithmic complexity vulnerability (CWE-407) in Soup Sieve, the default CSS selector library integrated into Beautiful Soup 4. In versions prior to 4.0, evaluating CSS selectors that contain descendant combinators (whitespace between compound selectors) executes an unmemoized upward traversal of the document tree. When evaluated against deeply nested HTML or XML documents, the time complexity scales polynomially at O(n^(k+1)), where n is the tree depth and k is the number of descendant combinators. Remote attackers can supply compact, deeply nested markup (1 KB to 4 KB) to trigger prolonged CPU starvation and denial of service in Python applications.

Amit Schendel
Amit Schendel
3 views•9 min read
•about 4 hours ago•CVE-2026-122006
9.8

CVE-2026-122006: Unauthenticated Arbitrary File Read and Write in Ruby on Rails Active Storage via ImageMagick

CVE-2026-122006 is a critical vulnerability in the Ruby on Rails Active Storage framework affecting versions prior to 8.0.5.2 and 8.1.4.1. When an application processes uploaded file attachments using the ImageMagick variant processor (:mini_magick), the framework invokes ImageMagick's metadata analyzer without applying a restrictive security policy. An unauthenticated attacker can upload a crafted file containing directives for unrestricted ImageMagick coders, such as Magick Scripting Language (MSL), resulting in arbitrary server-side file read and write operations. In deployments using the Disk storage service, attackers can exfiltrate sensitive application configuration files, including secret_key_base and master encryption keys, which enables remote code execution via signed payload forgery.

Alon Barad
Alon Barad
3 views•10 min read
•about 5 hours ago•CVE-2026-128065
6.9

CVE-2026-128065: Authorization Bypass via Unapplied RBAC Overrides in @grpc/grpc-js-xds

CVE-2026-128065 (also tracked as GHSA-gg5j-6742-v827) is an authorization bypass vulnerability affecting the @grpc/grpc-js-xds library in the Node.js gRPC ecosystem. In versions 1.14.0 and 1.14.1, XdsServer acknowledges Route and VirtualHost resources from an xDS control plane while failing to parse, store, or apply RBACPerRoute override configurations. When experimental RBAC is enabled and a broad top-level ALLOW policy is paired with restrictive VirtualHost or Route overrides, the server evaluates requests strictly against the top-level policy, permitting unauthorized RPC execution.

Alon Barad
Alon Barad
4 views•10 min read
•about 6 hours ago•CVE-2026-126188
3.7

CVE-2026-126188: Local Media Information Disclosure in Ruby on Rails Active Storage via FFmpeg Demuxer Selection

CVE-2026-126188 (GHSA-3f4q-8x5m-76v8) is an information disclosure vulnerability in the Active Storage component of Ruby on Rails prior to versions 8.0.5.2 and 8.1.4.1. When generating previews or extracting metadata from uploaded media files, Active Storage invokes the external command-line tools ffmpeg and ffprobe without specifying a format whitelist. An attacker who can upload a crafted HTTP Live Streaming (HLS) playlist while declaring a standard video or audio MIME type can cause FFmpeg to engage the HLS demuxer, resolve file:// segment URIs, and disclose visual frames or metadata from local media files readable by the Rails application process.

Amit Schendel
Amit Schendel
3 views•10 min read