Intelligence
criticalVulnerabilityActive

Saltcorn SQL Injection in Mobile Sync Endpoints — Template Literal Interpolation of User-Controlled Values

Saltcorn's mobile sync routes embed unauthenticated user-supplied numeric values directly into SQL template literals without parameterization. Any authenticated user with table read access can execute arbitrary SQL, including data exfiltration and (on PostgreSQL) DDEs.

S
Sebastion

Affected

saltcorn/saltcorn

Vulnerability Description

This is a template-literal SQL injection vulnerability in Saltcorn's packages/server/routes/sync.js. The getSyncRows() and getDelRows() functions construct SQL queries using JavaScript template literals, directly interpolating the maxLoadedId, syncFrom, and syncUntil values from req.body.syncInfos without any type coercion, parameterization, or value-level sanitization. The code uses db.sqlsanitize() to escape identifiers (table/column names via double-quote escaping), but this function is not applied to numeric values and provides no protection against injection. The root cause is confusion between identifier escaping and value parameterization — the two are distinct mechanisms, and the former provides no defense against the latter.

Proof-of-Concept Significance

The PoC demonstrates that any authenticated user with the default "user" role (role_id ≥ 80) and read access to at least one table can inject arbitrary SQL. Preconditions are minimal: the attacker must have a valid user account and permission to sync a single table. The vulnerability is highly reliable because it allows direct SQL syntax injection into WHERE clauses and can chain with SQL comments (e.g., --, /**/) to bypass subsequent query logic. On SQLite-backed instances, write operations are blocked by permissions; on PostgreSQL, DDL and DML are typically executable.

Detection Guidance

Log Indicators:

  • POST requests to /sync/load_changes or /sync/deletes with unusual syncInfos payloads (e.g., non-numeric strings, SQL keywords like UNION, SELECT, OR, semicolons in numeric fields)
  • Database query logs showing syntax errors or unexpected WHERE clauses (e.g., > 1 OR 1=1 --)
  • Elevated error rates in sync endpoints correlated with multi-table enumeration or large result sets

Signatures:

  • Monitor request bodies for regex patterns: maxLoadedId|syncFrom|syncUntil regex containing [^0-9.-] outside expected formats, or common injection markers: UNION, SELECT, --, /*, xp_, sp_
  • Alert on successful sync requests followed by unusual database metadata queries (information_schema access)

Mitigation Steps

  1. Immediate patch: Apply parameterized queries using prepared statements or parameterized binding (e.g., Node.js db.query(sql, [values]) patterns). Replace all template-literal interpolations in getSyncRows() and getDelRows() with parameterized placeholders.
  2. Input validation: Enforce strict type checking on maxLoadedId, syncFrom, and syncUntil using Number.isInteger(), Number.isFinite(), or schema validation libraries (e.g., Joi, Zod) before query construction.
  3. Workaround (temporary): Disable mobile sync endpoints (/sync/load_changes, /sync/deletes) via reverse proxy or application firewall rules until patched, if mobile functionality is non-critical.
  4. Audit: Review all SQL construction in sync.js, routes/, and query builders for similar template-literal patterns and apply parameterization uniformly.

Risk Assessment

Likelihood of exploitation: High. The attack requires only valid credentials and read access to one table — both common in multi-tenant or team-based Saltcorn deployments. Mobile sync is a core feature, increasing exposure. Threat actor interest: Very high — SQL injection provides direct database access, credential theft (admin hashes), and reconnaissance for lateral movement. The vulnerability is reliable, low-profile (logs appear as sync traffic), and bypasses application-level access controls. Organizations using Saltcorn should assume compromise if this endpoint is exposed and patched versions are not deployed immediately.