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.
Affected
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_changesor/sync/deleteswith unusualsyncInfospayloads (e.g., non-numeric strings, SQL keywords likeUNION,SELECT,OR, semicolons in numeric fields) - Database query logs showing syntax errors or unexpected
WHEREclauses (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|syncUntilregex 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
- 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 ingetSyncRows()andgetDelRows()with parameterized placeholders. - Input validation: Enforce strict type checking on
maxLoadedId,syncFrom, andsyncUntilusingNumber.isInteger(),Number.isFinite(), or schema validation libraries (e.g., Joi, Zod) before query construction. - 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. - 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.
Sources