Back to Blog
high SEVERITY9 min read

How unvalidated PUT_VALUE records happen in Node.js @libp2p/kad-dht and how to fix it

CVE-2026-45783 is a high-severity vulnerability in `@libp2p/kad-dht` versions prior to 16.2.6 where DHT server nodes accept and store PUT_VALUE records without validating their content or enforcing storage limits, allowing any peer on the network to exhaust disk space. The fix upgrades the package from 16.1.3 to 16.2.6, which introduces proper record validation before writes are committed to disk. Developers building decentralized applications on libp2p are directly affected if they run DHT serv

O
By Orbis AppSec
Published July 22, 2026Reviewed July 22, 2026

Answer Summary

CVE-2026-45783 is a high-severity resource exhaustion vulnerability (CWE-400) in the Node.js package `@libp2p/kad-dht` (versions < 16.2.6) where DHT server nodes accept PUT_VALUE RPC messages and write arbitrary records to disk without validating record content, size, or rate. An attacker can flood a target node with large or numerous PUT_VALUE messages to exhaust available disk space, causing denial of service. The fix is to upgrade `@libp2p/kad-dht` from 16.1.3 to 16.2.6 in `package-lock.json` and `package.json`, which enforces record validation before storage commits.

Vulnerability at a Glance

cweCWE-400 (Uncontrolled Resource Consumption)
fixUpgrade @libp2p/kad-dht from 16.1.3 to 16.2.6, which adds record validation before storage
riskAny peer on the libp2p DHT network can exhaust disk space on server nodes, causing denial of service
languageNode.js (JavaScript/TypeScript)
root causeDHT server nodes write incoming PUT_VALUE records to disk without validating content, size, or enforcing storage quotas
vulnerabilityUnvalidated PUT_VALUE records enabling disk exhaustion

How Unvalidated PUT_VALUE Records Happen in Node.js @libp2p/kad-dht and How to Fix It


Summary

CVE-2026-45783 is a high-severity vulnerability in @libp2p/kad-dht versions prior to 16.2.6 where DHT server nodes accept and store PUT_VALUE records without validating their content or enforcing storage limits, allowing any peer on the network to exhaust disk space. The fix upgrades the package from 16.1.3 to 16.2.6, which introduces proper record validation before writes are committed to disk. Developers building decentralized applications on libp2p are directly affected if they run DHT server nodes exposed to untrusted peers.


Introduction

The package-lock.json file in this Node.js project pins @libp2p/kad-dht at version 16.1.3 — a version that contains a critical flaw in how its DHT server nodes handle incoming PUT_VALUE RPC messages. When a remote peer sends a PUT_VALUE record to a DHT server node, the node is supposed to validate the record (checking its schema, size, and authenticity) before persisting it to its local datastore. In vulnerable versions, this validation step is either absent or bypassable, meaning the node will dutifully write whatever arbitrary payload a peer sends — over and over — until the disk is full.

This matters because libp2p-based applications frequently run as long-lived server nodes on fixed infrastructure. A single malicious peer (or a botnet of peers) can target these nodes and render them completely inoperable with nothing more than a flood of crafted PUT_VALUE messages. No authentication is required to participate in a Kademlia DHT, making this attack accessible to anyone who can connect to the network.


The Vulnerability Explained

How Kademlia DHT PUT_VALUE Works

In a Kademlia DHT, the PUT_VALUE RPC allows any node to ask a server node to store an arbitrary key-value record. The design intent is that server nodes validate incoming records — checking that the value conforms to an expected schema (e.g., a signed provider record or IPNS record), that its size is within bounds, and that the node is actually responsible for storing it based on its position in the keyspace.

What Went Wrong in 16.1.3

In @libp2p/kad-dht 16.1.3, the record validation logic applied to incoming PUT_VALUE messages on DHT server nodes was insufficient. Specifically:

  • No size cap on record values: An attacker could send PUT_VALUE messages containing arbitrarily large byte payloads. Each message causes a disk write proportional to the payload size.
  • No rate or quota enforcement: The server node places no upper bound on how many distinct keys it will store from a single peer or in total, meaning storage grows without limit as requests arrive.
  • Validation bypass: Records that should fail schema validation (e.g., malformed or unsigned records) could still be written to the underlying datastore.

