Feb 14, 2026·6 min read·52 visits
Unprivileged users can define malicious 'future' fields or functions. When a privileged admin queries these records, the malicious code executes with the admin's full permissions, leading to total database takeover.
A critical Confused Deputy vulnerability in SurrealDB allows unprivileged users to execute arbitrary logic with Root privileges by injecting malicious 'futures' or closures. The database engine failed to capture the creation context of these definitions, executing them instead with the permissions of the invoker—often an administrator.
SurrealDB is the cool kid on the database block. It promises to be the database for everything—relational, document, graph, you name it. One of its killer features is the ability to embed logic directly into the data model. We aren't just talking about simple stored procedures; we are talking about futures, closures, and embedded functions that evaluate dynamically when data is accessed.
Imagine a database field that isn't just a static string, but a living, breathing piece of code that says, "Hey, calculate my value right now based on the current state of the universe." It is powerful. It is flexible. And as it turns out, it was a gaping security hole.
This vulnerability (GHSA-3v2x-9xcv-2v2v) is a classic "Confused Deputy" scenario. The database engine, in its eagerness to be helpful, forgot to ask a very important question: "Who wrote this code?" Instead, it only asked, "Who is running this code?" If you are a lowly editor, and you trick the Root Owner into running your code, guess what? You just became the Root Owner.
The root cause here is a failure to snapshot permissions at the time of definition. In security engineering, we often talk about the difference between creation context and execution context.
In a secure system, if User A (Editor) creates a function, that function should run with User A's permissions, even if User B (Root) calls it. SurrealDB, prior to version 2.5.0, did not track this distinction for futures and closures. It treated the code as part of the data, agnostic to its creator.
Here is the logic flow of the failure:
secret_backdoor with a FUTURE value containing a malicious payload (e.g., DEFINE USER attacker ON ROOT ...).SELECT * FROM table.FUTURE. It needs to compute the value to show it to User B. It executes the payload.The Critical Error: The engine executes the payload using User B's session context. Because User B is Root, the DEFINE USER command succeeds. The engine acted as the "Confused Deputy," performing a privileged action on behalf of an attacker who couldn't do it themselves.
The fix involved teaching SurrealDB to remember. The developers introduced a new struct called AuthLimit. This struct is essentially a sticky note attached to every piece of stored logic, recording the AuthLevel (Root, Namespace, Database, or Record) and the Role of the user who defined it.
Let's look at the concept behind the patch in f515c91363ee.
Before (Conceptual Rust):
// The computation just ran with whatever context was active
fn compute(ctx: &Context, code: &Code) -> Result<Value> {
// If ctx.user is Root, code runs as Root
return code.execute(ctx);
}After (Conceptual Rust):
// Now we have limits
struct AuthLimit {
level: AuthLevel,
role: Role,
}
fn compute(ctx: &Context, code: &Code, limit: &AuthLimit) -> Result<Value> {
// Calculate the INTERSECTION of privileges
let effective_permissions = intersect(ctx.permissions, limit);
// If the creator was an Editor, code runs as Editor,
// even if ctx.user is Root.
return code.execute_with_permissions(effective_permissions);
}The patch ensures that the execution privilege is the lowest common denominator between the invoker and the creator. If an Admin runs an Editor's script, it runs with Editor privileges. If an Editor runs an Admin's script, it runs with Editor privileges. This creates a sandbox that prevents upward escalation.
To exploit this, an attacker needs editor permissions (or any role that can write to a record) and a bit of patience. The goal is to plant a landmine that a privileged user will inevitably step on.
The attacker identifies a table that administrators frequently query or audit. Let's say audit_logs.
The attacker inserts a record with a malicious FUTURE. The payload uses SurrealQL to create a new root user.
UPDATE audit_logs:123 SET message = <future> {
-- This code sits dormant until read
DEFINE USER pwned ON ROOT PASSWORD 'hacker123' ROLES OWNER;
RETURN "Nothing to see here";
};The attacker waits. Alternatively, they can try to trigger an error or a report that forces an administrator to inspect that specific record.
The Administrator logs in and runs:
SELECT * FROM audit_logs;SurrealDB iterates through the records. When it hits audit_logs:123, it evaluates the message field. The DEFINE USER command executes under the Administrator's session.
The attacker can now log in as pwned with full Root access. They own the database, the data, and potentially the underlying server if they can leverage further file-system access features available to Root users.
Mitigation seems simple: Upgrade to version 2.5.0 or 3.0.0-beta.3. But there is a massive catch that could leave you vulnerable even after patching.
> [!WARNING] > The Legacy Trap
When you upgrade, SurrealDB has to decide what permissions to assign to existing futures and functions defined before the patch. Since it didn't track the creator before, it doesn't know who wrote them.
The decision? It defaults old definitions to Root/Owner level permissions to avoid breaking existing applications.
What this means: If an attacker planted a backdoor before you upgraded, and you upgrade without cleaning up, that backdoor is now officially sanctioned as a Root-level script by the migration logic. The patch fixes the hole for new definitions, but it essentially "grandfathers in" the exploit.
futures, functions, and closures.DEFINE FIELD ... again for every schema definition to stamp them with the correct, current AuthLimit.Simply restarting the server with the new version is not enough to clear existing malware.
CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
SurrealDB SurrealDB | < 2.5.0 | 2.5.0 |
SurrealDB SurrealDB | 3.0.0-alpha.1 - < 3.0.0-beta.3 | 3.0.0-beta.3 |
| Attribute | Detail |
|---|---|
| Attack Vector | Network |
| CVSS v4.0 | 7.7 (High) |
| Privileges Required | Low (Editor) |
| User Interaction | Passive (Required) |
| Impact | Privilege Escalation / Full Takeover |
| Bug Class | Confused Deputy |
An uncontrolled resource consumption vulnerability exists in the Scala-based http4s-blaze-server package of the http4s/blaze library. The vulnerability allows remote, unauthenticated attackers to cause an Out of Memory Error (OOM) and JVM crash by streaming a continuous sequence of small or empty WebSocket continuation frames with the FIN bit set to 0. This bypasses typical payload size checks because of the JVM's per-object allocation overhead, leading to rapid heap exhaustion with minimal network bandwidth.
A critical path traversal vulnerability has been identified in the OpenList Go-based backend package. The vulnerability exists within the batch rename handler because the application does not validate the source filename parameter before constructing filesystems paths. This omission allows authenticated users to escape their designated directory and rename files in sibling paths.
OpenList version 4.2.3 and prior is vulnerable to an authorization bypass and metadata leakage. When configured with the Bleve search engine backend, OpenList fails to perform separator-aware path matching when validating tenant containment. This allows authenticated users to access sibling directories sharing similar name prefixes. Furthermore, the search backend returns unfiltered global result counts, leaking existence verification data of unauthorized files via side-channel analysis.
An authorization bypass vulnerability in OpenList version 4.2.3 and below allows authenticated users to read arbitrary files outside of their designated base directories due to an insecure path prefix check using Go's standard strings.HasPrefix function.
A security policy bypass vulnerability exists in the AWS API MCP Server (awslabs-aws-api-mcp-server) from version 0.2.13 through 1.3.46. When the server fails to load the read-only operations index during startup (due to transient network failures, file permission issues, or other exceptions), it logs a warning but continues running in an insecure, degraded state. Under this condition, the security policy engine fails open, silently skipping all subsequent security checks and consent prompts for the lifetime of the process. This permits unauthorized mutating AWS CLI commands to execute via indirect prompt injection attacks.
An incomplete escaping vulnerability in the npm package 'shescape' allows unauthenticated users to trigger dynamic shell expansions, absolute path disclosure, and command block break-outs on Unix and Windows systems.