
بحث أصلي وإثبات مفهوم (PoC) لثغرة تجاوز سعة المخزن المؤقت على المكدس في كلمة المرور المُفكَّكة بترميز Base64 قبل المصادقة في Netis NC63 login.cgi
login.cgi مما يؤدي إلى تنفيذ تعليمات برمجية عن بُعد (RCE)الباحث: Özcan Ersan (@ozcanpng)
CVE-2026-76070NC63_V3.0.0.3327/bin/netis.cgiPOST /cgi-bin/login.cgipassword المُرمَّز بـ Base64يسترد معالج تسجيل الدخول العام في البرنامج الثابت Netis NC63 V3.0.0.3327 المعامل password الذي يتحكم فيه المهاجم ويفك ترميزه باستخدام روتين Base64 المخصص FUN_00402bd4. يوفّر المستدعي مخزنًا مؤقتًا محليًا على المكدس بحجم 64 بايت لكنه لا يمرّر سعته إلى مفكّك الترميز. يستمد مفكّك الترميز عمله من المدخل المُرمَّز ويكتب البايتات المُفكَّكة دون التحقق من نهاية الوجهة.
يقع عنوان العودة المحفوظ لمعمارية MIPS على بُعد 136 بايت من بداية المخزن المؤقت المُفكَّك. أكّدت الاختبارات الديناميكية على CGI الإنتاجي الأصلي بالتجزئة الأصلية ما يلي:
B مُفكَّك بحجم 140 بايت يُنتج خطأً عند 0x42424242؛ra المحفوظ بـ 0x0041a2e0 يؤدي إلى دخول ثانٍ ملاحَظ عند معالج تسجيل الدخول، مما يثبت التحكم في عدّاد البرنامج؛ وsystem() المباشر في الملف التنفيذي الأصلي بقيمة a0 من معمارية MIPS يختارها المهاجم. سجّل البديل /bin/sh الأمر /bin/sh -c NC63_RCE_PROOF ولم ينفّذ أي أمر.يتوقف PoC العام في هذا المستودع عمدًا عند نمط الانهيار. لا يحتوي على سلسلة عودة، أو shellcode، أو أمر، أو reverse shell، أو استمرارية. سكربت RCE المُدار بشكل منفصل في poc/exploit-db/ مُعدّ لتقديمه إلى Exploit-DB ومخصّص للاختبار المصرّح به للأنظمة المتأثرة فقط.
193f6a5e2ce65972b1805bf076f8d3521379a8441c8aaeb5ad0ba174bbee0792 netis_NC63_V3.0.0.3327.bin
23faa747b7d2f067aa5431bcc227ceca97a7977cf3e7c372f715cbba57f9209b squashfs-root/bin/boa
eb298774c27070dc595fefcabb4e8c12a46cb5f4fd08f91c3ca92282c3a289a2 squashfs-root/bin/netis.cgi
نسخة /bin/netis.cgi التي تم اختبارها ديناميكيًا تحمل نفس SHA-256 الخاص بالملف التنفيذي المستخرج من البائع.

تُرسل الواجهة الأمامية الخاصة بالبائع كلمة المرور إلى نقطة النهاية العامة بترميز Base64:
obj.password = base64encode(utf16to8(password));
request({
url: "/cgi-bin/login.cgi",
data: obj
});
يستخدم حقل HTML السمة maxlength="63"، لكن هذا مجرد قيد من جهة المتصفح. يمكن لعميل HTTP مباشر إرسال قيمة مُرمَّزة أكبر.

