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-27584

Zero-Dollar Auth: Leaking Bank Data with ActualBudget (CVE-2026-27584)

Amit Schendel
Amit Schendel
Senior Security Researcher

Feb 25, 2026·6 min read·74 visits

Executive Summary (TL;DR)

The ActualBudget server forgot to ask "Who are you?" on its banking sync routes. Unauthenticated attackers could send POST requests to /simplefin or /pluggyai endpoints and download the server owner's complete financial history. Fixed in version 26.2.1 by adding session validation middleware.

ActualBudget, a local-first personal finance application designed for privacy enthusiasts, suffered from a critical authentication bypass in its server synchronization component. Specifically, the endpoints handling SimpleFIN and Pluggy.ai banking integrations lacked middleware verification, allowing unauthenticated attackers to query the server and retrieve sensitive financial data—including account balances and transaction histories—using the server owner's stored credentials.

The Hook: Privacy-First, Security-Maybe

In the modern era of surveillance capitalism, tools like ActualBudget are a godsend. They promise a "local-first" philosophy, meaning your financial data lives on your server, not in some venture-backed cloud that sells your transaction history to advertisers. Users self-host this stack, trusting that the isolation of their own infrastructure provides the ultimate security boundary.

But here is the dark irony of self-hosting: you are the sysadmin, the blue team, and the victim all at once. If the code you deploy has a hole, there is no corporate security team monitoring logs to save you. And CVE-2026-27584 is not just a hole; it is a tunnel.

This vulnerability targets the most sensitive part of the application: Bank Synchronization. To make life easier, ActualBudget integrates with providers like SimpleFIN and Pluggy.ai to automatically pull your bank transactions. The server acts as a proxy, holding the API secrets to talk to these banks. The vulnerability lies in how the server handled requests to trigger these syncs. Spoiler alert: it didn't handle them very well.

The Flaw: The Middleware That Wasn't There

Under the hood, the ActualBudget server is a Node.js application using Express.js. If you have ever written an Express app, you know that security is often a chain of responsibility—literally. You chain middleware functions together to filter traffic. A typical secure route looks like this:

app.get('/secret', checkAuth, returnSecret);

The checkAuth middleware acts as the bouncer. If you don't have a valid session, you don't get in. The flaw in ActualBudget was a classic case of "security by omission." The developers created separate sub-applications for different integrations (app-simplefin.js and app-pluggyai.js) to keep the code modular and clean. While the main application and other modules (like GoCardless) correctly implemented the validateSessionMiddleware, these two specific modules did not.

They were initialized with logging and JSON parsing, but the session guard was completely absent. In the world of web frameworks, silence is consent. Because the code did not explicitly say "Stop unauthenticated users," Express happily passed the requests straight to the business logic. The server assumed that if you were knocking on the door, you must belong there.

The Code: A One-Line Catastrophe

Let's look at the "smoking gun" in packages/sync-server/src/app-simplefin/app-simplefin.js. This is where the magic (and the tragedy) happens. The fix, applied in commit ea937d100956ca56689ff852d99c28589e2a7d88, reveals just how simple the oversight was.

The Vulnerable Code:

// ... imports ...
import { requestLoggerMiddleware } from '../util/middlewares';
 
const app = express();
export { app as handlers };
 
app.use(requestLoggerMiddleware);
app.use(express.json());
 
// ROUTES DEFINED BELOW IMMEDIATELY
app.post('/accounts', async (req, res) => {
  // ... fetches your bank data ...
});

Notice anything missing? The code logs the request (requestLoggerMiddleware) and parses the body (express.json()), but it never checks if the request comes from a logged-in user. It just executes.

The Fix:

+ import {
+   requestLoggerMiddleware,
+   validateSessionMiddleware, // <--- The Bouncer
+ } from '../util/middlewares';
 
  const app = express();
  export { app as handlers };
 
  app.use(requestLoggerMiddleware);
  app.use(express.json());
+ app.use(validateSessionMiddleware); // <--- The Lock

By adding that single line, the application now checks for a valid session cookie or token before processing any routes defined below it. Without it, the endpoint was effectively public API documentation for your bank account.

The Exploit: cURLing for Cash Flow

Exploiting this requires zero sophistication. There is no heap corruption, no race condition, and no cryptographic breakage. You simply ask the server for the data, and it gives it to you. An attacker scans the internet for exposed ActualBudget instances (which often run on default ports) and fires off a simple HTTP request.

Here is what a theoretical attack looks like. The attacker doesn't even need to know the user's username. They just hit the /simplefin/accounts endpoint.

#!/bin/bash
TARGET="http://victim-finance-server.com:5006"
 
# Step 1: Check if SimpleFIN is configured
echo "[*] Probing status..."
curl -s -X POST "$TARGET/simplefin/status" | jq .
 
# Step 2: Loot the vault
echo "[*] Extracting bank accounts..."
curl -s -X POST "$TARGET/simplefin/accounts" \
     -H "Content-Type: application/json" \
     -d '{}' | jq .

> [!WARNING] > The Response: If the server owner has connected their bank, the server responds with a JSON object containing accounts (balances, names, types) and potentially transactions (payees, amounts, dates).

The server performs the authentication with the bank using the stored keys on the backend, effectively laundering the unauthenticated request into an authenticated banking session.

The Impact: Financial Voyeurism

The CVSS score of 9.2 is not an exaggeration. While this vulnerability does not allow Remote Code Execution (RCE) or direct modification of the server's filesystem, the Confidentiality Impact is catastrophic. We are talking about personal finance data.

An attacker exploiting this can view:

  • Real-time Balances: How much money you have in checking, savings, and investment accounts.
  • Transaction History: Where you shop, how much you pay for rent, medical expenses, salary deposits, and debt payments.
  • Financial Habits: Enough metadata to construct a highly effective phishing campaign or simply doxx the user.

Furthermore, because ActualBudget is often self-hosted by technical users who might expose it to the internet for mobile access (without a VPN), the attack surface is wider than just "internal networks." The breach of trust here is total; a tool designed to protect your data from third parties ended up serving it to the entire internet.

The Fix: Closing the Door

The remediation is straightforward: Update to version 26.2.1 immediately. The developers have patched the missing middleware and added additional authorization checks to ensure that file access permissions are strictly enforced.

If you cannot update immediately, you must mitigate this at the network layer:

  1. Reverse Proxy Rules: If you use Nginx or Traefik, block access to paths starting with /simplefin and /pluggyai from external IP addresses.
  2. Disable Integrations: In the server configuration, remove the SimpleFIN and Pluggy tokens until the patch is applied.

For developers reading this, the lesson is clear: Authorization and Authentication should be default-deny. Use global middleware that enforces authentication on all routes by default, and only whitelist specific public endpoints (like login pages). Relying on remembering to add validateSession to every new file is a strategy that will eventually fail.

Official Patches

ActualBudgetOfficial Release Notes for v26.2.1
GitHubCommit fixing the authentication middleware

Fix Analysis (2)

Technical Appendix

CVSS Score
9.2/ 10
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N

Affected Systems

ActualBudget Server (sync-server)

Affected Versions Detail

Product
Affected Versions
Fixed Version
ActualBudget Server
ActualBudget
< 26.2.126.2.1
AttributeDetail
CWECWE-306 (Missing Authentication for Critical Function)
CVSS v4.09.2 (Critical)
Attack VectorNetwork (AV:N)
Attack ComplexityLow (AC:L)
Privileges RequiredNone (PR:N)
Confidentiality ImpactHigh (VC:H)
Affected ComponentsSimpleFIN & Pluggy.ai Integration Modules

MITRE ATT&CK Mapping

T1190Exploit Public-Facing Application
Initial Access
T1539Steal Web Session Cookie
Credential Access
CWE-306
Missing Authentication for Critical Function

The software does not perform any authentication for functionality that requires a provable user identity or consumes a significant amount of resources.

Known Exploits & Detection

TheoreticalDirect CURL POST request to /simplefin/accounts endpoints.

Vulnerability Timeline

