Feb 14, 2026·6 min read·27 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 |