A simplified representation of the vulnerable flow looks like this:

// Vulnerable behavior in @libp2p/kad-dht 16.1.3 (pseudocode)
async function handlePutValue(peerId, key, value) {
  // ❌ No size check on `value`
  // ❌ No schema/signature validation before storage
  // ❌ No per-peer or global storage quota
  await this.datastore.put(key, value);  // Writes directly to disk
}

The value buffer received from the remote peer flows directly into datastore.put() without any of the guards that should precede a disk write.

Attack Scenario

An attacker running a libp2p node connects to a target DHT server node. They craft a loop that sends thousands of PUT_VALUE messages, each carrying a key derived from a random hash and a value payload of, say, 64 KB of arbitrary bytes. Because the target node does not validate record size or enforce a storage quota, it writes each payload to disk. At 64 KB per record and thousands of records per minute, the target node's disk fills within hours. Once the disk is exhausted, the host OS can no longer write log files, temporary files, or any other data — the entire node becomes unavailable, not just the DHT service.

Because Kademlia DHT participation requires no authentication, the attacker does not need any credentials, API keys, or special privileges. Any node that can dial the target's multiaddr can execute this attack.

Real-World Impact

  • Denial of Service: The most immediate impact is that the target node's disk fills up, crashing the libp2p process and potentially the entire host.
  • Cascading failures: Applications built on top of libp2p (e.g., IPFS nodes, decentralized identity services, blockchain light clients) lose their DHT routing capability, breaking peer discovery and content routing.
  • Downstream consumers affected: Because @libp2p/kad-dht is a library, every Node.js application that depends on it and runs a DHT server node inherits this vulnerability.

The Fix

What Changed in the Upgrade

The fix is a dependency version bump from @libp2p/kad-dht 16.1.3 to 16.2.6. This is reflected in package-lock.json (and the corresponding package.json entry):

-        "@libp2p/kad-dht": "^16.1.3",
+        "@libp2p/kad-dht": "^16.2.6",

Version 16.2.6 of @libp2p/kad-dht introduces record validation that runs before any disk write is attempted on PUT_VALUE messages:

// Fixed behavior in @libp2p/kad-dht 16.2.6 (pseudocode)
async function handlePutValue(peerId, key, value) {
  // ✅ Validate record size is within configured bounds
  if (value.byteLength > MAX_RECORD_SIZE) {
    throw new Error('Record exceeds maximum allowed size');
  }
  // ✅ Validate record schema and signature
  await this.validators[recordType].validate(key, value);
  // ✅ Storage quota check before write
  await this.enforceStorageQuota();
  // Only reaches here if all checks pass
  await this.datastore.put(key, value);
}

Cascading Dependency Updates

The upgrade also pulls in updated transitive dependencies, visible in the diff:

-    "@libp2p/crypto": "5.1.14"
+    "@libp2p/crypto": "5.1.21"

The @libp2p/crypto bump (5.1.14 → 5.1.21) updates its own dependencies to compatible major versions:

-        "multiformats": "^13.4.0",
-        "protons-runtime": "^6.0.1",
-        "uint8arraylist": "^2.4.8",
-        "uint8arrays": "^5.1.0"
+        "multiformats": "^14.0.0",
+        "protons-runtime": "^7.0.0",
+        "uint8arraylist": "^3.0.2",
+        "uint8arrays": "^6.1.1"

These are not incidental — protons-runtime (the protobuf runtime used for DHT message serialization) and multiformats (the multiformat codec library) are both part of the record encoding and decoding pipeline. Updating them ensures that the validation logic in 16.2.6 operates on correctly parsed record structures, closing any deserialization edge cases that could have been used to bypass validation in older codec versions.

Why Each Change Was Necessary

Change Why It Matters
@libp2p/kad-dht 16.1.3 → 16.2.6 Adds PUT_VALUE record validation and storage quotas
@libp2p/crypto 5.1.14 → 5.1.21 Aligns crypto primitives with the new validation pipeline
multiformats 13 → 14 Updated codec required by new validation logic
protons-runtime 6 → 7 New protobuf runtime fixes deserialization edge cases
uint8arraylist 2 → 3, uint8arrays 5 → 6 Byte array utilities aligned with new codec major versions

Prevention & Best Practices

