{"id":"GHSA-9g2q-w3w2-vf7q","summary":"Kimai has Missing Voter Check that Allows Cross-Team Timesheet Manipulation","details":"### Summary\n\nAny ROLE_TEAMLEAD user can enumerate, read, modify, and permanently delete timesheets belonging to any other user in the system — regardless of team membership. This enables data destruction (deleted billable hours), data tampering (forged timesheet durations), and full authorization bypass on timesheet resources. Verified against Kimai 2.52.0.\n\n### Details\n\n`TimesheetVoter::voteOnAttribute()` maps permissions to `own_timesheet` or `other_timesheet` without checking team membership. The voter's own comment confirms this is a known gap:\n\n```php\n// extend me for \"team\" support later on\nif ($subject-\u003egetUser()?-\u003egetId() === $user-\u003egetId()) {\n    $permission .= 'own';\n} else {\n    $permission .= 'other';\n}\n```\n\n### PoC\n\nTested against Kimai 2.52.0 Docker instance.\n\nSetup:\n- User A (usera, ROLE_TEAMLEAD) owns timesheet ID 2 with description \"Private timesheet - UserA only\"\n- User B (userb, ROLE_TEAMLEAD) is NOT on any team with User A\n\n**User B reads User A's timesheet data:**\n\n```\nGET /api/timesheets/2 HTTP/1.1\nX-AUTH-USER: userb\nX-AUTH-TOKEN: \u003cuserb_api_token\u003e\n```\n\nResponse: HTTP 200 — returns full timesheet record including description \"Private timesheet - UserA only\".\n\n**User B deletes User A's timesheet:**\n\n```\nDELETE /api/timesheets/3 HTTP/1.1\nX-AUTH-USER: userb\nX-AUTH-TOKEN: \u003cuserb_api_token\u003e\n```\n\nResponse: HTTP 204 No Content — timesheet permanently deleted.\n\n**User B tampers User A's timesheet:**\n\n```\nPATCH /api/timesheets/6 HTTP/1.1\nX-AUTH-USER: userb\nX-AUTH-TOKEN: \u003cuserb_api_token\u003e\nContent-Type: application/json\n\n{\"begin\":\"2026-03-24T08:00:00\",\"end\":\"2026-03-24T18:00:00\",\"project\":1,\"activity\":1,\"description\":\"TAMPERED\",\"exported\":false,\"billable\":false}\n```\n\nResponse: HTTP 200 OK — duration inflated from 3600s to 36000s, description overwritten.\n\n**Note:** ROLE_USER (userc) is correctly blocked — DELETE returns 403 and the actions endpoint returns an empty array. The vulnerability only affects ROLE_TEAMLEAD and above. Timesheet IDs are sequential integers, trivially enumerable.\n\n### Impact\n\nAny authenticated user with ROLE_TEAMLEAD or above can:\n\n1. Permanently delete timesheets belonging to any user system-wide — destroying billable hours, payroll data, and project billing history\n2. Silently alter timesheet descriptions, hours, and billing flags — forging hours up or down, directly affecting invoicing and payroll\n3. Enumerate all timesheet IDs (sequential integers) and access action metadata for arbitrary records\n\nNo user interaction required. ROLE_USER accounts are correctly restricted; the vulnerability is specific to ROLE_TEAMLEAD receiving global scope instead of team-scoped access.\n\n### Maintainers answer: why this is not eligible for a CVE\n\nThe behavior described matches the documented permission model. Per the Kimai documentation, the relevant permissions granted to `ROLE_TEAMLEAD` are:\n\n- `edit_other_timesheet` — Edit existing records of other users\n- `delete_other_timesheet` — Delete existing records of other users\n\nThese permissions were global by design, not team-scoped. The UI surfaces only the teamlead's own team timesheets, but the API has historically honored these permissions as documented: a role holding `*_other_timesheet` can act on any other user's timesheet. The inline comment `// extend me for \"team\" support later on` reflects this accurately — team-scoped enforcement was a planned enhancement, not a security control that existed and failed.\n\nThe report frames this as authorization bypass, but no authorization boundary is being crossed: `ROLE_TEAMLEAD` is operating within its documented permissions.\n\nKimai acknowledges that this behavior might not be expected, so while it will be treated as a feature request for team-scoped permission enforcement and not a vulnerability, it still track it as having security implications.\n\n### Solution\n\nTeam-scoped timesheet permission checks were added in 2.56.0.\n\nOperators of Kimai \u003c= 2.55 who need stricter isolation between teamleads should not grant `ROLE_TEAMLEAD` to users who must not act on other teams' timesheets.","aliases":["CVE-2026-80202"],"modified":"2026-08-27T04:10:44.398113559Z","published":"2026-05-06T18:28:45Z","database_specific":{"github_reviewed_at":"2026-05-06T18:28:45Z","nvd_published_at":null,"cwe_ids":["CWE-863"],"severity":"MODERATE","github_reviewed":true},"references":[{"type":"WEB","url":"https://github.com/kimai/kimai/security/advisories/GHSA-9g2q-w3w2-vf7q"},{"type":"PACKAGE","url":"https://github.com/kimai/kimai"}],"affected":[{"package":{"name":"kimai/kimai","ecosystem":"Packagist","purl":"pkg:composer/kimai/kimai"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.56.0"}]}],"versions":["0.1","0.2","0.3","0.4","0.5","0.6","0.6.1","0.7","0.8","0.8.1","0.9","1.0","1.0.1","1.1","1.10","1.10.1","1.10.2","1.11","1.11.1","1.12","1.13","1.14","1.14.1","1.14.2","1.14.3","1.15","1.15.1","1.15.2","1.15.3","1.15.4","1.15.5","1.15.6","1.16","1.16.1","1.16.10","1.16.2","1.16.3","1.16.4","1.16.5","1.16.6","1.16.7","1.16.8","1.16.9","1.17","1.17.1","1.18","1.18.1","1.18.2","1.19","1.19.1","1.19.2","1.19.3","1.19.4","1.19.5","1.19.6","1.19.7","1.2","1.20","1.20.1","1.20.2","1.20.3","1.20.4","1.21.0","1.22.0","1.22.1","1.23.0","1.23.1","1.24.0","1.25.0","1.26.0","1.27.0","1.28.0","1.28.1","1.29.0","1.29.1","1.3","1.30.0","1.30.1","1.30.10","1.30.11","1.30.2","1.30.3","1.30.4","1.30.5","1.30.6","1.30.7","1.30.8","1.30.9","1.4","1.4.1","1.4.2","1.5","1.6","1.6.1","1.6.2","1.7","1.8","1.9","2.0.0","2.0.0-alpha","2.0.0-beta","2.0.0-beta-2","2.0.0-beta-3","2.0.0-rc-1","2.0.1","2.0.10","2.0.11","2.0.12","2.0.13","2.0.14","2.0.15","2.0.16","2.0.17","2.0.18","2.0.19","2.0.2","2.0.20","2.0.21","2.0.22","2.0.23","2.0.24","2.0.25","2.0.26","2.0.27","2.0.28","2.0.29","2.0.3","2.0.30","2.0.31","2.0.32","2.0.33","2.0.34","2.0.35","2.0.4","2.0.5","2.0.6","2.0.7","2.0.8","2.0.9","2.1.0","2.10.0","2.11.0","2.12.0","2.13.0","2.14.0","2.15.0","2.16.0","2.16.1","2.17.0","2.18.0","2.19.0","2.19.1","2.2.0","2.2.1","2.20.0","2.20.1","2.21.0","2.22.0","2.23.0","2.24.0","2.25.0","2.26.0","2.27.0","2.28.0","2.29.0","2.3.0","2.30.0","2.31.0","2.32.0","2.33.0","2.34.0","2.35.0","2.35.1","2.36.0","2.36.1","2.37.0","2.38.0","2.39.0","2.4.0","2.4.1","2.40.0","2.41.0","2.42.0","2.43.0","2.44.0","2.45.0","2.46.0","2.47.0","2.48.0","2.49.0","2.5.0","2.50.0","2.51.0","2.52.0","2.53.0","2.54.0","2.55.0","2.6.0","2.7.0","2.8.0","2.9.0"],"database_specific":{"last_known_affected_version_range":"\u003c= 2.55.0","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/05/GHSA-9g2q-w3w2-vf7q/GHSA-9g2q-w3w2-vf7q.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V4","score":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N/E:P"}]}