Summary
FastGPT's plugin "invoke" reverse-call endpoints (/api/invoke/*) authenticate only by verifying a JWT signed with INVOKE_TOKEN_SECRET, which defaults to the constant string 'token' (packages/service/env.ts:300) and is not set in the official deployment templates. An unauthenticated attacker can self-sign a valid HS256 JWT with secret 'token' and an arbitrary payload, fully bypassing authentication. Two handlers are reachable this way:
POST /api/invoke/userInfo → handleGetUserInfo() resolves the user purely from the attacker-supplied tmbId (a 24-hex Mongo ObjectId, enumerable) with no cross-check against teamId, returning any user's username, registration email/phone (PII), member name, org and group names — unauthenticated cross-tenant PII disclosure.
POST /api/invoke/fileUpload → handleFileUpload() writes attacker-controlled content into uploadChatFile({appId, chatId, uId}) for any chat — cross-tenant chat-file planting.
Details
packages/service/env.ts:300: INVOKE_TOKEN_SECRET: z.string().default('token')
packages/service/support/invoke/invoke.ts:24: static jwtSecret = serviceEnv.INVOKE_TOKEN_SECRET; :47: jwt.verify(token, this.jwtSecret) is the only authentication for these routes.
projects/app/src/pages/api/invoke/userInfo.ts:18: InvokeProcessor.getInstanceFromToken(token).handleGetUserInfo(), wrapped in NextAPI(handler) (NextEntry({ beforeCallback: [] }), no session/global auth); there is no Next.js middleware gating /api/invoke/*.
deploy/version/main/docker-compose.template.yml: sets TOKEN_KEY / FILE_TOKEN_KEY / AES256_SECRET_KEY but omits INVOKE_TOKEN_SECRET (0 occurrences), so default/official Docker deployments run with the constant 'token'.
getInstanceFromToken only does jwt.verify + a zod schema parse; handleGetUserInfo calls getUserDetail({ tmbId }) and returns user.contact (email/phone) from the attacker-supplied tmbId (it only requires that the supplied teamId is an existing team, not that it owns the user).
Proof of Concept
TOKEN=$(python3 - <<'PY'
import jwt
print(jwt.encode({"appId":"000000000000000000000000","chatId":"x","uId":"x",
"tmbId":"<victim_tmbId_objectid>","teamId":"<any_valid_team_objectid>","permissions":["userInfo:read"]}, "token", algorithm="HS256"))
PY
)
curl -s https://TARGET/api/invoke/userInfo -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' -d '{}'
# -> returns the victim user's username, registration email/phone, org/group, with no authentication
Impact
Unauthenticated cross-tenant disclosure of any user's PII (email, phone, names, org/group membership) and unauthenticated cross-tenant chat-file write, on any FastGPT deployment following the official Docker templates (which do not set INVOKE_TOKEN_SECRET). CWE-798 (use of hard-coded / default credential) + CWE-639 (authorization bypass through user-controlled tmbId). CVSS 3.1 AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:N (8.2). Preconditions: a valid victim tmbId and any valid teamId (both Mongo ObjectIds — enumerable/obtainable, reflected in AC:L).
Suggested fix
Do not ship a default for INVOKE_TOKEN_SECRET — fail closed if unset and require a strong random secret at startup (as for TOKEN_KEY), and verify the token's teamId/tmbId matches the resolved object instead of trusting the client-supplied tmbId.
Affected
labring/FastGPT — default/official Docker deployments (INVOKE_TOKEN_SECRET unset → constant 'token'). Verified at commit 53d39a8f1f375b787f945d8d0c17b643575a7af2.
Summary
FastGPT's plugin "invoke" reverse-call endpoints (
/api/invoke/*) authenticate only by verifying a JWT signed withINVOKE_TOKEN_SECRET, which defaults to the constant string'token'(packages/service/env.ts:300) and is not set in the official deployment templates. An unauthenticated attacker can self-sign a valid HS256 JWT with secret'token'and an arbitrary payload, fully bypassing authentication. Two handlers are reachable this way:POST /api/invoke/userInfo→handleGetUserInfo()resolves the user purely from the attacker-suppliedtmbId(a 24-hex Mongo ObjectId, enumerable) with no cross-check againstteamId, returning any user's username, registration email/phone (PII), member name, org and group names — unauthenticated cross-tenant PII disclosure.POST /api/invoke/fileUpload→handleFileUpload()writes attacker-controlled content intouploadChatFile({appId, chatId, uId})for any chat — cross-tenant chat-file planting.Details
packages/service/env.ts:300:INVOKE_TOKEN_SECRET: z.string().default('token')packages/service/support/invoke/invoke.ts:24:static jwtSecret = serviceEnv.INVOKE_TOKEN_SECRET;:47:jwt.verify(token, this.jwtSecret)is the only authentication for these routes.projects/app/src/pages/api/invoke/userInfo.ts:18:InvokeProcessor.getInstanceFromToken(token).handleGetUserInfo(), wrapped inNextAPI(handler)(NextEntry({ beforeCallback: [] }), no session/global auth); there is no Next.js middleware gating/api/invoke/*.deploy/version/main/docker-compose.template.yml: setsTOKEN_KEY/FILE_TOKEN_KEY/AES256_SECRET_KEYbut omitsINVOKE_TOKEN_SECRET(0 occurrences), so default/official Docker deployments run with the constant'token'.getInstanceFromTokenonly doesjwt.verify+ a zod schema parse;handleGetUserInfocallsgetUserDetail({ tmbId })and returnsuser.contact(email/phone) from the attacker-suppliedtmbId(it only requires that the suppliedteamIdis an existing team, not that it owns the user).Proof of Concept
Impact
Unauthenticated cross-tenant disclosure of any user's PII (email, phone, names, org/group membership) and unauthenticated cross-tenant chat-file write, on any FastGPT deployment following the official Docker templates (which do not set
INVOKE_TOKEN_SECRET). CWE-798 (use of hard-coded / default credential) + CWE-639 (authorization bypass through user-controlledtmbId). CVSS 3.1AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:N(8.2). Preconditions: a valid victimtmbIdand any validteamId(both Mongo ObjectIds — enumerable/obtainable, reflected in AC:L).Suggested fix
Do not ship a default for
INVOKE_TOKEN_SECRET— fail closed if unset and require a strong random secret at startup (as forTOKEN_KEY), and verify the token'steamId/tmbIdmatches the resolved object instead of trusting the client-suppliedtmbId.Affected
labring/FastGPT — default/official Docker deployments (
INVOKE_TOKEN_SECRETunset → constant'token'). Verified at commit53d39a8f1f375b787f945d8d0c17b643575a7af2.