File size: 2,867 Bytes
131b46b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
---
license: mit
tags:
  - security
  - proof-of-concept
  - tensorflow-js
  - denial-of-service
---

# TensorFlow.js weights-manifest shape DoS PoC

This repository contains a proof-of-concept malicious `.tfjs`-style weights manifest for a
responsibly-disclosed vulnerability in [`tensorflow/tfjs`](https://github.com/tensorflow/tfjs)
(tested at commit `7f5309fef0a47545e34049903dbdae0f97285f7e`), reported via huntr's Model File
Vulnerability program.

## What these files are

- `malicious_manifest.json` β€” a weights manifest declaring a single weight, `malicious_weight`,
  of `dtype: "string"` and `shape: [500000000]` (500 million).
- `weights.bin` β€” the "backing" weight-data file the manifest points to. It is **completely
  empty (0 bytes)** β€” there is no real weight data behind the declared shape at all.
- `reproduce.js` β€” loads the manifest using `@tensorflow/tfjs`'s public, officially-documented
  `tf.io.weightsLoaderFactory` API (the same pattern shown in that function's own JSDoc example
  for loading weights from disk in Node.js without the native `tfjs-node` addon) β€” no internal or
  private functions are used.

## What happens when you run it

```
npm install @tensorflow/tfjs
node reproduce.js
```

The process becomes unresponsive (including a 2-second heartbeat timer in the script itself, which
does not fire while the vulnerable loop is running β€” the entire Node.js event loop is blocked, not
just the one loading operation) and, after roughly 3.5 minutes, **crashes outright** with a fatal
V8 out-of-memory error (`FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed -
JavaScript heap out of memory`, heap grown past 4 GB) β€” not merely a temporary freeze. `500000000`
was chosen so the demonstration completes in a few minutes; a real attacker could use an even
larger value (e.g. `999999999999`, still just a single JSON integer) for a longer/guaranteed crash
against a victim with more available memory. See the reporter's full write-up submitted via huntr
for the complete timing data and crash log.

This happens because `getWeightBytelength()` (`tfjs-core/src/io/io_utils.ts`) computes the
declared string-tensor size (`sizeFromShape(shape)`, unbounded, no validation) and loops that many
times over the weight's binary data β€” with no check that any real data is actually behind the
declared shape, and no early exit when the (in this case entirely empty) backing buffer is
exhausted.

## Scope note

This PoC is provided solely for the purpose of responsible vulnerability disclosure and
reproduction by the `tfjs` maintainers / huntr triage team. It is not intended for any other use.
Running it will make your own Node.js process unresponsive for the duration of the demo β€” this is
expected and is exactly the behavior being reported; do not run it against a shared or
production process.