Skip to main content
Vulnerability Database/CVE-2026-100371

CVE-2026-100371: InvoicePlane Auth Bypass Vulnerability

CVE-2026-100371 is an authorization bypass flaw in InvoicePlane that allows secondary administrators to take over the primary admin account by manipulating email addresses. This article covers technical details, affected versions, impact, and mitigation strategies.

Published:

CVE-2026-100371 Overview

CVE-2026-100371 is a broken authorization vulnerability in InvoicePlane, a self-hosted open source application for managing invoices, clients, and payments. Version 1.7.2 introduced an authorization guard on Users::change_password() to prevent secondary administrators from directly resetting the primary administrator's password. That fix did not extend to Users::form(), and the user_email attribute was omitted from PROTECTED_FIELDS. A secondary administrator (user_type=1, user_id != 1) can rewrite the primary administrator's email address and then trigger the public password-recovery flow to receive the reset token, taking over user_id=1. The issue is classified as [CWE-863: Incorrect Authorization].

Critical Impact

A secondary administrator can achieve full account takeover of the primary administrator via an alternate password-recovery path, bypassing the fix delivered in PR #1638.

Affected Products

  • InvoicePlane 1.7.2
  • InvoicePlane versions relying on the pre-8616fa4Users::form() authorization logic
  • Self-hosted InvoicePlane deployments exposing the users administration module to secondary administrators

Discovery Timeline

  • 2026-09-28 - CVE-2026-100371 published to NVD
  • 2026-09-30 - Last updated in NVD database

Technical Details for CVE-2026-100371

Vulnerability Analysis

InvoicePlane 1.7.2 shipped a targeted authorization guard on Users::change_password() to close a prior cross-administrator password-change flaw. The remediation protects only the direct password-change action. It leaves Users::form() — the general user-edit handler — without an equivalent object-level authorization check when a secondary administrator edits the primary administrator's record. The PROTECTED_FIELDS constant in Mdl_users.php blocks writes to user_type, user_active, and user_psalt, but not to user_email. Password recovery resolves an account by user_email, so mutating that attribute is equivalent to transferring identity ownership. This makes the recovery workflow the pivot for takeover.

Root Cause

The root cause is inconsistent object-level authorization across code paths that mutate the same privileged record. Users::form() treats a secondary administrator as authorized to edit arbitrary user rows, and the field allow-list omits the identity attribute that downstream recovery trusts. Authorization was enforced on the wrong operation rather than on the record boundary of user_id=1.

Attack Vector

An authenticated secondary administrator navigates to the user-edit form for user_id=1 and submits a modified user_email value. The attacker then requests a password reset from the public recovery endpoint using the attacker-controlled email. InvoicePlane emails a reset token to the attacker, who sets a new password and logs in as the primary administrator.

php
// Security patch: application/modules/users/controllers/Users.php
         $id = $id ? (int) $id : null;
         $current_user_id = (int) $this->session->userdata('user_id');
         $is_self_edit = $id && $id === $current_user_id;
-        $is_primary_admin = $current_user_id === 1;
+        $is_primary_admin = Mdl_Users::is_primary_administrator($current_user_id);

+        // Object-level authorization: only the primary administrator may edit
+        // another user's record. A peer administrator is limited to its own
+        // account, so it cannot rewrite user_id 1's role, email or password
+        // through this form (CWE-639 / CWE-269).
         if ($id && !$is_self_edit && !$is_primary_admin) {
             show_error(trans('access_denied'), 403);

Source: InvoicePlane commit 8616fa4

php
// Security patch: application/modules/users/models/Mdl_users.php
     private const PROTECTED_FIELDS = ['user_type', 'user_active', 'user_psalt'];

+    /**
+     * The bootstrap root account. Only this account may edit its own record;
+     * a peer administrator must not be able to alter any of its attributes
+     * (password, email, role type, active flag, or otherwise).
+     */
+    public const PRIMARY_ADMINISTRATOR_ID = 1;
+
+    /**
+     * Identity- and privilege-bearing fields the primary administrator's record
+     * must never receive from a non-root session, regardless of code path.
+     */
+    private const PRIMARY_ADMIN_LOCKED_FIELDS = ['user_type', 'user_active', 'user_psalt', 'user_email', 'user_password'];

Source: InvoicePlane commit 8616fa4

Detect Identity Attacks the Moment Authentication Fails

Singularity™ Identity surfaces credential misuse, lateral movement, and privilege escalation in real time, so you can contain the breach before it spreads.

Stop Identity Attacks

Detection Methods for CVE-2026-100371

Indicators of Compromise

  • POST requests to /users/form/1 originating from a session whose user_id is not 1.
  • Audit records showing a change to user_email on user_id=1 followed by a password-reset request for that new address.
  • Successful login events for user_id=1 from an IP address or user-agent previously associated with a secondary administrator account.
  • Recent modifications to ip_users.user_email where the row's user_id equals 1.

Detection Strategies

  • Instrument the users controller to log every mutation to user_id=1, including the acting session.user_id, request path, and field diff.
  • Correlate user_email changes on privileged accounts with subsequent password-recovery requests within a short time window.
  • Alert when a non-root session issues a POST to users/form/{id} with id != session.user_id.

Monitoring Recommendations

  • Enable web server access logging with request bodies redacted, and forward to a centralized SIEM for query.
  • Monitor outbound mail queues for password-reset emails destined for domains that do not match the historical primary administrator address.
  • Track administrative login sources and flag geolocation or ASN changes on user_id=1.

How to Mitigate CVE-2026-100371

Immediate Actions Required

  • Upgrade InvoicePlane to a build that includes commit 8616fa4 or later.
  • Audit the ip_users table for unexpected values in user_email, user_type, or user_active on user_id=1.
  • Force a password reset for the primary administrator and rotate any API tokens tied to that account.
  • Review the secondary administrator roster and revoke accounts that no longer require elevated access.

Patch Information

The issue is patched via commit 8616fa4. The fix replaces the current_user_id === 1 check with Mdl_Users::is_primary_administrator(), enforces object-level authorization in Users::form(), and expands the locked-field set to include user_email and user_password through the new PRIMARY_ADMIN_LOCKED_FIELDS constant. Review the InvoicePlane Security Advisory GHSA-77hm-22wp-96wp and the GitHub Pull Request #1638 discussion for background.

Workarounds

  • Restrict the users module to the primary administrator by placing web server ACLs on /users/form/* until patching is complete.
  • Reduce all user_type=1 accounts to non-administrative roles where operationally feasible.
  • Disable the public password-recovery endpoint temporarily and process resets manually via database access.
bash
# Configuration example: nginx location block restricting user edits to trusted IPs
location ~ ^/users/(form|change_password)/ {
    allow 10.0.0.0/24;
    deny all;
    try_files $uri /index.php?$args;
}

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.