getProjectByName/ByIdentifier/ByEmail: distinguishable responses allow probing existence of arbitrary (private) projects
Environment
Kanboard v1.2.53 (Docker image kanboard/kanboard:v1.2.53), JSON-RPC API (POST /jsonrpc.php, HTTP Basic auth).
Summary
The project lookup procedures getProjectByName, getProjectByIdentifier and getProjectByEmail return distinguishable responses for "project exists but caller is unauthorized" versus "no such project", allowing any authenticated user to probe the existence of arbitrary projects, including private ones. Verified empirically on v1.2.53.
Root cause
The resolve-then-authorize shape (app/Api/Procedure/ProjectProcedure.php:22-56): the "not found" branch returns a plain false (successful JSON-RPC response), while the "found but forbidden" branch throws AccessDeniedException (JSON-RPC error 403). The difference is trivially scriptable.
Steps to reproduce
- As user A (regular):
createMyPrivateProjectwith a distinctive name -> project exists. - As user B (regular, no relation to the project):
{"method":"getProjectByName","params":{"name":"<that exact name>"}}->{"error":{"code":403,"message":"Forbidden"}}. - As user B: same call with a random unknown name -> successful response with
result: false. - The two shapes differ on every attempt, so any candidate name (or identifier, or project email via
getProjectByEmail) can be classified as existing / non-existing.
Impact
Information exposure through response discrepancy (CWE-204). Project names, identifiers and configured project emails can be enumerated organization-wide, including private project names which may carry business-confidential meaning. No project content is disclosed and no further access control bypass is possible with this oracle alone. CVSS:3.1 AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N (3.1, Low).
Suggested fix
Unify the response shape for "not found" and "found but forbidden" (both return false, or both raise the same error), eliminating the differential.
Source: kanboard/kanboard