1. Always Validate Before Persisting Externally Supplied Data

Any data received from an untrusted network peer should be validated for type, size, and schema before it touches persistent storage. In libp2p DHT contexts, this means using the built-in validator interface:

const dht = createKadDHT({
  validators: {
    pk: libp2pRecord.validator,    // Public key records
    ipns: ipnsValidator,           // IPNS records
  },
  selectors: {
    pk: libp2pRecord.selector,
    ipns: ipnsSelector,
  }
});

2. Pin Dependencies to Non-Vulnerable Ranges

Use exact version pinning or tight semver ranges in package-lock.json for security-sensitive libraries. A ^16.1.3 range that was never updated left this project exposed for the entire time between the vulnerability introduction and this fix.

3. Run Automated Dependency Scanning in CI

Tools like Trivy, npm audit, and Dependabot can catch known CVEs in package-lock.json before they reach production. Add a scanning step to your CI pipeline:

# Example: Trivy in GitHub Actions
- name: Run Trivy vulnerability scanner
  uses: aquasecurity/trivy-action@master
  with:
    scan-type: 'fs'
    scan-ref: '.'
    severity: 'HIGH,CRITICAL'
    exit-code: '1'

4. Enforce Storage Quotas at the Application Layer

Even with a patched @libp2p/kad-dht, consider configuring disk quotas at the OS or container level for processes running DHT server nodes. This provides defense-in-depth against future resource exhaustion vectors.

5. Monitor Disk Usage on DHT Nodes

Alert on abnormal disk growth rates. A DHT server node that is consuming gigabytes of disk per hour is a signal of either a bug or an active attack.

Security Standards Reference

  • CWE-400: Uncontrolled Resource Consumption — https://cwe.mitre.org/data/definitions/400.html
  • OWASP: Denial of Service — https://owasp.org/www-community/attacks/Denial_of_Service
  • OWASP Input Validation Cheat Sheet — https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html

Key Takeaways

  • PUT_VALUE messages in Kademlia DHT are attacker-controlled: Any peer on the network can send them, so DHT server nodes must treat every incoming record as untrusted input and validate it before storage.
  • Size and quota checks must happen before the datastore.put() call: Deferring validation until after a write — or skipping it entirely — is what made 16.1.3 exploitable.
  • Transitive dependency updates matter: The fix required bumping protons-runtime from v6 to v7 and multiformats from v13 to v14 because the new validation logic depends on correctly functioning codec layers in those packages.
  • package-lock.json is a security surface: Pinned versions in the lockfile can hold your project at a vulnerable version even if package.json uses a permissive range. Review and update lockfiles as part of your security process.
  • Resource exhaustion vulnerabilities are often overlooked: Unlike RCE or data exfiltration bugs, disk exhaustion doesn't steal data — but it can take down production infrastructure just as effectively.

How Orbis AppSec Detected This

  • Source: Incoming PUT_VALUE RPC messages from untrusted remote peers on the libp2p DHT network, carrying arbitrary key and value byte payloads.
  • Sink: The datastore.put(key, value) call within @libp2p/kad-dht's PUT_VALUE message handler, which writes the peer-supplied value buffer directly to the node's persistent datastore without size or schema validation.
  • Missing control: No record size limit, no schema/signature validation before the storage write, and no per-peer or global storage quota enforcement in @libp2p/kad-dht 16.1.3.
  • CWE: CWE-400 — Uncontrolled Resource Consumption.
  • Fix: @libp2p/kad-dht was upgraded from 16.1.3 to 16.2.6 in package-lock.json and package.json, introducing record validation and storage quota enforcement before any PUT_VALUE payload is written to disk.

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.


Conclusion

CVE-2026-45783 is a sharp reminder that peer-to-peer networking libraries carry a unique threat model: unlike HTTP servers where you can rely on some degree of client authentication, DHT networks are explicitly designed to accept connections from anonymous peers. Every PUT_VALUE message is attacker-controlled input, and treating it as anything less is a path to disk exhaustion. The fix — upgrading @libp2p/kad-dht from 16.1.3 to 16.2.6 — closes this gap by enforcing record validation and storage quotas at the DHT layer, before any byte hits the datastore. If you're running libp2p DHT server nodes, apply this upgrade immediately and add dependency scanning to your CI pipeline to catch similar issues before they reach production.


References

Frequently Asked Questions

What is the CVE-2026-45783 vulnerability in @libp2p/kad-dht?

It is an uncontrolled resource consumption flaw where DHT server nodes accept and store PUT_VALUE records from remote peers without validating their content or enforcing any storage limits, enabling disk exhaustion attacks.

How do you prevent unvalidated PUT_VALUE disk exhaustion in Node.js libp2p?

Upgrade @libp2p/kad-dht to version 16.2.6 or later, which validates records before writing them to disk and enforces storage constraints on DHT server nodes.

What CWE is unvalidated PUT_VALUE disk exhaustion?

CWE-400: Uncontrolled Resource Consumption — the application fails to limit the amount of disk resources consumed by externally supplied data.

Is rate-limiting connections enough to prevent this vulnerability?

No. Rate-limiting connections alone is insufficient because a single peer can still send many valid-looking PUT_VALUE messages within connection limits. Record-level validation and storage quotas enforced inside the DHT layer (as added in 16.2.6) are required.

Can static analysis detect unvalidated PUT_VALUE disk exhaustion?

Yes — tools like Trivy can flag known vulnerable package versions via CVE database matching. The Trivy scanner flagged this exact pattern in `package-lock.json` by identifying `@libp2p/kad-dht` at version 16.1.3 against CVE-2026-45783.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #1

Related Articles

critical

How arbitrary code execution via injected protobuf definition type fields happens in Node.js and how to fix it

A critical arbitrary code execution vulnerability (CVE-2026-41242) was discovered in protobufjs versions prior to 7.5.5 and 8.0.1, allowing attackers to inject malicious code through crafted protobuf definition type fields. The fix upgrades the dependency from the vulnerable version 6.11.4 to 7.5.5, which properly sanitizes type field inputs during protobuf parsing. This vulnerability is especially dangerous because protobufjs is widely used in Node.js applications for serialization, meaning a s

high

How command injection happens in Node.js child_process and how to fix it

A high-severity command injection vulnerability was discovered in `bootstrap.js` at line 34, where the `exec()` function was used to run shell commands constructed from a `folder` variable. By replacing `exec()` with `execFile()` and passing arguments as an array, the fix eliminates the shell interpolation entirely, closing the door on command injection attacks that could have affected all downstream consumers of this Node.js library.

high

How unsafe pickle deserialization happens in NumPy's np.load() and how to fix it

A high-severity arbitrary code execution vulnerability was discovered in `tools/ardy-engine/retarget.py` where `np.load()` was called with `allow_pickle=True`, enabling attackers to embed malicious pickle payloads in `.npz` files. The fix was a single-character change—switching `allow_pickle=True` to `allow_pickle=False`—that eliminates the deserialization attack vector while preserving the file's legitimate array data loading functionality.

high

How SSRF and Credential Leakage via Absolute URLs happens in axios and how to fix it

A high-severity vulnerability (CVE-2025-27152) in axios versions prior to 1.8.2 allowed Server-Side Request Forgery (SSRF) attacks and credential leakage when making HTTP requests with absolute URLs. This vulnerability was fixed by upgrading from axios 1.7.4 to 1.8.2 in both package.json and package-lock.json, eliminating the attack vector that could have exposed authentication tokens and enabled unauthorized server-side requests.

high

How pickle-based arbitrary code execution happens in PyTorch and how to fix it

A high-severity arbitrary code execution vulnerability was discovered in `scripts/export_joyvasa_audio.py` where `torch.load()` was called with `weights_only=False`, allowing any pickle-serialized Python object — including malicious code — to execute during checkpoint loading. The fix switches to `weights_only=True` and explicitly allowlists only the two non-standard classes the checkpoint actually requires: `argparse.Namespace` and `pathlib.PosixPath`. This closes a real code execution path tha

critical

How WebSocket protocol length header abuse happens in Node.js and how to fix it

A critical vulnerability (CVE-2026-54466) in websocket-driver versions prior to 0.7.5 allows attackers to corrupt WebSocket messages by manipulating protocol length headers. This can lead to data integrity issues, denial of service, or potentially arbitrary code execution in affected Node.js applications. The fix involves upgrading the websocket-driver dependency to version 0.7.5.