من الضروري أن يكون login.cgi قابلاً للوصول قبل المصادقة. يحدث فك الترميز غير الآمن قبل مقارنة كلمة المرور المفكوكة بكلمة مرور المسؤول المُهيأة. لا يُشترط وجود جلسة صالحة، أو ترويسة Cookie، أو ترويسة Authorization، أو كلمة مرور صحيحة.
Unauthenticated HTTP client
|
| POST /cgi-bin/login.cgi
| password=<attacker-controlled Base64>
v
/bin/netis.cgi: FUN_0041a2e0
|
| get_request_param("password")
v
FUN_00402bd4(decoded_stack_buffer, encoded_password)
|
| no destination-capacity argument
| decoded output exceeds 64 bytes
v
saved s8 at decoded offset 132
saved ra at decoded offset 136
|
v
attacker-selected MIPS PC
شيفرة زائفة مستمدة من Ghidra، مع تطبيع الأسماء لتحسين القراءة:
int login_cgi(void *request)
{
char decoded[64];
char stored[68];
char *password;
memset(decoded, 0, 64);
memset(stored, 0, 64);
password = get_request_param(request, "password");
if (password != NULL)
FUN_00402bd4(decoded, password); /* no capacity argument */
apmib_get(0x15e, stored);
if (strcmp(decoded, stored) == 0)
printf("[\"SUCCESS\"]");
else {
system("echo 0 >/tmp/boa_auth");
printf("[\"%d\"]", 0x15);
}
return 0;
}

لا يستقبل المفكّك عند FUN_00402bd4 سوى مؤشرات الوجهة والمصدر.
تُقدّم حلقة التكرار مؤشر الوجهة وتخزّن حتى ثلاثة بايتات مفكوكة
مقابل كل أربعة رموز Base64. لا يوجد أي مقارنة تتحقق من الوجهة مقابل
decoded + 64.

إن Base64 هو تحويل المدخلات، وليس العيب الأساسي. السبب الجذري هو عدم التطابق بين الطول المفكوك الذي يتحكم فيه المهاجم ووجهة ثابتة الحجم لا تُفرض سعتها أبدًا. بالنسبة للمدخلات العادية المبطّنة، تمثّل أربعة أحرف مشفّرة حتى ثلاثة بايتات مفكوكة؛ لذلك يجب على عمليات التحقق من جهة الخادم حساب الحجم المفكوك والتحقق منه قبل الكتابة.
يبدأ FUN_0041a2e0 عند 0x0041a2e0 وينشئ إطارًا بحجم 0xa8 بايت:
0041a2e0 addiu sp,sp,-168
0041a2e4 sw ra,164(sp)
0041a2e8 sw s8,160(sp)
0041a2ec move s8,sp
يبدأ الوجهة المفكوكة عند s8+0x1c؛ ويوجد s8 المحفوظ وra المحفوظ عند
s8+0xa0 وs8+0xa4:
decoded[64] s8+0x1c decoded offset 0
saved s8 s8+0xa0 decoded offset 132
saved ra s8+0xa4 decoded offset 136
المسافة الدقيقة لعنوان العودة هي 0xa4 - 0x1c = 0x88، أي 136 بايت.

استبدل نمط B المفكوك الترميز بطول 140 بايت عنوان العودة المحفوظ المكوّن من أربعة بايتات:
--- SIGSEGV {si_signo=SIGSEGV, si_code=1, si_addr=0x42424242} ---
qemu: uncaught target signal 11 (Segmentation fault)

مجموعة إدخال منفصلة بحجم 140 بايت حفظت ra بالقيمة 0x0041a2e0. سجّل تتبّع وحدة المعالجة المركزية في QEMU دخولًا عاديًا أولًا إلى المعالج، تلاه دخول ثانٍ بقيمة s8=0x41414141 وra=0x0041a2e0.

يحتوي الملف الثنائي الأصلي على تعليمة jal system مباشرة عند 0x0041a3cc. في التحقق المعزول الخاص، حمّلت تعليمات موجودة ذات قاعدة ثابتة علامةً في a0 ووصلت إلى ذلك الاستدعاء. تم تركيب برنامج مراقبة ساكن فوق /bin/sh؛ فقد سجّل وسائط مفسّر الأوامر ولم ينفّذ شيئًا: