Skip to content

Implement Express.js app with known vulnerabilities#46

Open
maekuss wants to merge 1 commit into
mainfrom
maekuss-patch-21
Open

Implement Express.js app with known vulnerabilities#46
maekuss wants to merge 1 commit into
mainfrom
maekuss-patch-21

Conversation

@maekuss

@maekuss maekuss commented Jul 10, 2026

Copy link
Copy Markdown
Owner

Added an Express.js application with multiple vulnerabilities including path traversal, insecure deserialization, and SSRF.

Added an Express.js application with multiple vulnerabilities including path traversal, insecure deserialization, and SSRF.

@hacktron-app hacktron-app Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 1 file

Severity Count
CRITICAL 1
HIGH 1

View full scan results

Comment thread trfsd.html
Comment on lines +22 to +25
app.post('/import', (req, res) => {
const obj = serialize.unserialize(req.body); // RCE via crafted payload
res.json({ imported: Object.keys(obj) });
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRITICAL Remote Code Execution via Insecure Deserialization of Untrusted Input

The application exposes an endpoint /import in testerror/trfsd.html that parses the request body using node-serialize's unserialize method. node-serialize is vulnerable to arbitrary code execution when deserializing untrusted data containing serialized JavaScript functions using the _$$ND_FUNC$$_ prefix. An attacker can send a crafted payload containing an immediately invoked function expression (IIFE) in the request body to execute arbitrary shell commands on the server.

Steps to Reproduce
  1. Start the application using Node.js: node testerror/trfsd.html (assuming dependencies like express and node-serialize are installed).
  2. Send a POST request to http://localhost:4000/import containing a serialized JavaScript function payload with the _$$ND_FUNC$$_ prefix.
  3. Observe that the immediately invoked function expression executes arbitrary commands on the host system.
curl -X POST -H "Content-Type: text/plain" -d '{"rce":"_$$ND_FUNC$$_function (){require(\"child_process\").execSync(\"id\");}()"}' http://localhost:4000/import
Fix with AI

Open in Cursor Open in Claude

A security vulnerability was found by Hacktron.

File: trfsd.html
Lines: 22-25
Severity: critical

Vulnerability: Remote Code Execution via Insecure Deserialization of Untrusted Input

Description:
The application exposes an endpoint `/import` in `testerror/trfsd.html` that parses the request body using `node-serialize`'s `unserialize` method. `node-serialize` is vulnerable to arbitrary code execution when deserializing untrusted data containing serialized JavaScript functions using the `_$$ND_FUNC$$_` prefix. An attacker can send a crafted payload containing an immediately invoked function expression (IIFE) in the request body to execute arbitrary shell commands on the server.

Proof of Concept:
**Steps to Reproduce**

1. Start the application using Node.js: `node testerror/trfsd.html` (assuming dependencies like `express` and `node-serialize` are installed).
2. Send a POST request to `http://localhost:4000/import` containing a serialized JavaScript function payload with the `_$$ND_FUNC$$_` prefix.
3. Observe that the immediately invoked function expression executes arbitrary commands on the host system.

```bash
curl -X POST -H "Content-Type: text/plain" -d '{"rce":"_$$ND_FUNC$$_function (){require(\"child_process\").execSync(\"id\");}()"}' http://localhost:4000/import
```

Affected Code:
app.post('/import', (req, res) => {
  const obj = serialize.unserialize(req.body); // RCE via crafted payload
  res.json({ imported: Object.keys(obj) });
});

Acceptance criteria:
- Acceptance is defined by the **actual reported behavior**, not by tests passing.
- Reproduce the issue, or narrow the exact code path that produces it, *before* changing code. State what you confirmed.
- Fix the underlying cause. Mitigations that paper over the reported behavior do not count as a fix.
- Add a regression test that fails on the unpatched code and passes on the fix. If a regression test is genuinely impractical (e.g. race condition, infra-level issue), say so and explain why.
- Existing tests passing is **not** the bar. Do not declare done on tests-pass theatre.

Only change what is necessary to fix this vulnerability. Do not refactor adjacent code or modify unrelated files.

Triage: Reply !fp <reason> (false positive), !valid (confirmed), !accepted_risk <reason>, or !fixed (resolved). Any other reply is saved as a triage note.
Reason is optional but improves future scans — e.g. !fp internal endpoint, not user-facing.

View finding in Hacktron

Comment thread trfsd.html
// VULN 3: SSRF — server fetches an attacker-supplied URL with no allowlist
app.get('/fetch', (req, res) => {
const target = req.query.url; // ?url=http://169.254.169.254/latest/meta-data/
http.get(target, (upstream) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH Server-Side Request Forgery (SSRF) in /fetch Endpoint

The Node.js Express application in trfsd.html exposes a /fetch endpoint that accepts an attacker-controlled URL via the url query parameter. This URL is passed directly to http.get without any validation, sanitization, or allowlisting. An attacker can exploit this to perform Server-Side Request Forgery (SSRF), allowing them to scan internal ports, access private network services, or query cloud metadata services (e.g., AWS IMDS) and retrieve sensitive information.

Steps to Reproduce
  1. Install the required Node.js dependencies: npm install express node-serialize.
  2. Run the server using Node: node testerror/trfsd.html.
  3. Send a GET request to the /fetch endpoint with a target URL in the url query parameter:
    curl "http://localhost:4000/fetch?url=http://example.com"
  4. Observe that the server fetches the content of http://example.com and returns it in the response.
# Install dependencies and start the vulnerable server
npm install express node-serialize
node testerror/trfsd.html &
sleep 2

# Trigger SSRF to fetch an external or internal resource
curl -i "http://localhost:4000/fetch?url=http://example.com"
Fix with AI

Open in Cursor Open in Claude

A security vulnerability was found by Hacktron.

File: trfsd.html
Lines: 30
Severity: high

Vulnerability: Server-Side Request Forgery (SSRF) in /fetch Endpoint

Description:
The Node.js Express application in `trfsd.html` exposes a `/fetch` endpoint that accepts an attacker-controlled URL via the `url` query parameter. This URL is passed directly to `http.get` without any validation, sanitization, or allowlisting. An attacker can exploit this to perform Server-Side Request Forgery (SSRF), allowing them to scan internal ports, access private network services, or query cloud metadata services (e.g., AWS IMDS) and retrieve sensitive information.

Proof of Concept:
**Steps to Reproduce**

1. Install the required Node.js dependencies: `npm install express node-serialize`.
2. Run the server using Node: `node testerror/trfsd.html`.
3. Send a GET request to the `/fetch` endpoint with a target URL in the `url` query parameter:
   `curl "http://localhost:4000/fetch?url=http://example.com"`
4. Observe that the server fetches the content of `http://example.com` and returns it in the response.

```bash
# Install dependencies and start the vulnerable server
npm install express node-serialize
node testerror/trfsd.html &
sleep 2

# Trigger SSRF to fetch an external or internal resource
curl -i "http://localhost:4000/fetch?url=http://example.com"
```

Affected Code:
app.get('/fetch', (req, res) => {
  const target = req.query.url; // ?url=http://169.254.169.254/latest/meta-data/
  http.get(target, (upstream) => {
    let body = '';
    upstream.on('data', (c) => (body += c));
    upstream.on('end', () => res.send(body));
  });
});

Acceptance criteria:
- Acceptance is defined by the **actual reported behavior**, not by tests passing.
- Reproduce the issue, or narrow the exact code path that produces it, *before* changing code. State what you confirmed.
- Fix the underlying cause. Mitigations that paper over the reported behavior do not count as a fix.
- Add a regression test that fails on the unpatched code and passes on the fix. If a regression test is genuinely impractical (e.g. race condition, infra-level issue), say so and explain why.
- Existing tests passing is **not** the bar. Do not declare done on tests-pass theatre.

Only change what is necessary to fix this vulnerability. Do not refactor adjacent code or modify unrelated files.

Triage: Reply !fp <reason> (false positive), !valid (confirmed), !accepted_risk <reason>, or !fixed (resolved). Any other reply is saved as a triage note.
Reason is optional but improves future scans — e.g. !fp internal endpoint, not user-facing.

View finding in Hacktron

@hacktron-app-stg hacktron-app-stg Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 1 file

Severity Count
CRITICAL 1
HIGH 2

View full scan results

Comment thread trfsd.html
Comment on lines +22 to +25
app.post('/import', (req, res) => {
const obj = serialize.unserialize(req.body); // RCE via crafted payload
res.json({ imported: Object.keys(obj) });
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRITICAL Remote Code Execution via Insecure Deserialization of Untrusted Input

The PR introduces a new endpoint /import that accepts a POST request body and passes it directly to serialize.unserialize() from the node-serialize package. The node-serialize package is vulnerable to arbitrary code execution when deserializing untrusted objects, particularly when they contain Immediately Invoked Function Expressions (IIFEs).

An attacker can exploit this by sending a crafted JSON or text payload containing a serialized object with a function that executes immediately upon deserialization. This allows the attacker to execute arbitrary system commands on the host running the Node.js backend service.

Attack Path

  1. The attacker sends an HTTP POST request to /import with a payload like:
    {"rce":"_$$ND_FUNC$$_function (){ require('child_process').exec('id', function(error, stdout, stderr) { console.log(stdout) }); }() "}
  2. The server receives the request and passes req.body to serialize.unserialize().
  3. The function is executed, leading to Remote Code Execution (RCE).
Steps to Reproduce
  1. Start the Node.js backend service by running node testerror/trfsd.html.
  2. Send an HTTP POST request to /import with the Content-Type: text/plain header and a serialized payload containing an Immediately Invoked Function Expression (IIFE) prefixed with _$$ND_FUNC$$_.
  3. The express.text() middleware parses the body as a string, which is then passed directly to serialize.unserialize().
  4. The IIFE is executed immediately upon deserialization, resulting in arbitrary code execution.
curl -X POST http://localhost:4000/import -H "Content-Type: text/plain" -d '{"rce":"_$$ND_FUNC$$_function (){ require(\"child_process\").exec(\"id\", function(error, stdout, stderr) { console.log(stdout) }); }() "}'
curl -X POST http://localhost:4000/import -H "Content-Type: text/plain" -d '{"rce":"_$$ND_FUNC$$_function (){ require(\"child_process\").exec(\"id\", function(error, stdout, stderr) { console.log(stdout) }); }() "}'
Fix with AI

Open in Cursor Open in Claude

A security vulnerability was found by Hacktron.

File: trfsd.html
Lines: 22-25
Severity: critical

Vulnerability: Remote Code Execution via Insecure Deserialization of Untrusted Input

Description:
The PR introduces a new endpoint `/import` that accepts a POST request body and passes it directly to `serialize.unserialize()` from the `node-serialize` package. The `node-serialize` package is vulnerable to arbitrary code execution when deserializing untrusted objects, particularly when they contain Immediately Invoked Function Expressions (IIFEs). 

An attacker can exploit this by sending a crafted JSON or text payload containing a serialized object with a function that executes immediately upon deserialization. This allows the attacker to execute arbitrary system commands on the host running the Node.js backend service.

### Attack Path
1. The attacker sends an HTTP POST request to `/import` with a payload like:
   `{"rce":"_$$ND_FUNC$$_function (){ require('child_process').exec('id', function(error, stdout, stderr) { console.log(stdout) }); }() "}`
2. The server receives the request and passes `req.body` to `serialize.unserialize()`.
3. The function is executed, leading to Remote Code Execution (RCE).

Proof of Concept:
**Steps to Reproduce**

1. Start the Node.js backend service by running `node testerror/trfsd.html`.
2. Send an HTTP POST request to `/import` with the `Content-Type: text/plain` header and a serialized payload containing an Immediately Invoked Function Expression (IIFE) prefixed with `_$$ND_FUNC$$_`.
3. The `express.text()` middleware parses the body as a string, which is then passed directly to `serialize.unserialize()`.
4. The IIFE is executed immediately upon deserialization, resulting in arbitrary code execution.

```bash
curl -X POST http://localhost:4000/import -H "Content-Type: text/plain" -d '{"rce":"_$$ND_FUNC$$_function (){ require(\"child_process\").exec(\"id\", function(error, stdout, stderr) { console.log(stdout) }); }() "}'
```

```bash
curl -X POST http://localhost:4000/import -H "Content-Type: text/plain" -d '{"rce":"_$$ND_FUNC$$_function (){ require(\"child_process\").exec(\"id\", function(error, stdout, stderr) { console.log(stdout) }); }() "}'
```

Affected Code:
app.post('/import', (req, res) => {
  const obj = serialize.unserialize(req.body); // RCE via crafted payload
  res.json({ imported: Object.keys(obj) });
});

Acceptance criteria:
- Acceptance is defined by the **actual reported behavior**, not by tests passing.
- Reproduce the issue, or narrow the exact code path that produces it, *before* changing code. State what you confirmed.
- Fix the underlying cause. Mitigations that paper over the reported behavior do not count as a fix.
- Add a regression test that fails on the unpatched code and passes on the fix. If a regression test is genuinely impractical (e.g. race condition, infra-level issue), say so and explain why.
- Existing tests passing is **not** the bar. Do not declare done on tests-pass theatre.

Only change what is necessary to fix this vulnerability. Do not refactor adjacent code or modify unrelated files.

Triage: Reply !fp <reason> (false positive), !valid (confirmed), !accepted_risk <reason>, or !fixed (resolved). Any other reply is saved as a triage note.
Reason is optional but improves future scans — e.g. !fp internal endpoint, not user-facing.

View finding in Hacktron

Comment thread trfsd.html
Comment on lines +28 to +35
app.get('/fetch', (req, res) => {
const target = req.query.url; // ?url=http://169.254.169.254/latest/meta-data/
http.get(target, (upstream) => {
let body = '';
upstream.on('data', (c) => (body += c));
upstream.on('end', () => res.send(body));
});
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH Server-Side Request Forgery (SSRF) via Arbitrary URL Fetching

The application exposes an endpoint /fetch that accepts a user-controlled url query parameter. This URL is passed directly to http.get() without any validation, allowlisting, or restriction. An attacker can exploit this Server-Side Request Forgery (SSRF) vulnerability to force the server to make outbound HTTP requests to internal network resources, loopback services, or cloud metadata endpoints (e.g., http://169.254.169.254/), and retrieve the responses.

Steps to Reproduce
  1. Start the Node.js application by running node testerror/trfsd.html.
  2. Send an HTTP GET request to the /fetch endpoint with the url parameter set to the target internal resource (e.g., http://localhost:4000/fetch?url=http://169.254.169.254/latest/meta-data/).
  3. Observe that the server performs the request and returns the response body directly to the client.
# Start the server
node testerror/trfsd.html

# Trigger SSRF to fetch internal/external resources
curl "http://localhost:4000/fetch?url=http://169.254.169.254/latest/meta-data/"
Fix with AI

Open in Cursor Open in Claude

A security vulnerability was found by Hacktron.

File: trfsd.html
Lines: 28-35
Severity: high

Vulnerability: Server-Side Request Forgery (SSRF) via Arbitrary URL Fetching

Description:
The application exposes an endpoint `/fetch` that accepts a user-controlled `url` query parameter. This URL is passed directly to `http.get()` without any validation, allowlisting, or restriction. An attacker can exploit this Server-Side Request Forgery (SSRF) vulnerability to force the server to make outbound HTTP requests to internal network resources, loopback services, or cloud metadata endpoints (e.g., `http://169.254.169.254/`), and retrieve the responses.

Proof of Concept:
**Steps to Reproduce**

1. Start the Node.js application by running `node testerror/trfsd.html`.
2. Send an HTTP GET request to the `/fetch` endpoint with the `url` parameter set to the target internal resource (e.g., `http://localhost:4000/fetch?url=http://169.254.169.254/latest/meta-data/`).
3. Observe that the server performs the request and returns the response body directly to the client.

```bash
# Start the server
node testerror/trfsd.html

# Trigger SSRF to fetch internal/external resources
curl "http://localhost:4000/fetch?url=http://169.254.169.254/latest/meta-data/"
```

Affected Code:
app.get('/fetch', (req, res) => {
  const target = req.query.url; // ?url=http://169.254.169.254/latest/meta-data/
  http.get(target, (upstream) => {
    let body = '';
    upstream.on('data', (c) => (body += c));
    upstream.on('end', () => res.send(body));
  });
});

Acceptance criteria:
- Acceptance is defined by the **actual reported behavior**, not by tests passing.
- Reproduce the issue, or narrow the exact code path that produces it, *before* changing code. State what you confirmed.
- Fix the underlying cause. Mitigations that paper over the reported behavior do not count as a fix.
- Add a regression test that fails on the unpatched code and passes on the fix. If a regression test is genuinely impractical (e.g. race condition, infra-level issue), say so and explain why.
- Existing tests passing is **not** the bar. Do not declare done on tests-pass theatre.

Only change what is necessary to fix this vulnerability. Do not refactor adjacent code or modify unrelated files.

Triage: Reply !fp <reason> (false positive), !valid (confirmed), !accepted_risk <reason>, or !fixed (resolved). Any other reply is saved as a triage note.
Reason is optional but improves future scans — e.g. !fp internal endpoint, not user-facing.

View finding in Hacktron

Comment thread trfsd.html
Comment on lines +12 to +19
app.get('/download', (req, res) => {
const file = req.query.file;
const full = path.join('/var/www/uploads/', file); // ?file=../../etc/passwd
fs.readFile(full, (err, data) => {
if (err) return res.status(404).send('not found');
res.send(data);
});
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HIGH Arbitrary File Read via Path Traversal in File Download Endpoint

The PR introduces a new endpoint /download that allows users to download files from /var/www/uploads/. However, the user-supplied file query parameter is joined directly with the base directory using path.join() without any sanitization or validation.

Because path.join() resolves relative directory segments like ../, an attacker can supply directory traversal sequences (e.g., ../../etc/passwd) to escape the intended directory and read arbitrary files on the server's filesystem that the Node.js process has permissions to read.

Attack Path

  1. The attacker sends an HTTP GET request to /download?file=../../etc/passwd.
  2. The server joins /var/www/uploads/ and ../../etc/passwd, resolving the path to /etc/passwd.
  3. The server reads /etc/passwd via fs.readFile() and returns its contents to the attacker.
Steps to Reproduce
  1. Start the Node.js application (e.g., via node testerror/trfsd.html).
  2. Send an HTTP GET request to the /download endpoint with a path traversal payload in the file query parameter.
  3. Observe that the server returns the contents of the requested file (e.g., /etc/passwd or another system file reachable by the process).
curl "http://localhost:4000/download?file=../../etc/passwd"
Fix with AI

Open in Cursor Open in Claude

A security vulnerability was found by Hacktron.

File: trfsd.html
Lines: 12-19
Severity: high

Vulnerability: Arbitrary File Read via Path Traversal in File Download Endpoint

Description:
The PR introduces a new endpoint `/download` that allows users to download files from `/var/www/uploads/`. However, the user-supplied `file` query parameter is joined directly with the base directory using `path.join()` without any sanitization or validation.

Because `path.join()` resolves relative directory segments like `../`, an attacker can supply directory traversal sequences (e.g., `../../etc/passwd`) to escape the intended directory and read arbitrary files on the server's filesystem that the Node.js process has permissions to read.

### Attack Path
1. The attacker sends an HTTP GET request to `/download?file=../../etc/passwd`.
2. The server joins `/var/www/uploads/` and `../../etc/passwd`, resolving the path to `/etc/passwd`.
3. The server reads `/etc/passwd` via `fs.readFile()` and returns its contents to the attacker.

Proof of Concept:
**Steps to Reproduce**

1. Start the Node.js application (e.g., via `node testerror/trfsd.html`).
2. Send an HTTP GET request to the `/download` endpoint with a path traversal payload in the `file` query parameter.
3. Observe that the server returns the contents of the requested file (e.g., `/etc/passwd` or another system file reachable by the process).

```bash
curl "http://localhost:4000/download?file=../../etc/passwd"
```

Affected Code:
app.get('/download', (req, res) => {
  const file = req.query.file;
  const full = path.join('/var/www/uploads/', file); // ?file=../../etc/passwd
  fs.readFile(full, (err, data) => {
    if (err) return res.status(404).send('not found');
    res.send(data);
  });
});

Acceptance criteria:
- Acceptance is defined by the **actual reported behavior**, not by tests passing.
- Reproduce the issue, or narrow the exact code path that produces it, *before* changing code. State what you confirmed.
- Fix the underlying cause. Mitigations that paper over the reported behavior do not count as a fix.
- Add a regression test that fails on the unpatched code and passes on the fix. If a regression test is genuinely impractical (e.g. race condition, infra-level issue), say so and explain why.
- Existing tests passing is **not** the bar. Do not declare done on tests-pass theatre.

Only change what is necessary to fix this vulnerability. Do not refactor adjacent code or modify unrelated files.

Triage: Reply !fp <reason> (false positive), !valid (confirmed), !accepted_risk <reason>, or !fixed (resolved). Any other reply is saved as a triage note.
Reason is optional but improves future scans — e.g. !fp internal endpoint, not user-facing.

View finding in Hacktron

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant