PHP2Shell: Read our blog on the critical RCE vulnerabilities affecting 70% of the Internet
Vulnerability DatabaseCVE-2026-104849

CVE-2026-104849: 
JavaScript vulnerability analysis and mitigation

Overview

CVE-2026-104849 is a prototype pollution gadget to remote code execution (RCE) vulnerability in Tinypool, a minimal Node.js worker thread pool library. When pool.run(task, options) is called with a caller-supplied options object, Tinypool reads the filename property without verifying it is an own property, allowing a polluted Object.prototype.filename to redirect the worker pool to load an attacker-controlled JavaScript module. All versions of Tinypool prior to 2.1.2 (i.e., <= 2.1.0) are affected; calls to pool.run(task) with no second argument are not affected. The vulnerability was published on August 30, 2026, and carries a CVSS v4.0 base score of 9.5 (Critical) (Github Advisory).

Technical details

The root cause is a combination of CWE-1321 (Prototype Pollution) and CWE-94 (Code Injection): Tinypool reads options.filename via a standard property lookup rather than using Object.hasOwn() or Object.prototype.hasOwnProperty.call(), so a polluted Object.prototype.filename is resolved when the caller-supplied options object lacks its own filename property. An attacker must first achieve prototype pollution through an upstream gadget in the same application (e.g., lodash.merge, qs.parse, or similar functions processing attacker-controlled input), then trigger a pool.run(task, options) call where the options object has no own filename. The fix, implemented in PR #135, wraps all user-supplied option objects with withNullPrototype() — a helper that copies own properties onto a null-prototype object via Object.assign(Object.create(null), source) — before any property is read (Github Advisory, Fix PR, Fix Commit). A complete, runnable proof-of-concept is included in the security advisory (Github Advisory).

Impact

Successful exploitation allows an attacker to redirect the Tinypool worker pool to load and execute arbitrary attacker-controlled JavaScript with the full privileges of the host Node.js process. The malicious worker receives all task data passed to pool.run() — which may include sensitive request bodies, credentials, or business logic data — enabling both remote code execution and data exfiltration or modification. The CVSS v4.0 scoring reflects high confidentiality, integrity, and availability impact on both the vulnerable and subsequent systems, making this a total-compromise scenario for affected applications (Github Advisory).

Exploitability

A complete, runnable JavaScript proof-of-concept demonstrating the full attack chain (prototype pollution → malicious worker execution → task data exfiltration) is publicly available in the GitHub security advisory (Github Advisory). The NVD SSVC assessment classifies exploitation status as "poc" and the vulnerability as automatable. The EPSS score is approximately 0.39% (31st percentile), and there is no current evidence of in-the-wild exploitation or CISA KEV catalog inclusion (Github Advisory). Exploitation requires a chained precondition: a reachable prototype pollution gadget elsewhere in the application, limiting the attack surface compared to standalone RCE vulnerabilities.

Exploitation steps

  1. Identify a prototype pollution gadget: Locate an upstream function in the target application that unsafely merges attacker-controlled input into an object (e.g., lodash.merge(obj, JSON.parse(userInput)), qs.parse(queryString), or similar), reachable via a public HTTP endpoint.
  2. Trigger prototype pollution: Send a crafted HTTP request (e.g., a JSON POST body with {"__proto__": {"filename": "/attacker/path/malicious-worker.mjs"}}) to the vulnerable endpoint, causing Object.prototype.filename to be set to the path of an attacker-controlled module.
  3. Place the malicious worker module: Ensure a malicious ES6 module (e.g., malicious-worker.mjs) is accessible at the polluted path on the server's filesystem. Example content: export default async (task) => ({ by: 'attacker', stolenRequestBody: task }).
  4. Trigger pool.run(task, options): Send an HTTP request to any application endpoint that calls pool.run(req.body, { signal: ac.signal }) or similar — passing a caller-supplied options object without an own filename property.
  5. Tinypool resolves the polluted filename: Because the options object has no own filename, the prototype chain lookup resolves to Object.prototype.filename, which now points to the malicious module. Tinypool loads and executes it in a worker thread.
  6. Receive exfiltrated data: The malicious worker processes the task (e.g., the HTTP request body) and returns attacker-controlled output. The application's response handler (e.g., res.json(result)) sends this back to the attacker, confirming RCE and data exfiltration (Github Advisory).

