GHSA-qh43-xrjm-4ggp CVE-2026-40486
Kimai's User Preferences API allows standard users to modify restricted attributes: hourly_rate, internal_rate
- Severity
- 4.3 / 10
- Medium · CVSS 3.1
- Exploit chance (EPSS)
- 0.27%
- Chance of exploitation in the next 30 days. Higher than 18.88% of scored vulnerabilities.
- Known exploited
- No
- Not in CISA's catalog
- Weakness
- CWE-915 Improperly Controlled Modification of Dynamically-Determined Object Attributes
- Fix
2.53.0- Upgrade kimai/kimai to this version or later
Description
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
- Log into Kimai as an unprivileged, standard employee account (a user with absolutely no
rolesarray privileges). - Capture the
cookieor Session cookies. (In this example, the user's ID is2). - 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"
}
]'
- The server responds with
HTTP/1.1 200 OK. (Note: Thehourly_ratewill intentionally NOT appear in the JSON echo due toUser::getVisiblePreferencessanitizing output based on the same disabled flag). - If an Administrator organically views User 2's profile within Kimai, or if the user logs any new timesheets, the active and billed
hourly_rateapplied to their account will be confirmed as1337. <img width="1542" height="1039" alt="user_account" src="https://github.com/user-attachments/assets/fff5e2da-d598-408d-8a01-784499ade844" /> <img width="1539" height="1037" alt="admin_account" src="https://github.com/user-attachments/assets/86a6e8c3-a97f-4be3-9f9f-2e23fad1d8a0" />
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.
Affected versions
Packagist
| Package | Vulnerable | Fixed in |
|---|---|---|
| kimai/kimai | < 2.53.0 |
2.53.0 |
How to fix
Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.
kimai/kimai Packagist → 2.53.0
- composer
composer require kimai/kimai:^2.53.0
Severity breakdown
CVSS 3.1 4.3
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N
- Attack vector
- Network
- Attack complexity
- Low
- Privileges required
- Low
- User interaction
- None
- Scope
- Unchanged
- Confidentiality
- None
- Integrity
- Low
- Availability
- None
Weakness
CWE-915: Improperly Controlled Modification of Dynamically-Determined Object Attributes
The product receives input from an upstream component that specifies multiple attributes, properties, or fields that are to be initialized or updated in an object, but it does not properly control which attributes can be modified.
How to prevent it
- Implementation: If available, use features of the language or framework that allow specification of allowlists of attributes or fields that are allowed to be modified. If possible, prefer allowlists over denylists. For applications written with Ruby on Rails, use the attr_accessible (allowlist) or attr_protected (denylist) macros in each class that may be used in mass assignment.
- Architecture and Design, Implementation: If available, use the signing/sealing features of the programming language to assure that deserialized data has not been tainted. For example, a hash-based message authentication code (HMAC) could be used to ensure that data has not been modified.
- Implementation: For any externally-influenced input, check the input against an allowlist of internal object attributes or fields that are allowed to be modified.
- Implementation, Architecture and Design: Refactor the code so that object attributes or fields do not need to be dynamically identified, and only expose getter/setter functionality for the intended attributes.
References
Advisories
Packages
Other links
Timeline
- Published
- Last updated by the source
- Latest EPSS score
Related advisories
| Advisory | Severity | Why it's related |
|---|---|---|
| GHSA-rw46-qg69-vg6h Kimai: ExportTemplate CRUD Missing Authorization Check Allows Unauthorized TEAMLEAD Access | Medium | Also affects kimai/kimai |
| GHSA-v8hx-4vx8-wc96 Kimai: Pre-2FA KIMAI_SESSION cookie grants full authenticated REST API access, bypassing TOTP | High | Also affects kimai/kimai |
| GHSA-2xgg-2x8h-8xw4 Kimai: Improper Authorization in Project, Customer, and Activity Rate Edit Endpoints Allows Cross-Scope Rate Manipulation | Medium | Also affects kimai/kimai |
| GHSA-xv4r-4885-gwpg Kimai has Improper Authorization in Team Member and Team Activity Assignment APIs Which Allows Expansion of Team Scope Beyond Authorized Visibility | Medium | Also affects kimai/kimai |
| GHSA-jr9p-4h4j-6c58 Kimai: Default APP_SECRET in Docker Image Enables Cookie Forgery and Account Takeover | Critical | Also affects kimai/kimai |
| GHSA-r8vr-m544-qh4h Kimai: Login CSRF in the Timesheet Stop and Restart API Endpoints Allows Unauthorized State Changes | Medium | Also affects kimai/kimai |
| GHSA-c6w6-57jj-62vh Improper Authorization in Kimai Timesheet Restart and Duplicate Allows New Timesheets After Project Access Revocation | Medium | Also affects kimai/kimai |
| GHSA-3q6q-26vg-v97x Kimai: Improper Authorization Through Activity Creation with Preset Project Allows Creation Under Unauthorized Projects | Medium | Also affects kimai/kimai |
MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.
Check your repos with MergeHarbor