CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-33318
8.8

CVE-2026-33318: Privilege Escalation via Sequential Exploit Chain in Actual sync-server

Alon Barad
Alon Barad
Software Engineer

Apr 24, 2026·5 min read·6 visits

PoC Available

Executive Summary (TL;DR)

A privilege escalation vulnerability in Actual's sync-server allows authenticated users with basic roles to obtain administrative privileges. The issue stems from missing authorization checks on password change endpoints, blind credential overwrites, and improper handling of client-controlled authentication methods during OIDC migration.

Actual versions prior to 26.4.0 contain a critical privilege escalation vulnerability within the sync-server component. The flaw affects environments migrating to OpenID Connect (OIDC) and allows an authenticated user with a basic role to hijack an administrative account via an orphaned password record.

Vulnerability Overview

The Actual sync-server component prior to version 26.4.0 is vulnerable to a privilege escalation attack. This vulnerability affects deployments that have migrated from legacy password-based authentication to OpenID Connect (OIDC). The issue is formally tracked as CVE-2026-33318 and GHSA-prp4-2f49-fcgp.

The vulnerability is classified under CWE-284 (Improper Access Control) and CWE-862 (Missing Authorization). The core issue stems from multiple architectural flaws interacting within the sync-server authentication flow. These flaws interact sequentially, allowing a low-privileged user to hijack the primary administrative account.

Exploitation requires the system to have undergone an OIDC migration, which leaves an orphaned password database record. The attacker must possess an active session token, even with the lowest privilege level (BASIC), to initiate the exploit chain. No further interaction from the administrator is required.

Technical Root Cause Analysis

The vulnerability is a sequential exploit chain relying on four distinct architectural flaws. First, the POST /account/change-password endpoint in packages/sync-server/src/app-account.js lacks proper authorization checks. It validates the presence of a session token but fails to verify the user's role, allowing BASIC role users to invoke it.

Second, the changePassword() function in packages/sync-server/src/accounts/password.js performs a blind credential overwrite. It updates the password hash via an unconditional SQL UPDATE on the auth table where method = 'password', without requiring the current password. This allows an attacker to alter credentials for an account they do not own.

Third, migration scripts introduced ghost records. The script 1719409568000-multiuser.js created an administrative account with an empty user_name (''). When migrating to OIDC, the password row in the auth table was marked active = 0 but not deleted. This left an orphaned, inactive password row tied to the admin account.

Fourth, the getLoginMethod() function in packages/sync-server/src/account-db.js trusts client input. It prioritizes the loginMethod parameter from the HTTP request body over server configuration. Crucially, it fails to check the active flag in the database, permitting the client to force the legacy password flow even when the server expects OIDC authentication.

Exploit Chain Methodology

Exploitation requires network access and a valid OIDC session with a BASIC role. The attacker initiates the chain by obtaining a standard session token from the OIDC provider. This token fulfills the basic authentication requirement for subsequent API calls to the sync-server.

The attacker sends a crafted POST request to /account/change-password containing a new password payload. The server processes this request without validating the user's role. The SQL UPDATE statement executes against the orphaned administrative row (where method = 'password'), overwriting the admin's password hash with the attacker's supplied value.

Next, the attacker sends a POST request to the login endpoint, explicitly setting the payload {"loginMethod": "password"} alongside the newly set credentials. The getLoginMethod() function honors this client-controlled parameter, bypassing the intended OIDC flow entirely.

The server authenticates the attacker using the legacy method, disregarding the active = 0 status of the database row. The server issues a new session token with ADMIN privileges, completing the privilege escalation.

Impact Assessment

The vulnerability carries a CVSS v3.1 base score of 8.8. The confidentiality, integrity, and availability impacts are all rated as High. Successful exploitation grants the attacker full administrative access to the Actual instance.

With administrative privileges, the attacker can view and exfiltrate all financial data stored within the system. They can manipulate budgets, modify account balances, and alter transaction histories. The integrity of the financial records is entirely compromised.

Furthermore, the attacker can disrupt the availability of the service. They can lock out legitimate users by changing account credentials, alter server configurations, or execute data deletion operations. The vulnerability bypasses the intended security boundary of the OIDC migration.

Remediation and Patch Analysis

The vulnerability is fully resolved in Actual version 26.4.0. Administrators must update the sync-server component immediately. As a defense-in-depth measure, administrators of OIDC-migrated servers should manually inspect the auth table and delete orphaned rows where method = 'password'.

The patch was implemented across multiple pull requests. PR #7155 resolved the initial privilege escalation by enforcing role checks in the /change-password endpoint. It also hardened getLoginMethod() to ignore client-supplied overrides, forcing the server to dictate the authentication flow based on backend configuration.

PR #7207 introduced strict "admin-only" role requirements for password changes. It also required that sessions attempting to change passwords must have been authenticated via the password method initially. This severs the attack path between OIDC-authenticated sessions and legacy password modification.

Finally, PR #7334 addressed the ghost record issue. The patch enforces a strict check on the active status of authentication methods during login. If an account is marked active = 0, the server refuses the authentication attempt, neutralizing the client-side loginMethod override vector.

Official Patches

Actual BudgetOfficial Release Blog
Actual BudgetGitHub Security Advisory

Technical Appendix

CVSS Score
8.8/ 10
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

Affected Systems

Actual sync-server component (prior to v26.4.0)

Affected Versions Detail

Product
Affected Versions
Fixed Version
Actual
Actual Budget
< 26.4.026.4.0
AttributeDetail
CWECWE-284, CWE-862
Attack VectorNetwork (AV:N)
CVSS Score8.8 (High)
Privileges RequiredLow (PR:L)
User InteractionNone (UI:N)
Exploit Statusweaponized

MITRE ATT&CK Mapping

T1190Exploit Public-Facing Application
Initial Access
T1068Exploitation for Privilege Escalation
Privilege Escalation
T1078Valid Accounts
Defense Evasion
CWE-284
Improper Access Control

Improper Access Control

Vulnerability Timeline

Initial discovery of related authentication weaknesses during server refactoring.
2026-03-04
Official release of version 26.4.0 containing the full fix.
2026-04-05
Public disclosure via the Actual Budget blog.
2026-04-05
CVE-2026-33318 published to the Global CVE Record.
2026-04-24

References & Sources

  • [1]GitHub Security Advisory GHSA-prp4-2f49-fcgp
  • [2]Actual Budget Release 26.4.0 Blog Post
  • [3]CVE.org Record for CVE-2026-33318
  • [4]Actual Budget Primary Repository

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.