GHSA-JV9X-W4GM-HWCM

Vulnerability from github – Published: 2026-04-24 16:17 – Updated: 2026-05-12 13:27
VLAI
Summary
Kimai has Missing Object-Level Authorization in the Team API
Details

Summary

The Team API endpoints use #[IsGranted('edit_team')] instead of #[IsGranted('edit', 'team')], causing Symfony TeamVoter to abstain from voting. This removes entity-level ownership checks on team operations, allowing any user with the edit_team permission to modify any team, not just teams they are authorized to manage.

Details

All 8 team association endpoints in src/API/TeamController.php (lines 177, 201, 229, 252, 275, 298, 321, 339) use #[IsGranted('edit_team')] with a single argument. The web controller at src/Controller/TeamController.php:118 correctly uses #[IsGranted('edit', 'team')] with two arguments, passing the $team parameter as the subject. When edit_team is passed as the attribute, TeamVoter::supportsAttribute() returns false because it only recognizes view, edit, and delete. The voter abstains entirely. Only RolePermissionVoter fires, which checks the role-level permission without any entity-level ownership validation.

PoC

Authenticate as a user with edit_team permission who is NOT a member of Team 1

curl -X POST https://TARGET/api/teams/1/members/2 \
  -H "Authorization: Bearer <API_TOKEN>" \
  -H "Content-Type: application/json"

Expected: 403 Forbidden (user is not ROLE_ADMIN/ROLE_SUPER_ADMIN, or member of Team 1)

Actual (pre-2.54.0): 200 OK, user added to Team 1

Impact

In default configuration, only ROLE_ADMIN and ROLE_SUPER_ADMIN have edit_team, and both roles already have irrevocable view_all_data access, making the missing check redundant. The vulnerability becomes exploitable if an administrator grants edit_team to a lower-privilege role (such as ROLE_TEAMLEAD) through the permissions UI. In that scenario, the lower-privilege user could modify any team's membership, customer assignments, project assignments, and activity assignments without being a member or teamlead of that team.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Packagist",
        "name": "kimai/kimai"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.54.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-41498"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-862"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-04-24T16:17:35Z",
    "nvd_published_at": "2026-05-08T04:16:14Z",
    "severity": "LOW"
  },
  "details": "### Summary\nThe Team API endpoints use #[IsGranted(\u0027edit_team\u0027)] instead of #[IsGranted(\u0027edit\u0027, \u0027team\u0027)], causing Symfony TeamVoter to abstain from voting. This removes entity-level ownership checks on team operations, allowing any user with the edit_team permission to modify any team, not just teams they are authorized to manage.\n\n### Details\nAll 8 team association endpoints in src/API/TeamController.php (lines 177, 201, 229, 252, 275, 298, 321, 339) use #[IsGranted(\u0027edit_team\u0027)] with a single argument. The web controller at src/Controller/TeamController.php:118 correctly uses #[IsGranted(\u0027edit\u0027, \u0027team\u0027)] with two arguments, passing the $team parameter as the subject.\nWhen edit_team is passed as the attribute, TeamVoter::supportsAttribute() returns false because it only recognizes view, edit, and delete. The voter abstains entirely. Only RolePermissionVoter fires, which checks the role-level permission without any entity-level ownership validation.\n\n### PoC\n#### Authenticate as a user with edit_team permission who is NOT a member of Team 1\n```\ncurl -X POST https://TARGET/api/teams/1/members/2 \\\n  -H \"Authorization: Bearer \u003cAPI_TOKEN\u003e\" \\\n  -H \"Content-Type: application/json\"\n```\n\n#### Expected: 403 Forbidden (user is not ROLE_ADMIN/ROLE_SUPER_ADMIN, or member of Team 1)\n#### Actual (pre-2.54.0): 200 OK, user added to Team 1\n\n### Impact\nIn default configuration, only ROLE_ADMIN and ROLE_SUPER_ADMIN have edit_team, and both roles already have irrevocable view_all_data access, making the missing check redundant. The vulnerability becomes exploitable if an administrator grants edit_team to a lower-privilege role (such as ROLE_TEAMLEAD) through the permissions UI. In that scenario, the lower-privilege user could modify any team\u0027s membership, customer assignments, project assignments, and activity assignments without being a member or teamlead of that team.",
  "id": "GHSA-jv9x-w4gm-hwcm",
  "modified": "2026-05-12T13:27:24Z",
  "published": "2026-04-24T16:17:35Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/kimai/kimai/security/advisories/GHSA-jv9x-w4gm-hwcm"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-41498"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/kimai/kimai"
    },
    {
      "type": "WEB",
      "url": "https://github.com/kimai/kimai/releases/tag/2.54.0"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:L/I:L/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Kimai has Missing Object-Level Authorization in the Team API"
}


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…