Rook
AI red-teamer · 42nights

Filesystem error messages reflected to clients disclose absolute server paths

medium5.3

info-leak · server.js:40-41

Introduced in f87c6fc2fa by Macintosh1011 on 2026-05-30

Summary

The /api/file endpoint passes raw Node.js fs error messages directly into the 404 response body. When fs.readFile fails, the underlying ENOENT (or EACCES/EISDIR) error string includes the fully-resolved absolute path that __dirname expands to, exposing internal directory structure. The handler should return a generic error and log the detailed message server-side instead.

Impact. An unauthenticated attacker can probe arbitrary filenames and read the server's reflected error messages to learn the absolute deployment path (e.g. /Users/macintosh/Documents/Claude/Projects/.../public/), the username, project layout, and whether specific files exist or are merely unreadable. This information materially aids further attacks such as path-traversal exploitation, targeted LFI, and reconnaissance for credential or config file locations.

Vulnerable code — server.js

🔴 fs.readFile(path.join(__dirname, "public", name), "utf8", (err, data) => {
🔴 if (err) return send(res, 404, `not found: ${err.message}`);

Working exploit

curl -s 'http://127.0.0.1:4600/api/file?name=definitely_not_a_real_file_ROOKPOC.txt'

Exploit transcript

$ curl -s 'http://127.0.0.1:4600/api/file?name=definitely_not_a_real_file_ROOKPOC.txt'
HTTP 404  Content-Type: text/plain
not found: ENOENT: no such file or directory, open '/Users/macintosh/Documents/Claude/Projects/42nights_internship/rook/fixtures/vulnerable-app/public/definitely_not_a_real_file_ROOKPOC.txt'

Confirmed: not found: ENOENT: no such file or directory, open '/Users/macintosh/Documents/Claude/Projects/42nights_internship/rook/fixtures/vulnerable-app/public/definitely_not_a_real_file_ROOKPOC.txt'

CVSS v3.1

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N5.3 (medium)

Recommended fix

AI-suggested fix — review before applying (derived from analysis of untrusted repo content):

Return a generic message and log details server-side:

--- a/server.js
+++ b/server.js
@@
-    fs.readFile(path.join(__dirname, "public", name), "utf8", (err, data) => {
-      if (err) return send(res, 404, `not found: ${err.message}`);
+    fs.readFile(path.join(__dirname, "public", name), "utf8", (err, data) => {
+      if (err) {
+        console.error("file read failed:", err);
+        return send(res, 404, "not found");
+      }

Ship the fix

Rook found it and proved it. Hand the finding — with its working exploit as a failing test — to Otis to write the fix PR.