
CVE-2026-3805: استخدام بعد التحرير في إعادة استخدام اتصال SMB في curl - كشف معلومات الكومة
اكتشفت ثغرة استخدام الذاكرة بعد تحريرها (use-after-free) في معالج بروتوكول SMB في libcurl. عندما يعيد نقل SMB ثانٍ استخدام اتصال قائم إلى نفس الخادم، يكون مسار الملف للطلب الجديد (req->path) مؤشرًا معلّقًا إلى ذاكرة كومة محرَّرة. تُقرأ هذه الذاكرة عبر strlen() وتُنسخ في حزمة SMB صادرة، مما يسرّب محتويات الكومة إلى الخادم أو يتسبب في تعطّل البرنامج.
الإصلاح في سطر واحد: اجعل req->path يمتلك نسخته الخاصة بدلاً من الاقتراض من smbc->share الخاص بالإبرة.
| CVE | CVE-2026-3805 |
| فئة الثغرة | استخدام الذاكرة بعد تحريرها (CWE-416) |
| السبب الجذري | req->path يشير إلى smbc->share الخاص بالإبرة، ويُحرَّر عند إعادة استخدام الاتصال |
| أُدخلت | 777c5209df (2025-04-30) - curl 8.13.0 |
| أُصلحت | e090be9f73a7a71459ef678c - curl 8.19.0 (11 مارس 2026) |
| الإصدارات المتأثرة | curl 8.13.0 حتى 8.18.0 |
| الأثر | كشف معلومات الكومة إلى الخادم، وتعطّل البرنامج |
| الخطورة | 7.5 - عالية - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H |
تعمل smb_setup_connection() على اتصال "إبرة" مؤقت يُستخدم للبحث في ذاكرة التخزين المؤقت للاتصالات. يضبط req->path ليشير داخل smbc->share (ذاكرة كومة تملكها الإبرة). عندما تعثر ذاكرة التخزين المؤقت للاتصالات على اتصال قابل لإعادة الاستخدام، تُدمَّر الإبرة - مما يحرر smbc->share - لكن req->path يبقى على مقبض easy، وأصبح الآن معلّقًا.
عندما يُنشئ smb_send_open() طلب SMB OPEN، يستدعي strlen(req->path) وينسخ النتيجة في الحزمة الصادرة. أي بيانات موجودة في تلك المنطقة المحرَّرة من الكومة تذهب إلى الخادم. إذا كان المهاجم يتحكم في الخادم (SSRF، أو مستخدم يتصل بخادم SMB خبيث)، فإنه يستلم محتويات الكومة المسرَّبة كـ "اسم ملف" في طلب SMB NT_CREATE_ANDX.
يعيد libcurl استخدام الاتصالات بنشاط لتحسين الأداء. عندما تُرسل طلبات متعددة إلى نفس المضيف، يتحقق curl مما إذا كان يمكن إعادة استخدام اتصال قائم بدلاً من إنشاء اتصال جديد. تعمل الآلية كما يلي:
يخزّن معالج بروتوكول SMB الحالة في مكانين:
smbc (حالة الاتصال) على meta_hash الخاص بالاتصالreq (حالة الطلب) على meta الخاص بمقبض easyالخلل: يُضبط req->path ليشير داخل smbc->share أثناء إعداد الإبرة. عندما تُدمَّر الإبرة عند إعادة الاستخدام، يُحرَّر smbc->share، لكن req->path ما زال يشير إليه.
في smb_parse_url_path()، يحلل الكود مسار URL الخاص بـ SMB ويقسمه إلى اسم مشاركة ومسار ملف:
// lib/smb.c, smb_parse_url_path() line 431:
smbc->share = curlx_strdup((*path == '/' || *path == '\\') ? path + 1 : path);
// ...
*slash++ = 0;
req->path = slash; // <--- points into smbc->share on the NEEDLE
بالنسبة إلى URL smb://server/share1/file1.txt، يُنشئ ذلك:
smbc->share = "share1\0file1.txt" (مُخصَّص في الكومة، تملكه الإبرة)req->path = مؤشر إلى "file1.txt" (داخل smbc->share)عند تفعيل إعادة استخدام الاتصال:
// lib/url.c, url_find_or_create_conn() line 3619:
out:
if(needle)
Curl_conn_free(data, needle); // Destroys needle -> frees smbc->share
يستدعي Curl_conn_free() الدالة Curl_hash_destroy(&conn->meta_hash) التي تستدعي smb_conn_dtor()، مما يحرر smbc->share. لكن req->path (على مقبض easy، الذي يبقى على قيد الحياة) ما زال يشير إلى الذاكرة المحرَّرة الآن.
عند متابعة تنفيذ طلب SMB:
// lib/smb.c, smb_send_open() line 750-769:
const size_t byte_count = strlen(req->path) + 1; // UAF READ
// ...
curlx_strcopy(msg.bytes, sizeof(msg.bytes), req->path, byte_count - 1); // UAF READ
قد تكون ذاكرة الكومة المحرَّرة أُعيد تخصيصها وتحتوي على بيانات حساسة. يمسح strlen(req->path) للأمام حتى يصل إلى بايت صفري (null byte)، وتنسخ curlx_strcopy() ذلك المحتوى في حزمة SMB المُرسَلة إلى الخادم.
سيناريو الهجوم: SSRF حيث يتحكم المهاجم في خادم SMB. يُرسل تطبيق الضحية طلبين SMB إلى خادم المهاجم. يسرّب الطلب الثاني محتويات الكومة كـ "اسم ملف" في طلب SMB OPEN.
إذا أُعيدت الذاكرة المحرَّرة إلى نظام التشغيل (غير مُعيّنة/unmapped)، يؤدي strlen() إلى حدوث SIGSEGV أو انتهاك وصول.
حتى بدون ثغرة الاستخدام بعد التحرير، فإن إعادة استخدام اتصال SMB معطّلة دلاليًا. تستخدم smb_send_tree_connect() قيمة smbc->share من الاتصال المُعاد استخدامه (المشاركة القديمة)، وليس مشاركة الطلب الجديد. يذهب TREE_CONNECT إلى المشاركة الخاطئة تمامًا.
# Two SMB URLs to the same server, different shares/files:
curl smb://192.168.1.100/share1/file1.txt -o /dev/null \
smb://192.168.1.100/share2/file2.txt -o /dev/null
CURLM *multi = curl_multi_init();
CURL *e1 = curl_easy_init();
curl_easy_setopt(e1, CURLOPT_URL, "smb://server/share1/file1");
curl_multi_add_handle(multi, e1);
CURL *e2 = curl_easy_init();
curl_easy_setopt(e2, CURLOPT_URL, "smb://server/share2/file2");
curl_multi_add_handle(multi, e2);
// When e2 runs after e1 completes and reuses the connection: UAF
ابنِ curl مع -fsanitize=address وشغّل ما سبق:
==PID==ERROR: AddressSanitizer: heap-use-after-free on address 0x...
READ of size 1 at 0x... thread T0
#0 strlen
#1 smb_send_open lib/smb.c:750
#2 smb_request_state lib/smb.c:1163
...
freed by thread T0 here:
#0 free
#1 smb_conn_dtor lib/smb.c:388
#2 Curl_hash_destroy
#3 Curl_conn_free lib/url.c:557
انظر poc/REPRODUCE_UAF.sh للحصول على سكربت إعادة الإنتاج الكامل.
المشكلة الأساسية هي أن req->path يستعير مؤشرًا إلى ذاكرة يملكها smbc الخاص بالإبرة. يجعل الإصلاح req->path يمتلك نسخته الخاصة:
--- a/lib/smb.c
+++ b/lib/smb.c
@@ -378,7 +378,7 @@ static void smb_easy_dtor(void *key, size_t klen, void *entry)
(void)key;
(void)klen;
+ curlx_free(req->path);
curlx_free(req);
}
@@ -428,7 +428,10 @@ static CURLcode smb_parse_url_path(struct Curl_easy *data,
/* Parse the path for the file path converting any forward slashes into
backslashes */
*slash++ = 0;
- req->path = slash;
+ req->path = curlx_strdup(slash);
+ if(!req->path) {
+ Curl_safefree(smbc->share);
+ return CURLE_OUT_OF_MEMORY;
+ }
نفّذ Stefan Eissing الإصلاح الرسمي في e090be9f73a7a71459ef678c.
كان المطورون على علم جزئي بمخاطر المؤشر المعلّق. يوجد هذا التعليق في smb_easy_dtor():
/* `req->path` points to somewhere in `struct smb_conn` which is
* kept at the connection meta. If the connection is destroyed first,
* req->path points to free'd memory. */
لكن هذا يأخذ في الاعتبار فقط السيناريو الذي يُدمَّر فيه الاتصال قبل مقبض easy. وقد فاته سيناريو تدمير الإبرة أثناء إعادة استخدام الاتصال، وهو المُحفِّز الفعلي.
| التاريخ | الحدث |
|---|
!defined(CURL_DISABLE_SMB)defined(USE_CURL_NTLM_CORE)sizeof(curl_off_t) > 4 (off_t 64-بت، وهو المعيار على معظم المنصات)يكون SMB مفعّلاً افتراضيًا في إصدارات curl التي تدعم NTLM.
e090be9f73a7a71459ef678c777c5209dfCVE-2026-3805 - أُصلحت في curl 8.19.0. الإصدارات المتأثرة: 8.13.0 حتى 8.18.0.
Daniel Wade - GitHub - [email protected]
| 2025-04-30 | 777c5209df يعيد هيكلة كود SMB لاستخدام meta hash، مما أدخل الخلل |
| 2026-03-07 | اكتشفت الخلل أثناء مراجعة أمنية |
| 2026-03-08 | أُبلغ curl عبر HackerOne (#3591944) |
| 2026-03-08 | تواصل curl مع distros@openwall |
| 2026-03-11 | صدر curl 8.19.0 مع الإصلاح، ونُشر CVE-2026-3805 |