
SolarWinds Serv-U CVE-2026-28318: انهيار غير مصادق عليه لـ Content-Encoding: deflate. تحليل السبب الجذري (تحرير غير صحيح لمؤشر داخلي -> تلف الكومة) + إثبات مفهوم (PoC) يقتصر على رفض الخدمة (DoS). تم الإصلاح في 15.5.4 Hotfix 1.
تحليل السبب الجذري + إثبات المفهوم لتعطيل الخدمة. التقرير العام يصنف هذا على أنه تعطيل للخدمة / استهلاك غير مسيطر عليه للموارد قبل المصادقة. يُظهر التحليل الثنائي أن العيب الأساسي هو سلامة الذاكرة: استدعاء
free()غير صحيح لمؤشر داخلي في مسار فك تشفيرdeflateالخاص بـ HTTP، مما يفسد كومة العملية (STATUS_HEAP_CORRUPTION,0xC0000374). إنه ليس قنبلة فك ضغط، والتعطل مستقل عن حجم البيانات المفكوكة.
يؤثر فقط على مستمع HTTP/HTTPS (مسار الويب / الإدارة). بروتوكولات FTP/FTPS/SFTP ليست على هذا المسار البرمجي.
أي طلب HTTP يحمل Content-Encoding: deflate مع جسم deflate صالح يُعطِّل الخدمة. يقوم المعالج بفك ضغط الجسم، ثم يحرر المؤشر إلى الجسم المضغوط ويستبدله بالمخزن المؤقت المفكوك — لكن هذا المؤشر هو مؤشر داخلي في مخزن استقبال HTTP (يشير إلى الجسم، مباشرة بعد \r\n\r\n)، ليس أساس تخصيص كومة. استدعاء free() على مؤشر غير أساسي وغير محاذي يُفسد الكومة وتنتهي العملية.
نظرًا لأن الخلل يكمن في كيفية تحرير مؤشر الجسم — وليس في كمية البيانات المنتجة — حتى جسم مضغوط بحوالي 25 بايت (يفك ضغطه إلى بضعة كيلوبايتات) يُعطِّله بشكل موثوق. لا يزيد استخدام الذاكرة؛ إنها ليست "قنبلة zip/deflate".
توجد روتين فك تشفير deflate في RhinoNET.dll (مكتبة الشبكة لـ Serv-U) ويُصل إليه من مسار استقبال HTTP (ProcessReceive) بمجرد أن يضبط محلل الرأس علامة "deflate" عند رؤية Content-Encoding: deflate.
بشكل مبسط، يُستدعى الروتين كـ decode(this, &bodyPtr, &bodyLen) ويقوم بما يلي:
1. فك ضغط bodyPtr[0..bodyLen] -> ينمو مخزن مؤقت تراكمي `acc` (zlib قياسي، محدود، صحيح)
2. free(*bodyPtr) <-- *bodyPtr هو مؤشر داخلي في مخزن استقبال HTTP
3. *bodyPtr = acc // استبدال الجسم المضغوط بالمخزن المؤقت المفكوك
4. *bodyLen = total
الخطوة 2 هي الخلل. *bodyPtr ليس كتلة مخصصة بشكل مستقل: إنه يشير إلى جسم الطلب داخل مخزن استقبال HTTP الواحد، أي receive_buffer + header_length. تحرير مؤشر داخلي (وغير محاذي لـ 16 بايت) يجعل المُخَصِّص يُفسر منطقة متأثرة من قبل المهاجم كرأس كتلة كومة → تلف بيانات وصفية للكومة → 0xC0000374.
كلتا الفرضيتين "الواضحتين" خاطئتان، تم التحقق منهما وقت التشغيل:
zlib1.dll!inflate القياسي ويحترم avail_out في كل استدعاء (لوحظ عبر أكثر من 100 شريحة؛ صفر كتابات خارج الحدود).prev_total + produced + 1 كل جولة ويُكتب بهذا المقدار بالضبط — ضيق، لا تجاوز.التلف هو حصرًا بسبب free() غير الصحيح في الخطوة 2.
تم التقاطها وقت التشغيل (Frida) ضد Serv-U 15.5.4.108 أثناء إرسال طلب Content-Encoding: deflate الذي يفك ضغط جسمه إلى "A" * 8192:
…ce — mod 16 == 14. قواعد تخصيص الكومة محاذاة لـ 16 بايت، لذا هذا ليس قاعدة تخصيص؛ إنه مؤشر داخلي.\r\n\r\n:
…tream\r\nContent-Length: 26\r\nConnection: close\r\n\r\n
ed c1 01 0d 00 …).ntdll.dll، الاستثناء 0xC0000374 (STATUS_HEAP_CORRUPTION)، مع نشأة الاستدعاء من free في معالج deflate في RhinoNET.dll. آخر عملية كومة قبل التعطل هي بالضبط هذا free(bodyPtr).free غير الصحيح بسرعة (0xC0000374) في اختباراتنا، ولم نحقق تنفيذ كود. عالج هذا على أنه تعطل DoS مع تلف كومة قبل المصادقة وسبب جذري في سلامة الذاكرة؛ لا تفترض تنفيذ كود عن بُعد.يتطلب Python 3 (المكتبة القياسية فقط). وجهه إلى مستمع HTTP لـ Serv-U مخوَّل باختباره. على Windows، يستخدم استدعاء واحد Get-NetTCPConnection لقراءة PID المستمع وسجل أحداث التطبيق لتأكيد تعطل تلف الكومة.
python poc_verify.py # افتراضي 127.0.0.1:80، حزمة صغيرة، مرة واحدة
python poc_verify.py --host <ip> --port 80
python poc_verify.py --big # الجسم يفك ضغطه إلى 8192 بايت
python poc_verify.py --shots 3
python poc_verify.py --no-events # تخطي فحص سجل الأحداث (لا يحتاج صلاحيات)
يبني إثبات المفهوم طلبًا أدنى:
POST / HTTP/1.1
Host: <target>
Content-Encoding: deflate
Content-Type: application/octet-stream
Content-Length: <n>
Connection: close
<جسم deflate خام — حتى بضع عشرات من البايتات كافية>
يُبلغ عن PASS إذا تعطلت الخدمة (تغير PID المستمع / ظهر حدث 0xC0000374) وFAIL إذا بقيت (تم التصحيح بالفعل، غير متأثرة، أو لم يُعالج الجسم كـ deflate).
Content-Encoding الوارد على مستمع HTTP/HTTPS، مثلاً على وكيل عكسي:
if ($http_content_encoding) { return 400; }
RhinoNET.dll / Serv-U.exe (قاعدة الصورة 0x180000000) لتعيين مسار الاستقبال → تحليل الرأس → فك تشفير deflate وتحديد موقع free الخاطئ.zlib يحترم avail_out، تتبع كل alloc/free/memcpy لاستدعاء فك التشفير على القرص، ومعالج استثناء عملية لالتقاط التعليمات المُخطئة ومكدس 0xC0000374 عند لحظة التلف. ثم تم تأكيد تحرير المؤشر الداخلي بتفريغ البايتات حول المؤشر المحرر (ذيل رأس HTTP + جسم deflate خام، غير محاذٍ لـ 16 بايت).الإزاحات المشار إليها في التحليل خاصة بالبناء 15.5.4.108 وستختلف عبر الإصدارات.
نُشر كبحث دفاعي بعد توفر التصحيح من البائع. إثبات المفهوم هو لتعطيل الخدمة فقط ومخصص للاختبار المصرح به.
| المنتج | SolarWinds Serv-U (FTP / MFT / File Server) |
| الإصدارات المتأثرة | 15.5.4 والإصدارات السابقة في هذا الفرع بدون Hotfix 1 (التحليل تم على البناء 15.5.4.108) |
| المُصحَّح | Serv-U 15.5.4 Hotfix 1 (صدر في 2026-06-04) |
| ناقل الهجوم | غير مصادق، شبكة (منفذ HTTP/HTTPS للإدارة / الويب) |
| التصنيف العام | CWE-400 استهلاك غير مسيطر عليه للموارد · DoS · CVSS 7.5 · CISA KEV |
| التصنيف الفعلي | CWE-763 تحرير مؤشر غير صالح / CWE-590 تحرير ذاكرة غير موجودة على الكومة → تلف الكومة |