CVE-2026-104848
Executive Summary
CVE-2026-104848 is a prototype pollution vulnerability in Tinypool, a minimal Node.js worker thread pool implementation, affecting all versions prior to 2.1.1. The flaw allows values inherited from a polluted Object.prototype to be copied into ThreadPool.options own properties and subsequently passed to worker_threads.Worker, enabling attacker-controlled JavaScript to be loaded by every newly spawned worker via preload arguments or NODE_OPTIONS. Successful exploitation results in code execution at the host process's privilege level, potentially exposing CI secrets, signing material, and build artifacts in developer and build environments.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Technical Analysis
Tinypool constructs its ThreadPool.options object from a normal options object and reads the execArgv and env worker options inside dist/index.js. Because the implementation performs these reads against inherited properties rather than guarding with own-property checks, any value present on a polluted Object.prototype is copied into the resulting options object as an own property. That options object is then handed to worker_threads.Worker, which applies execArgv and env during worker bootstrap.
The security consequence is a prototype-pollution-to-code-execution chain. The execArgv array accepts Node.js command-line flags, including --require (or -r), which preloads and executes a JavaScript module before the worker entry point. Similarly, the env object can carry NODE_OPTIONS, which Node.js also honors at process startup for permitted flags such as --require. In both cases, an attacker who can influence the polluted prototype causes each newly created worker to load attacker-selected JavaScript. The code then executes with the privileges of the host process.
Two preconditions define the attack surface. First, the attacker must be able to pollute Object.prototype for either the execArgv or env property name somewhere in the same process that instantiates a Tinypool ThreadPool. This typically requires a separate, earlier prototype pollution primitive reachable through the application — for example via an unsafe recursive merge, a deep-object assignment routine, or an unvalidated JSON-to-object deserializer. Second, a worker pool must subsequently be created, which is the normal operating condition in applications using Tinypool for CPU-bound parallel work. Tinypool does not itself provide the pollution primitive; it is the sink that converts an existing pollution into execution.
CWE-1321 (Improperly Controlled Modification of Object Prototype Attributes, i.e., prototype pollution) is the correct classification. The vulnerability is an instance of the broader "prototype pollution gadget" pattern: the polluted property name is not merely read for a decision, it is used as a trusted configuration input. The relevant defense is own-property validation (Object.hasOwn / hasOwnProperty) before copying execArgv and env into the pool options, which is the control the 2.1.1 release applies. Pre-fix, the pool implicitly trusts inherited values.
Because the flaw lives in a dependency rather than an exposed network service, it is not remotely triggerable on its own. Exploitability is therefore conditional on the presence of a compatible pollution primitive in the host application's dependency graph or input handling, which raises attack complexity and lowers practical likelihood while leaving impact high when the preconditions are met.
Exploitation Scenarios
No public PoC is available for this CVE. The attack chain below is described at the vulnerability-class level and is inferred from the NVD description; no implementation specifics beyond those stated in that description are asserted.
Reconnaissance and target identification. The attacker identifies a target application that uses Tinypool prior to 2.1.1 (for example, a build tool, test runner, or web framework running under Node.js) and that additionally accepts attacker-influenced structured input. The attacker then searches for a second weakness in the same process capable of writing to
Object.prototype— commonly a deep merge, a configuration parser, or a query-string/JSON body parser that permits keys such as__proto__orconstructor.prototype. This reconnaissance is source- and dependency-oriented; there is no network fingerprint for Tinypool itself.Triggering the flaw class. Using the secondary weakness, the attacker sets an inherited property named
execArgvorenvonObject.prototype. The value is attacker-chosen: forexecArgv, an array containing a Node.js preload flag and a module path under attacker control; forenv, an object whoseNODE_OPTIONSvalue carries an equivalent preload flag and path.Payload and execution method. When the application next causes Tinypool to construct a
ThreadPool, the pool readsexecArgvandenvand, lacking an own-property check, copies the inherited values intoThreadPool.optionsas own properties. Those options are passed toworker_threads.Worker. Each newly spawned worker therefore starts with the attacker's preload argument applied, loading and executing attacker-selected JavaScript through the command-line preload mechanism or throughNODE_OPTIONS. Execution occurs with the privileges of the host process.Post-exploitation impact. Because workers run inside the host's trust context, the attacker gains code execution in the build or CI environment. In CI/CD contexts this can mean reading environment-injected secrets, signing keys and certificates, and build artifacts, as well as tampering with build output or persisting within the pipeline. Note that exploitation is possible only where a prior prototype pollution primitive exists; the attacker must chain two weaknesses, which Caps the likelihood but does not reduce the ceiling of impact.
Impact Assessment
There is no CVSS base score or vector published for CVE-2026-104848 (CVSS: N/A), so the vector listed in metadata is an analyst-derived estimate and should not be treated as vendor- or NVD-supplied. Under that estimate, confidentiality, integrity, and availability are all High, scope is Changed (the worker context executes attacker code distinct from the intended workload), and complexity is High because a separate pollution primitive is required. Privileges required are Low (the attacker need only influence structured input), user interaction is None, and the attack vector is Local in the sense that the attacker must reach the process's input surface rather than a remote service.
Business impact concentrates where Node.js build and test infrastructure is used: CI/CD runners, container image build pipelines, release automation, and developer workstations. Compromise of these systems can expose credentials embedded in the environment, code-signing material, publish tokens, and proprietary source or artifacts. Blast radius is bounded by the reach of the polluted process and the privileges under which it runs; a CI runner is a particularly attractive target because its credentials often permit publishing packages or artifacts, enabling downstream supply chain impact. EPSS has not been scored by FIRST.org, and the CVE is not in CISA KEV, so there is no exploit-in-the-wild evidence at present. The absence of a public PoC and the chained preconditions keep near-term likelihood moderate.
Detection Guidance
Detection should focus on the code-execution result — anomalous Node.js preload flags, unexpected NODE_OPTIONS content, and worker spawning with attacker-supplied modules — plus prototype pollution primitives in application input. The rules below are illustrative and require tuning to the local environment.
Sigma Rule
title: Tinypool Prototype Pollution to Worker Preload Execution (CVE-2026-104848)
id: 3f9d5a1c-7c4e-4b8e-9c2a-tinypool104848
status: experimental
description: Detects Node.js processes started with preload flags or NODE_OPTIONS referencing unexpected modules, consistent with Tinypool worker option injection.
references:
- https://nvd.nist.gov/vuln/detail/CVE-2026-104848
author: valtersit.com
date: 2026/01/01
logsource:
category: process_creation
product: linux
detection:
selection_node:
Image|endswith: '/node'
selection_flags:
CommandLine|contains:
- '--require'
- '-r '
- 'NODE_OPTIONS='
selection_suspicious_path:
CommandLine|contains:
- '/tmp/'
- '/dev/shm/'
- 'node_modules/.cache'
- 'http://'
- 'https://'
condition: selection_node and selection_flags and selection_suspicious_path
falsepositives:
- Legitimate instrumentation, coverage, or APM preload modules
level: high
tags:
- attack.t1195
- cve.2026.104848
YARA Rule
rule CVE_2026_104848_Tinypool_ProtoPollution
{
meta:
description = "Detects Tinypool pre-2.1.1 patterns where inherited execArgv/env are copied into worker options"
author = "valtersit.com"
reference = "https://nvd.nist.gov/vuln/detail/CVE-2026-104848"
date = "2026-01-01"
severity = "high"
strings:
$a = "new Worker(" ascii wide
$b = "execArgv" ascii wide
$c = "ThreadPool.options" ascii wide
$d = "worker_threads" ascii wide
$e = "NODE_OPTIONS" ascii wide
condition:
(uint32(0) == 0x6A736F6E or $d) and
$b and $a and ($c or $e)
}
Suricata IDS Rule
alert tcp $HOME_NET any -> $EXTERNAL_NET any (msg:"CVE-2026-104848 Tinypool prototype pollution preload fetch attempt"; flow:to_server,established; content:"NODE_OPTIONS"; nocase; http_header; content:"--require"; nocase; distance:0; classtype:attempted-user; reference:cve,2026-104848; reference:url,nvd.nist.gov/vuln/detail/CVE-2026-104848; sid:2026104848; rev:1;)
Elastic Detection
process where event.type == "start" and
process.name in ("node", "node.exe") and
(process.command_line : "*--require*" or process.args : "--require*") and
(process.command_line : "*/tmp/*" or process.command_line : "*/dev/shm/*" or
process.command_line : "*http*") and
process.parent.name in ("node", "npm", "yarn", "pnpm", "npx")
Splunk SPL Query
index=edr OR index=sysmon EventCode=1 Image=*node*
| eval cl=lower(CommandLine)
| where (match(cl,"--require|-r ") AND match(cl,"/tmp/|/dev/shm/|http"))
| stats count min(_time) as first_seen max(_time) as last_seen by host, User, ParentImage, CommandLine
| where count >= 1
| convert ctime(first_seen) ctime(last_seen)
Wazuh OSSEC Rule
<group name="cve_2026_104848,tinypool,">
<rule id="104848" level="12">
<if_sid>100000</if_sid>
<field name="win.eventdata.image">node\.exe|node$</field>
<field name="win.eventdata.commandLine">--require|NODE_OPTIONS</field>
<field name="win.eventdata.commandLine">/tmp/|/dev/shm/|http</field>
<description>CVE-2026-104848: Node worker spawned with suspicious preload/NODE_OPTIONS - possible Tinypool prototype pollution exploitation</description>
<mitre>
<id>T1195</id>
</mitre>
<group>attack,supply_chain,</group>
</rule>
</group>
Mitigation & Patch Guidance
Patch confidence is 0/4 — UNPATCHED, with no patch evidence from any tracked source. The NVD description states the issue is fixed in version 2.1.1, but no vendor advisory, commit reference, or package registry entry has been confirmed by the tracked sources, so patch status must be independently verified before relying on it.
Immediate mitigations. Upgrade Tinypool to 2.1.1 or later and pin it via lockfile to prevent dependency confusion or downgrade. Where immediate upgrade is not possible, defensively freeze prototypes at process start (
Object.freeze(Object.prototype)and the same forArray.prototype) to blunt prototype pollution, and enforce own-property checks in any application code that builds option objects. Eliminate reachable prototype pollution primitives by validating or rejecting__proto__andconstructor.prototypekeys in all parsers, deep merges, and configuration loaders. Consider--frozen-intrinsicswhere supported. Restrict or sanitizeNODE_OPTIONSvia environment policy and run CI workers with least privilege and no persistent credentials.Patch/upgrade steps. Identify all transitive and direct dependents on Tinypool (for example through
npm ls tinypoolorpnpm why tinypool) and audit lockfiles for versions below 2.1.1. Upgrade to the fixed release, regenerate the lockfile, and re-run the dependency tree check to confirm no residual vulnerable copies remain. Because a fix is only asserted by the NVD description here, verify the version resolves correctly from the registry and inspect the resolveddist/index.jsfor own-property validation ofexecArgvandenvbefore considering the issue closed.Long-term hardening. Adopt automated SCA and dependency review in CI to catch prototype pollution gadgets and unpinned transitive dependencies. Apply least privilege to CI runners, use ephemeral, short-lived credentials, and avoid exposing signing keys or publish tokens to build jobs that execute third-party code. Add runtime prototype hardening and input schema validation as default controls. Monitor npm advisories and the Node.js security releases for follow-up guidance.
References
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-104848
- CWE-1321: https://cwe.mitre.org/data/definitions/1321.html
- MITRE ATT&CK T1195 Supply Chain Compromise: https://attack.mitre.org/techniques/T1195/
- Node.js worker_threads documentation: https://nodejs.org/api/worker_threads.html
- Node.js NODE_OPTIONS documentation: https://nodejs.org/api/cli.html#node_optionsoptions
No public PoC, exploit code, Nuclei template, or exploit-in-the-wild evidence is currently available for this CVE. It is not listed in CISA KEV and has not been scored by FIRST.org (EPSS). No known threat actor campaigns are associated with CVE-2026-104848 at this time.
What is this? Security researchers use these exact search queries on Shodan.io (a search engine for internet-connected devices) to find vulnerable systems globally. You can copy these dorks to see if your organization's IP space is exposed.