SurrealDB RPC Bincode Subquery Injection
CVE-2024-58362 is a critical deserialization flaw in SurrealDB's RPC API allowing unauthenticated attackers to inject and execute arbitrary subqueries via bincode.
Overview
CVE-2024-58362 is a pre-authentication subquery injection vulnerability in SurrealDB’s RPC API, affecting all releases prior to 1.5.5 and all 2.0.0-beta releases prior to 2.0.0-beta.3. The flaw resides in the signin and signup RPC operations, which accept credential objects from untrusted callers. When those objects are transmitted in bincode serialization format, the engine deserializes them without recursively validating that every nested field represents a literal, non-computed value. An attacker can embed a fully formed SurrealQL subquery inside a bincode-encoded binary object and supply it in place of a legitimate credential field.
The injected subquery is then evaluated within the context of the database owner’s SIGNIN or SIGNUP query, executing under a system-level session with the editor role. This means an unauthenticated remote attacker can read, write, update, and delete arbitrary non-IAM records across the database without possessing any valid credentials. The attacker cannot directly observe query results (the response channel does not echo them back) and cannot modify IAM resources, which require the elevated owner role — but these limitations do little to reduce the practical severity of unauthorized data manipulation at scale.
Any SurrealDB deployment that exposes the RPC API to untrusted network clients and defines at least one record access method with a SIGNIN or SIGNUP clause is affected. This includes cloud-hosted instances, multi-tenant SaaS backends, and self-hosted installations that have not yet been upgraded.
Technical Analysis
The root cause is an incomplete trust boundary during deserialization of RPC credential payloads. SurrealDB’s RPC layer supports multiple wire formats, including JSON and bincode. When the server processes a signin or signup call, it deserializes the incoming payload into a generic Object type that maps string keys to SurrealQL Value variants. The validation logic checks that the top-level fields of this object are non-computed — that is, plain literals — but it does not recurse into nested structures. A malicious actor can therefore place a Value::Subquery variant inside a nested object key, and that variant will survive deserialization and reach the query engine intact.
The following pseudocode illustrates the vulnerable pattern as it existed in the affected versions:
// VULNERABLE: shallow validation only checks top-level fields
async fn process_signin(rpc: &Rpc, params: Array) -> Result<Value, RpcError> {
let credentials: Object = params
.needs_one()?
.try_into()?; // deserializes bincode payload into Value::Object
// Only iterates over top-level entries — nested Values are unchecked
for (key, val) in credentials.iter() {
if val.is_computed() {
return Err(RpcError::InvalidParams);
}
// Nested objects inside `val` are never inspected
}
// credentials is now passed directly into the SIGNIN query template
// where a nested subquery value will be evaluated by the query engine
let result = rpc
.kvs
.signin(rpc.session.clone(), credentials)
.await?;
Ok(result)
}
When the bincode-encoded payload contains something structurally equivalent to:
{
"user": "alice",
"pass": {
"__surreal_subquery__": "SELECT * FROM sensitive_table"
}
}
…the nested Value::Subquery is carried through undetected. During query execution, the SIGNIN template interpolates the credential object fields into a SurrealQL expression. Because the subquery variant is a first-class Value in the AST, the query planner evaluates it in the current session context — a system user session bearing the editor role — rather than rejecting it as untrusted input. The result is blind subquery injection: the subquery runs, its side effects (writes, deletes) take effect, and the attacker infers success or failure through timing or downstream observation rather than direct output.
The critical enabler here is the bincode format itself. JSON-based callers cannot easily smuggle a Value::Subquery because the JSON deserializer maps all scalar strings to Value::Strand, not to query AST nodes. Bincode, however, serializes Rust enum variants by their numeric discriminant, so a crafted byte sequence can directly encode the Subquery discriminant and its payload, bypassing any string-level sanitization.
Impact
An unauthenticated attacker with network access to the SurrealDB RPC endpoint can:
- Exfiltrate data indirectly by copying sensitive records into attacker-controlled tables or modifying records to include beacon values observable through legitimate application responses.
- Destroy or corrupt data by executing
DELETEorUPDATEstatements across any non-IAM namespace, database, or table. - Escalate persistence by creating rogue records, forged user entries, or poisoned lookup tables that influence application logic.
- Deny service to legitimate users by bulk-deleting records or corrupting indexes.
What the attacker cannot do is retrieve raw query output directly through the RPC response, nor can they touch IAM resources (users, roles, access methods) that require the owner role. These constraints partially limit the blast radius but do not prevent devastating data loss or integrity violations in production workloads.
The CVSS 8.8 score reflects network-accessible attack surface (AV:N), low attack complexity (AC:L), no required privileges (PR:N), no user interaction (UI:N), and high impacts across integrity and availability (I:H, A:H) with partial confidentiality impact (C:L) given the blind nature of exfiltration.
How to Fix It
Upgrade immediately. The authoritative fix is to update to SurrealDB 1.5.5 or later (stable channel) or 2.0.0-beta.3 or later (beta channel). The patch introduces recursive validation of all Value variants within credential objects, ensuring that subqueries, futures, expressions, and other non-literal types are rejected at the RPC boundary regardless of nesting depth.
# Cargo-based projects
cargo update -p surrealdb --precise 1.5.5
# Docker
docker pull surrealdb/surrealdb:1.5.5
# Homebrew (macOS)
brew upgrade surrealdb
The corrected validation logic recurses into every Value variant before the credential object is passed to the query engine:
// FIXED: recursive validation rejects non-literal values at any depth
fn assert_no_computed_values(val: &Value) -> Result<(), RpcError> {
match val {
Value::Object(obj) => {
for (_, v) in obj.iter() {
assert_no_computed_values(v)?; // recurse into nested objects
}
}
Value::Array(arr) => {
for v in arr.iter() {
assert_no_computed_values(v)?;
}
}
v if v.is_computed() => {
return Err(RpcError::InvalidParams);
}
_ => {}
}
Ok(())
}
async fn process_signin(rpc: &Rpc, params: Array) -> Result<Value, RpcError> {
let credentials: Object = params.needs_one()?.try_into()?;
for (_, val) in credentials.iter() {
assert_no_computed_values(val)?; // now validates the full value tree
}
let result = rpc.kvs.signin(rpc.session.clone(), credentials).await?;
Ok(result)
}
Additionally, operators who cannot upgrade immediately should restrict network access to the RPC endpoint to trusted clients only, and should disable bincode as an accepted wire format at the reverse proxy or firewall layer until the patch is applied.
Our Take
This vulnerability is a textbook illustration of what happens when a trust boundary is defined at the wrong layer. The SurrealDB team correctly identified that credential fields must be literals, but the validation was implemented shallowly — checking the envelope without inspecting the contents. This pattern recurs across almost every serialization-heavy system we audit: teams write validation for the shape of data they expect and forget to account for the full expressiveness of the deserialization target type.
The bincode angle makes this particularly instructive. Binary serialization formats that directly encode type discriminants are substantially more dangerous than text-based formats when paired with rich internal type systems. A Value::Subquery cannot be smuggled through a JSON endpoint without custom parser hooks, but bincode makes it trivially encodable. Developers choosing binary wire formats for performance must understand that they are also expanding the attack surface relative to more restrictive text formats.
For enterprises running multi-tenant applications on SurrealDB, this is a reminder that pre-authentication endpoints are the highest-risk surface area in any API. Unauthenticated callers should receive the minimum possible expressive power, and any data they supply should be validated at every structural level before it touches a query engine.
Detection with SAST
This vulnerability class maps to CWE-20 (Improper Input Validation) and more specifically to CWE-1287 (Improper Validation of Specified Type of Input), as the validation fails to account for all valid inhabitants of the input type.
Offensive360’s SAST engine detects this pattern by analyzing data flow from deserialization entry points (functions that produce Value, Object, or equivalent generic AST types from untrusted byte streams) to query execution sinks. Key rules that fire on this pattern include:
- Shallow struct validation: detecting
for (k, v) in obj.iter()guards that do not recurse into nestedObjectorArrayvariants before the object reaches a query-building function. - Enum variant reachability: in Rust codebases, tracing whether
Value::Subquery,Value::Future, orValue::Expressionvariants are reachable at a sensitive sink after a validation gate — even if the gate handles other variants correctly. - Binary deserialization to query sink: flagging any path where bincode, MessagePack, or CBOR deserialization output flows into a templated query without a whitelist-based type assertion.
Teams performing DAST against SurrealDB deployments should probe signin and signup RPC calls with bincode-encoded payloads that include nested structured values and monitor for anomalous database state changes as an indicator of successful injection.
References
Detect this vulnerability class in your codebase
Offensive360 SAST scans your source code for CVE-2024-58362-class vulnerabilities and thousands of other patterns — across 60+ languages.