SurrealDB, Information Disclosure via COUNT Side-Channel, GHSA-c8jx-96c9-8xrp (Medium) -DC-Jul2026-795

Listen to this Post

How the Vulnerability Works

A record user with table-level `SELECT` permissions can learn the value of a field that is protected by field-level `SELECT` permissions, provided that field is indexed. When `DEFINE FIELD … PERMISSIONS FOR select WHERE …` hides a field from a caller, and that field is indexed, the query planner uses an indexed‑COUNT shortcut — such as Index::Count, IndexCountScan, or the legacy `Iterate Index Count` / `Iterate Index Keys` paths — that counts matching index entries and skips the permission check that would normally hide the value.

For example, an attacker can run:

SELECT count() FROM t WHERE hidden_field = "guess" GROUP ALL;

This returns a count greater than zero whenever a record actually has that value, even though the caller is never allowed to read the field directly. The same query with `WITH NOINDEX` correctly returns [], confirming the gap. By repeating the count query with different guesses, an attacker can confirm or recover the contents of any restricted field they cannot read through a normal SELECT.
The legacy planner (surrealdb/core/src/idx/planner/tree.rs) and the streaming planner (surrealdb/core/src/exec/planner/select/mod.rs) now both refuse the indexed fast path when the `WHERE` / `ORDER` tree references a field governed by a non‑Full `SELECT` permission. A new `cond_touches_restricted_field` flag is propagated, and `eval_count` refuses a dedicated `Index::Count` when set. The streaming planner adds cond_touches_restricted_select_field, a `RestrictedIdiomChecker` visitor that matches each idiom against the table’s field‑permission prefixes (loaded via the plan‑time transaction), and gates `IndexCountScan` emission on it. The fast paths are preserved for root / owner sessions via should_check_perms_for_view. Versions 3.1.0 and later are not affected.

DailyCVE Form

Platform: SurrealDB
Version: <3.1.0
Vulnerability: Info Disclosure
Severity: Medium
date: 2026-07-01

Prediction: 2026-05-26

What Undercode Say

Bash / SurrealQL commands to test the vulnerability:

Check SurrealDB version
surreal version
Connect to the database and run the exploit query
surreal sql --conn http://localhost:8000 --user root --pass root --ns test --db test
-- Create a table with a hidden indexed field
DEFINE TABLE t SCHEMAFULL;
DEFINE FIELD secret ON t TYPE string;
DEFINE FIELD secret ON t PERMISSIONS FOR select WHERE $auth.id = 'owner';
DEFINE INDEX idx_secret ON t COLUMNS secret;
-- Insert some data
INSERT INTO t (secret) VALUES ('guess1'), ('guess2'), ('guess3');
-- Attacker (non-owner) runs:
SELECT count() FROM t WHERE secret = 'guess1' GROUP ALL;
-- Returns > 0 if 'guess1' exists, leaking the value
-- Confirm the leak is due to the index:
SELECT count() FROM t WHERE secret = 'guess1' GROUP ALL WITH NOINDEX;
-- Returns [] correctly

Code snippet showing the fix (from commit 0c6dd021b):

// surrealdb/core/src/exec/planner/select/mod.rs
fn resolve_indexes(&mut self) {
// Skip any B-tree / unique index whose columns are governed
// by a non-Full SELECT permission.
if self.cond_touches_restricted_select_field {
return; // Do not use the indexed COUNT shortcut
}
// ... otherwise proceed with index selection
}

Exploit

An attacker with table‑level `SELECT` can:

  • Run repeated `SELECT count() FROM t WHERE hidden_field = “guess” GROUP ALL` queries with different guesses.
  • Observe whether the count is greater than zero, confirming the presence of that value.
  • Recover the entire value of any indexed, field‑protected column by brute‑forcing or dictionary attacks.

What the attack cannot do:

  • Read fields that are not indexed (the shortcut only fires when an index covers the predicate column).
  • Cross table, database, or namespace isolation boundaries.
  • Modify data, escalate privileges, or affect availability.

Protection

  • Upgrade to SurrealDB 3.1.0 or later – versions 3.1.0 and later include the fix that gates the indexed‑COUNT fast path behind the proper permission checks.
  • Avoid `DEFINE INDEX` on fields whose values are protected by field‑level `SELECT` permissions – without the index, the leak does not occur.
  • Restrict the ability of record users to issue arbitrary `SELECT count() … GROUP ALL` queries against tables containing field‑protected columns.
  • Use namespace / database isolation as the primary boundary where feasible.

Impact

What an attacker can do:

  • Confirm or recover values of a field protected by field‑level `SELECT` permissions on any table they hold table‑level `SELECT` on, provided the field is indexed.
  • Repeat the query with different guesses to read restricted field contents one value at a time.

What it cannot do:

  • Read fields that are not indexed.
  • Cross table, database, or namespace isolation boundaries.
  • Modify data, escalate privileges, or affect availability.

🎯Let’s Practice Exploiting & Learn Patching For Free:

🎓 Live Courses & Certifications:

Join Undercode Academy for Verified Certifications

🚀 Request a Custom Project:

Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top