CTF 0x04 Write-up
=====================================================
في هذا المقال سنقوم بحل CTF 0x04 ,و هو عباره عن Code برنامج تشفير نصوص. في بداية التحدي يتم أمدادنا بنص مشفر باستخدام هذا الـ Code والمطلوب عمل Decrypt للنص، اى فك تشفيره بالاضافة الى القدرة على فك تشفير اي نص اخر تم تشفيره بواسطة هذا ال Code
\x31\xc0\x83\xc4\x08\x68\x65\x66\x67\x68\x68\x61\x62\x63\x64\x54\x5e\x89\xf7\xfc\xb1
\x08\xac\x50\x52\x0f\x31\x35\xff\xff\x00\x00\x88\xc2\x21\xd0\x89\xc3\x5a\x58\x30\xd8\xaa\x88
\x5c\x0c\x08\xe2\xe5\x54\x5e\x68\x63\x64\x65\x66\x68\x38\x39\x61\x62\x68\x34\x35\x36\x37
\x68\x30\x31\x32\x33\x54\x5b\xb1\x11\x31\xd2\x50\xac\x88\xc4\xc0\xe8\x04\x80\xe4\x0f\xd7
\x86\xe0\xd7\x88\xa2\x00\x91\x04\x08\x42\x88\x82\x00\x91\x04\x08\x42\x58\xe2\xe1\x31
\xdb\xb9\x00\x91\x04\x08\xb2\x22\xb3\x01\xb0\x04\xcd\x80\xb0\x01\xcd\x80
اذا نظرنا الى Code سوف نعرف انه Shellcode "كما هو متعارف عليه" أو Machine Code ممثل فى Hexadecimal.
و ال Machine code هى اللغة التى يتعامل بها و يفهمها ال CPU ويمكن ترجمتها لمجموعه من أوامر"instructions" للغة Assembly وهذه Assembly instructions تسمى mnemonics .و لكي نستطيع فهم هذا ال Machine code سوف تقوم بعمل Disassemble اى تحويل ال machine code الى mnemonics يسهل قرائتها.
بداية يجب أن نعرف أن "x\" هنا هى escape sequence توضح ان القيمة التى تليها ممثلة فى Hexadecimal وهي ليست جزء من الـ Code نفسه. لذلك سوف نقوم اولا بالتخلص منها عن طريق استبدلها ب space لان بعض ال Disassemblers اذا قمنا باعطائها ال machine code بهذه الصورة سوف تصدر خطأ اثناء عملية ال Disassembly.
توجد عدة طرق للقيام بذلك ولكن سنتعرض لطريقة واحدة فقط. سوف نقوم بنسخ ال shellcode فى ملف باستخدام vim وهو محرر نصوص يعمل على Linux و مماثل لمحرر النصوص vi وهو يتيح لنا كتابة اوامر تنفذ على النص منها عمل بحث واستبدال لنص معين داخل الملف.
سوف نقوم اولا بانشاء ملف عن طريق الامر vim shellcode.txt وعمل paste للـ shellcode فيه ثم بعد ذلك نقوم بالضغط على Esc لكى نخرج من insert mode الذي يسمح لنا بالتعديل فى النص ثم نكتب ":" لكى ندخل الى Command mode الخاص ب vim ثم نكتب الامر التالى لكى يستبدل كل " x\" بـ "space" و يكون syntax الامر كالتالى :
:[address]s/old_text/new_text/
:%s/\\x/ /g
بعد تنفيذ الأمر سوف نحصل على تلك النتيجة:
31 c0 83 c4 08 68 65 66 67 68 68 61 62 63 64 54 5e 89 f7 fc b1
08 ac 50 52 0f 31 35 ff ff 00 00 88 c2 21 d0 89 c3 5a 58 30 d8 aa 88
5c 0c 08 e2 e5 54 5e 68 63 64 65 66 68 38 39 61 62 68 34 35 36 37
68 30 31 32 33 54 5b b1 11 31 d2 50 ac 88 c4 c0 e8 04 80 e4 0f d7
86 e0 d7 88 a2 00 91 04 08 42 88 82 00 91 04 08 42 58 e2 e1 31
db b9 00 91 04 08 b2 22 b3 01 b0 04 cd 80 b0 01 cd 80
بعد ذلك نقوم بنسخ النتيجة عن طريق عمل select لها ثم الضغط على "y" لعمل copy او yank.
و لحفظ الملف نقوم بكتابة الامر:
:wq
الخطوه التاليه هى تشغيل الـ shellcode او عمل disassembly له عن طريق عمل Analysis لل code لفهم كيف تتم عمليه ال encryption فى البرنامج لكى نستطيع عمل decryption للنصوص المشفره.
و لتنفيذ هذه الخطوة توجد عدة طرق منها:
الطريقة الأولى :استخدام udis86 و هى library لعمل disassembly بتنفيذ الامر :
udcli -x -32 -intel shellcode.txt
الطريقة الثانية : باستخدام Debugger مثل Immunity Debugger هو برنامج يستخدم فى الهندسه العكسيه Reverse Engineering و Malware Analysis و فى كتابه ال Exploits.
سوف نستخدم هنا immunity debugger و سوف نقوم بتشغيل الكود عن طريق عمل binary paste للـ shellcode داخل immunity debugger بعد فتح اى برنامج exe فى ال immunity debugger سوف نستخدم هنا "internet explorer" ثم عمل select لمجموعة من ال instructions لم تنفذ بعد (يمكنك مراجعة EIP لمعرفة ما هي الـ Instruction القادمة التي سيتم تنفيذها). يجب عمل select لعدد كبير من الـ instructions لضمان وجود مكان كافي لاستيعاب الـ shellcode الخاص بنا. يمكننا التأكد من ذلك عن طريق التحقق من أخر Instruction تم عمل paste لها وأنها بالفعل آخر Instruction في الـ Shellcode الخاص بنا.
سوف يكون البرنامج فى حال الـ Pause ,سوف نقوم بالضغط على f9 لتشغيله ولكننا سوف نحصل على Access Violation لان احد ال instructions تريد الوصول الى Protected memory Address اى انه لا يجوز لهذا البرنامج الوصول لهذا الـ Memory location ,و هذا حدث لان ECX register يتواجد به قيمة ما (والمفترض أن يتم عمل Initialization له ليصبح 0) و بالتالى لن تنفذ الـ Loop بشكل صحيح , و للتغلب على هذه النقطه سوف نقوم بتعديل ال shellcode لكى نقوم بمسح محتويات ال ECX قبل البدء فى تنفيذ ال shellcode
فبالتالى الـ assembly instruction التى يجب ان نقوم باضافتها هى :
xor ecx,ecx
و ذلك يمكن ان يتم بواسطة عدة طرق منها:
الطريقة الأولى: باستخدام nasm.rb ويتم ذلك عن طريق تشغيل ال tool ثم كتابه ال Assembly instruction لكى نحصل على ال Machine code المكافئ لها
الطريقة الثانية: عن طريق استخدام ال Immunity Debugger يمكن لنا ان نقوم بكتابه ال instructions بشكل مباشر دون الحاجه للتحويل الى Machine code وذلك عن طريق اختيار instruction فى البرنامج ثم الضغط على space أو "right click -> assemble" ثم نكتب الأمر كالاتى:
والان لنعود الى الـ shellcode…..بعد ان حصلنا على ال machine code ل XOR ECX,ECX سوف نقوم باضافتها فى shellcode قبل اول استخدام للECX register سنكتبه هنا بعد ال CLD instruction و قبل mov cl,8 فيصبح shellcode كالتالى
31 c0 83 c4 08 68 65 66 67 68 68 61 62 63 64 54 5e 89 f7 fc 31 c9 b1
08 ac 50 52 0f 31 35 ff ff 00 00 88 c2 21 d0 89 c3 5a 58 30 d8 aa 88
5c 0c 08 e2 e5 54 5e 68 63 64 65 66 68 38 39 61 62 68 34 35 36 37
68 30 31 32 33 54 5b b1 11 31 d2 50 ac 88 c4 c0 e8 04 80 e4 0f d7
86 e0 d7 88 a2 00 91 04 08 42 88 82 00 91 04 08 42 58 e2 e1 31
db b9 00 91 04 08 b2 22 b3 01 b0 04 cd 80 b0 01 cd 80
نجد الآن Access violation ولكن خاصه ب instruction اخرى ,واذا نظرنا الى ال memory address سوف نجد انه immediate addressing أى انه قيمة ثابتة مكتوبه فى shellcode. هذا ال Memory Address هو الذي يقوم الكود بعمل write للنص المشفر فيه.
و لتخطى هذه ال access violation سوف نضع memory address يستطيع البرنامج الوصول اليه وسوف نختار memory address فى نطاق ال Stack ,و حيث ان ال ESP يحتوى على memory location هو "13ffd4" فيمكن مثلا ان نستبدل 08049100 ب 132000 و نقوم باعاده كتابه ال instructions التى كانت تحتوى عليه لتصبح:
Mov Byte PTR DS:[EDX+0x132000],AH
Mov Byte PTR DS:[EDX+0x132000],AL
Mov ECX,0x132000
نقوم بكتابه كتابه كل instruction فى مكانه ليصبح ال shellcode بعد التعديل :
31 c0 83 c4 08 68 65 66 67 68 68 61 62 63 64 54 5e 89 f7 fc 31 c9 b1
08 ac 50 52 0f 31 35 ff ff 00 00 88 c2 21 d0 89 c3 5a 58 30 d8 aa 88
5c 0c 08 e2 e5 54 5e 68 63 64 65 66 68 38 39 61 62 68 34 35 36 37
68 30 31 32 33 54 5b b1 11 31 d2 50 ac 88 c4 c0 e8 04 80 e4 0f d7
86 e0 d7 88 A2 00 20 13 00 42 88 82 00 20 13 00 42 58 e2 e1 31
db B9 00 20 13 00 b2 22 b3 01 b0 04 cd 80 b0 01 cd 80
بعد ذلك تعديل و اعادة التشغيل واضافة الـ shellcode سوف نبدأ عمل Analysis ل shellcode و سوف نقسم العمليه الى جزئين :
الجزء الاول :
هنا يتم عمل Push للنص المراد تشفيره فى ال stack ثم يتم اخذ كل حرف على حده ليخضع لعملية xor مع قيمة ناتجة عن عدة أوامر أولها RDTSC ,ثم بعد عمليه ال xor يتم تخزين النتيجة بالإضافة إلى القيمة التى تم عمل xor لها مع الحرف .و تلك العمليه هى اساس ال encryption حيث ان اذا كان هناك اثنان operands و قمنا بعمل xor لهم ,فيمكن لنا ان نقوم بعمل xor للنتيجه مع احد تلك ال operands حتى نحصل على ال operand الاخر.
a “ascii” ----> 61 hexadecimal
61 xor DD = BC
BC xor DD = 61
الجزء الثانى :
هنا يتم عمل push ل string وهو 0123456789abcdef وهى ال hexadecimal characters حيث أن هذا ال string سوف يتم استخدامه كجدول مع xlat instruction فى LOOP لعمل Translation لكل hexadecimal character من ال string المشفر الناتج عن الجزء الاول ثم الاحتفاظ به بدايتا من الMemory address الذى قمنا بتحديده مسبقا و هو 0x132000 لانه سوف يحتوى على الناتج النهائى الخاص بال decrypted string و تكون ال index لهذا الجدول هى ال value الخاصة ب AL .
الجزء الثالث :
هنا يستخدم الكود () system call write و exit فى ال Linux للقيام بعملية طباعة النص المشفر على الشاشة ثم عمل exit
الDecryptor :
سوف نقوم بكتابة Assembly instructions لعمل Decryption للنص :
بعد أن قمنا بعمل Analysis لل assembly instruction و عرفنا كيف يتم تشفير اى نص نستطيع عمل Reverse Engineering أو هندسه عكسيه للكود لفك تشفير أى نص تم تشفيره بواسطة هذا الكود.
0967b4941d0e6fe2f15757c300f72f6797cf073f6fa7df2767
سنحتاج أولا لمعرفة عدد ال characters بالنص و تحويلها الى hexadecimal ,يمكن لنا عن طريق كتابة الأمر التالي :
echo -n "0967b4941d0e6fe2f15757c300f72f6797cf073f6fa7df2767" | wc -c
Printf ‘%x \n’ 50
كود فك التشفير :
توجد طريقتين :
1- اما ان نقوم باعطاء الكود الencrypted string عن طريق عمل push لها ثم عمل loop لحفظه ب memory location
0x132000 ,سيكون ال machine code كالتالى
68 36 37 00 00 68 64 66 32 37 68 36 66 61 37 68 30 37 33 66 68 39 37 63 66 68 32 66 36 37 68 3030 66 37 68 35 37 63 33 68 66 31 35 37 68 36 66 65 32 68 31 64 30 65 68 62 34 39 34 68 30 39 3637 B9 32 00 00 00 31 D2 54 5E BF 00 20 13 00 AC AA 42 E2 FB 68 63 64 65 66 68 38 39 61 62 68 3435 36 37 68 30 31 32 33 B9 00 20 13 00 54 5F 89 FB 51 5E 31 C9 57 89 D0 B9 02 00 00 00 F6 F1 89C2 42 31 C9 B9 00 21 13 00 01 D1 51 31 C9 B1 11 53 5F AC F2 AE FE C9 80 F1 0F 88 CC 90 B1 11 535F AC F2 AE FE C9 80 F1 0F 88 C8 C0 E4 04 08 E0 88 82 00 21 13 00 4A 89 D1 E2 D3 BE 02 21 13 005A 89 D0 29 F0 B9 02 00 00 00 F6 F1 89 C1 BF 00 22 13 00 31 C0 AC 8A 22 4A 30 E0 AA E2 F5 B9 0022 13 00 83 C3 14
2- ان نقوم بتحويل النص من Ascii الى hexadecimal ثم نقوم فى ال immunity debugger بعمل inject encrypted string بشكل مباشر فى ال memory location 0x132000 "طريقة عملية اكثر و اسهل في التنفيذ" باختيار ال memory dump ثم الضغط على ctrl+g و كتابة ال memory location المراد الذهاب اليه و عمل binary paste لل hexdecimal value للنص كالتالى:
استخدمنا xxd -p لتحويل النص الى plaintext hexdump style
printf ‘0967b4941d0e6fe2f15757c300f72f6797cf073f6fa7df2767’ | xdd -p
:النتيجه
3039363762343934316430653666653266313537353763333030663732663637393763663037336636666137646632373637
نذهب الى memory location 0x132000 ثم نقوم بعمل ال binary paste ل hex به و نقوم بعمل binary paste ل "كود التشفير" Machine code فتكون النتيجه كالتالى:
كود التشفير :
BA 32 00 00 00 68 63 64 65 66 68 38 39 61 62 68 34 35 36 37 68 30 31 32 33 B9 00 20 13 00 54 5F 89 FB 51 5E 31 C9 57 89 D0 B9 02 00 00 00 F6 F1 89 C2 42 31 C9 B9 00 21 13 00 01 D1 51 31 C9 B1 11 53 5F AC F2 AE FE C9 80 F1 0F 88 CC 90 B1 11 53 5F AC F2 AE FE C9 80 F1 0F 88 C8 C0 E4 04 08 E0 88 82 00 21 13 00 4A 89 D1 E2 D3 BE 02 21 13 00 5A 89 D0 29 F0 B9 02 00 00 00 F6 F1 89 C1 BF 00 22 13 00 31 C0 AC 8A 22 4A 30 E0 AA E2 F5 B9 00 22 13 00 BB 00 20 13 00
سوف نتبع تلك الطريقة
نظرة عامة عن الكود :
سوف نقسم الكود الى جزأين
الجزء الأول :
وهى reversing الجزء الثانى من عملية التشفير حيث أن الكود يحتوى على طول النص و نقوم هنا بعمل push ل hexadecimal table كالذى تم استخدامه فى عملية التشفير فى الجزء الثانى ثم بعد ذلك نقوم بعمل nested loop و عمل قسمه لطول النص على 2 لاننا كل count فى ال loop الرئيسية سوف نقوم بعمل translation ل 2bytes من ال address 0x132000 عن طريق اخذ كل byte و عمل مقارنه لها بمحتويات الجدول وعندما يتم العثور عليه يتم اخذ ال index الموجوده به القيمه للعمليتين "تكرر مره لكل byte" و نقله الى memory address هو EDX+0x132100 أى انه سوف يكتب معكوس من الاخر الى 0x132100 "بمعنى آخر كتابه الASCII داخل ال memory"
مثال 30 39 فى ال memory سوف تحول الى 09 ويتم تسجيلها فى EDX+0x132100
وبعد الانتهاء من ال LOOP ستكون الmemory بالشكل التالى :
الجزء الثانى :
وهو reverse للجزء الاول فى كود التشفير وذلك عن طريق عمل xor لاول byte مع اخر byte ثم ال byte الثانية من الأول مع الثانية من النهاية ... ثم حفظ الناتج فى memory location 0x132200 وهو ال plaintext ممثل فى hexadecimal
و بعد تنفيذ الكود نحصل على ال Flag و هو n@k3r1h-f0x4
الحل ب High level language Decryptor :
بعد فهم كيفية عمل كود الـ encryption نستطيع إنشاء tool باى لغه كال python لتقوم بفك التشفير كهذه ال tool تقوم باخذ النص المشفر و عمل xor بنفس الطريقه لتحويله الى Plaintext
#!/usr/bin/python
import sys
if len(sys.argv) !=2:
print("")
print ("[*] Usage: nakerah_ctf_04_decryptor PASTE_YOUR_HASH_HERE ")
print("")
else:
def xor(s1,s2): # xor function
result=hex(int(s1,16) ^ int(s2,16))
return result[2:]
my_hash=sys.argv[1] #"ff409d3a27406d8674ee0a26425efe229e"
hash1=my_hash[0:int(len(my_hash)/2-1)]
hash2=my_hash[int(len(my_hash)/2+1):]
hash2_reversed=""
hash2_tmp=hash2
i=len(hash2)/2
while i>=0:
hash2_reversed=hash2_reversed + hash2_tmp[-2:]
hash2_tmp=hash2_tmp[0:-2]
i=i-1
i=0
hash2_lengh=len(hash2_reversed)
plaintext_hex=""
while i<hash2_lengh:
tmp1=hash1[i:i+2]
tmp2=hash2_reversed[i:i+2]
plaintext_hex=plaintext_hex+xor(tmp1,tmp2)
i=i+2
plaintext_ascii=(bytearray.fromhex(plaintext_hex).decode())
print("")
print("Plaintext is :"+plaintext_ascii)
print("")