FacturaScripts REST API SQL Injection via Parenthesis Bypass in Where::sqlColumn
Authenticated SQL injection in FacturaScripts REST API filter parameter allows scoped API keys with minimal privileges to extract sensitive data (password hashes, session cookies) and achieve account takeover. The vulnerability bypasses identifier escaping through parenthesis-containing field names.
CVE References
Affected
Vulnerability Description
The vulnerability exists in Core/Where.php::sqlColumn(), which implements a flawed escaping exemption: field names containing both ( and ) characters bypass identifier escaping entirely. This was apparently intended as a optimization or feature for parenthesized expressions, but creates a critical injection point. The two REST API filter builders (APIModel::getWhereValues and ApiAttachedFiles::getWhereValues) pass raw $_GET['filter'] keys directly into DataBaseWhere constructors without sanitization. When the ORM's all() method processes these through the query builder chain (Where::multiSqlLegacy → Where::sql() → Where::sqlColumn()), attacker-controlled strings are concatenated unescaped into SQL WHERE clauses.
Proof-of-Concept Significance
The verified PoC (2026-04-30) demonstrates end-to-end account takeover with severe real-world impact: a read-only API key (fullaccess=0, allowget=1 on clientes only) extracted the admin user's bcrypt password hash and logkey session cookie. This proves the vulnerability is:
- Reliable: Stock FacturaScripts instance responds with sensitive data
- Minimal preconditions: Requires only any valid API key with ANY resource read permission; no CSRF, rate-limiting, or second factor blocks exploitation
- High-fidelity proof: Leaked credentials enabled immediate UI session hijacking
- Horizontally exploitable: Any organization using FacturaScripts with API integrations has exposure
Detection Guidance
Web Access Logs: Search for GET /api/3/ requests where the filter parameter contains parentheses, UNION keywords, or SELECT statements. Patterns:
filter[(]*UNION|filter[)]*SELECT|filter[.*]--|filter[.*]UNION.*FROM
Database Query Logs: Look for unexpected UNION SELECT or WHERE clauses containing user data tables (users, admins) when filtering on client resources (clientes).
Application Logs: Enable FacturaScripts debug logging to capture raw SQL queries; filter for anomalies in Where::sql() output.
Network IDS/IPS: Create signatures for HTTP requests with filter[ parameters containing balanced parentheses or SQL keywords; correlate with API authentication events.
Mitigation Steps
- Immediate patch: Update FacturaScripts to post-2026-05 releases that fix
Where::sqlColumn()to reject field names containing parentheses or apply proper parameterized query binding - Input validation: In
APIModel::getWhereValues()andApiAttachedFiles::getWhereValues(), validate filter keys against a whitelist of allowed field names; reject keys containing parentheses, quotes, or SQL keywords - Prepared statements: Refactor the query builder to use parameterized queries (
?placeholders) instead of string concatenation - API token scope reduction: Audit existing API keys; revoke overprivileged tokens; implement least-privilege access (single-resource keys only)
- Session hardening: Implement session binding (tie
logkeyto IP/User-Agent); reduce session lifetime; add CSRF tokens to state-changing operations - WAF rule: Deploy a ModSecurity/WAF rule blocking
/api/3/requests with parenthesized filter parameters
Risk Assessment
Likelihood of exploitation: HIGH. The vulnerability requires only a valid API token (commonly issued to integrations, partners, or CI/CD pipelines) and a single HTTP GET request. Many deployments have such keys in production.
Threat actor interest: CRITICAL. This enables lateral movement from low-privilege integrations to full admin compromise without detection—ideal for supply-chain attacks, APT persistence, or ransomware preparation.
Prevalence: FacturaScripts is widely used in Spanish-speaking SMB/enterprise environments; exposure is likely widespread until patches are broadly deployed.
Sources