Indicators of compromise

  • Logs: Unexpected Node.js worker thread loading a module path that differs from the configured filename in the Tinypool constructor; application error logs referencing unfamiliar .mjs or .js file paths in worker initialization.
  • File System: Presence of unexpected JavaScript module files (e.g., malicious-worker.mjs) in application directories or world-writable locations accessible to the Node.js process; new files created by the Node.js process user in non-standard directories.
  • Network: Unusual outbound HTTP or TCP connections originating from the Node.js worker process to attacker-controlled infrastructure; HTTP responses containing unexpected fields such as {"by": "attacker", "stolenRequestBody": ...} in application API responses.
  • Process: Child processes or system calls (e.g., exec, spawn) initiated from within Node.js worker threads that are inconsistent with normal application behavior; unexpected access to sensitive files (e.g., /etc/passwd, environment files, database credentials) by the Node.js process.

Mitigation and workarounds

Upgrade Tinypool to version 2.1.2 or later, which wraps all user-supplied option objects with a null-prototype copy before reading any properties, preventing prototype chain lookups (Release v2.1.2, Fix Commit). As a defense-in-depth measure, audit the application for prototype pollution gadgets (e.g., unsafe use of lodash.merge, qs.parse, JSON.parse into merge operations) and sanitize or freeze Object.prototype at application startup. Applications that call pool.run(task) without a second argument are not affected and do not require immediate action beyond upgrading (Github Advisory).

Community reactions

The vulnerability was noted as the Tinypool counterpart to a previously discovered piscina vulnerability (GHSA-x9g3-xrwr-cwfg), indicating a pattern of prototype pollution exposure in Node.js worker pool libraries forked from piscina (Github Advisory). Multiple downstream projects — including Blockcast/paperclip, ethereum-optimism/actions, percona/pmm-extensions, and Expensify/App — opened dependency update PRs shortly after disclosure, reflecting broad adoption of Tinypool in the Node.js ecosystem (Fix PR). SystemTek published a brief coverage article highlighting the critical severity rating (SystemTek).

Additional resources

Linux Distribution fix status

Fix availability across major Linux distributions and their releases.

RHEL / CentOS

Unknown

Source: This report was generated using AI

Related JavaScript vulnerabilities:

CVE ID

Severity

Score

Technologies

Component name

CISA KEV exploit

Has fix

Published date

GHSA-jr5w-w6x2-84p9HIGH7.4
  • JavaScript logoJavaScript
  • electron
NoYesOct 22, 2026
GHSA-v9q2-hcqj-ggqrMEDIUMN/A
  • JavaScript logoJavaScript
  • seamless-auth-api
NoYesOct 22, 2026
GHSA-v2q2-q246-8chmMEDIUMN/A
  • JavaScript logoJavaScript
  • seamless-auth-api
NoYesOct 22, 2026
GHSA-qx82-28gf-w744HIGHN/A
  • JavaScript logoJavaScript
  • seamless-auth-api
NoYesOct 22, 2026
GHSA-9xg2-j8fg-427cMEDIUMN/A
  • JavaScript logoJavaScript
  • seamless-auth-api
NoYesOct 22, 2026

Free Vulnerability Assessment

Benchmark your Cloud Security Posture

Evaluate your cloud security practices across 9 security domains to benchmark your risk level and identify gaps in your defenses.

Request assessment

Get a personalized demo

Ready to see Wiz in action?

"Best User Experience I have ever seen, provides full visibility to cloud workloads."
David EstlickCISO
"Wiz provides a single pane of glass to see what is going on in our cloud environments."
Adam FletcherChief Security Officer
"We know that if Wiz identifies something as critical, it actually is."
Greg PoniatowskiHead of Threat and Vulnerability Management