Fix committed to repository (ea937d10)
2026-02-19
Version 26.2.1 released
2026-02-22
CVE-2026-27584 and GHSA published
2026-02-24

References & Sources

  • [1]GHSA-m2cq-xjgm-f668 Advisory
  • [2]ActualBudget Project Homepage

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.

More Reports

•16 minutes ago•CVE-2026-92937
10.0

CVE-2026-92937: Sandbox Escape leading to Remote Code Execution via Promise Indirection in vm2

CVE-2026-92937 is a critical sandbox escape vulnerability in the `vm2` Node.js library. Due to a logical failure in checking direct invocation targets inside the Proxy bridge, an attacker can register Promise callbacks using `Function.prototype.call` or `Function.prototype.apply` indirection. This bypasses the error sanitization wrappers, delivering raw host error objects directly to sandboxed callbacks and allowing the attacker to escape the sandbox and execute arbitrary shell commands on the host.

Amit Schendel
Amit Schendel
2 views•6 min read
•about 1 hour ago•CVE-2026-92935
9.5

CVE-2026-92935: Remote Code Execution via Array-Shaped Require Config in vm2 NodeVM Sandbox

CVE-2026-92935 is a critical sandbox escape and remote code execution vulnerability in the vm2 library. By supplying an array or exotic object to the require property of NodeVM while nesting is enabled, attackers can bypass security checks, load the host vm2 module, and run arbitrary shell commands on the hosting server.

Alon Barad
Alon Barad
3 views•7 min read
•about 2 hours ago•CVE-2026-92949
4.0

CVE-2026-92949: Sandbox Escape and State Mutation in vm2 via Accessor Property Descriptor Leak

CVE-2026-92949 is a sandbox bypass vulnerability in the vm2 library affecting versions 3.9.6 through 3.11.6. The flaw exists due to a breakdown in the ReadOnlyHandler proxy boundary, allowing sandboxed scripts to obtain direct references to wrapped property setters on frozen host-bound objects, ultimately leading to unauthorized state modification in the host environment. This security failure violates the read-only contract enforced by the sandbox for frozen/readonly objects, though it does not by itself allow a full execution-level realm escape. Due to systemic and structural design difficulties in securing a shared-runtime JavaScript sandbox, the vm2 library has been officially deprecated.

Amit Schendel
Amit Schendel
4 views•7 min read
•about 3 hours ago•CVE-2026-92957
9.9

CVE-2026-92957: Sandbox Escape and Remote Code Execution in vm2 via node: Prefix Policy Bypass

A vulnerability in the NodeVM component of the vm2 sandbox package through version 3.11.6 allows sandboxed code to bypass security policies restricting access to built-in modules. When a wildcard require policy is configured with negative deny entries using the 'node:' prefix (e.g., '-node:child_process'), the parser fails to recognize the exemption due to exact string comparison. As a result, the unmitigated module is registered, allowing sandboxed code to import the host child_process module and execute arbitrary system commands.

Alon Barad
Alon Barad
5 views•6 min read
•about 4 hours ago•CVE-2026-92958
8.5

CVE-2026-92958: Built-in Module Denylist Bypass via fs/promises in vm2 NodeVM Subsystem

CVE-2026-92958 is a high-severity sandbox escape and denylist bypass vulnerability within the NodeVM subsystem of the vm2 sandboxing library. When configuring wildcards with negative deny entries, exact-string matches fail to block subpaths like fs/promises. Sandboxed code can import these subpaths to bypass isolation and execute arbitrary filesystem operations on the host.

Amit Schendel
Amit Schendel
6 views•6 min read
•about 5 hours ago•CVE-2026-92951
9.9

CVE-2026-92951: Sandbox Escape via External Package Allowlist Bypass in vm2

An incorrect authorization and directory traversal vulnerability in the vm2 library before version 3.11.7 allows remote attackers to bypass the sandbox's external package allowlist. This flaw permits sandboxed code to resolve and execute arbitrary packages available on the host filesystem under host privileges, leading to unauthenticated sandbox escape and host code execution.

Alon Barad
Alon Barad
5 views•6 min read