Skip to content
KitploitKITPLOIT
أدواتالمدونة
إرسال
أدواتالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2021-4034-CTF-writeup — # كتابة تحليل لتحدي CTF من نوع pwn يستغل CVE-2021-4034 (pkexec) عبر التلاعب بالكومة، مع هندسة عكسية قائمة على Ghidra وملف مساعد ثنائي مخصص باسم shelly.so. | Kitploit
أدوات/GitHubGitHub/wechicken456/cve-2021-4034-ctf-writeup
الاستغلالالهندسة العكسيةCTFالتعلم والتعليماستغلال الملفات الثنائيةمختبرات وتدريب عملي
GitHubwechicken456/cve-2021-4034-ctf-writeup

CVE-2021-4034-CTF-writeup

# كتابة تحليل لتحدي CTF من نوع pwn يستغل CVE-2021-4034 (pkexec) عبر التلاعب بالكومة، مع هندسة عكسية قائمة على Ghidra وملف مساعد ثنائي مخصص باسم shelly.so.

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
عرض المستودع
25منذ 2 سنواتلم تتم المراجعة بعد
مشاركة

CVE-2021-4034-CTF-writeup

هذا تحدٍّ من نوع CTF pwn كتبته بلغة C ويتطلب من المستخدم استغلال ثغرة CVE-2021-4034. يحصل اللاعبون على ملفين ثنائيين في مجلد challenge في هذا المستودع. الملف الثنائي chal ينفّذ تحدّي CTF، وshelly.so ملف ثنائي مساعد.

كيفية محاكاة هذا التحدي

في وقت كتابة هذه المقالة، لا يزال ملف Dockerfile غير مكتمل. ملف Dockerfile مطلوب لنشر هذا التحدي أثناء CTF مباشر، لكن ليس محليًا: لا يزال بإمكانك محاكاة هذا التحدي محليًا عن طريق إعداد صلاحيات المستخدم وتثبيت الحزم المعرّضة للثغرة كما يلي:

  1. لاستغلال هذه الثغرة بنجاح، يجب على اللاعبين الذين ليسوا على جهاز Linux تثبيت جهاز Linux افتراضي أولًا. بعد ذلك، ستحتاج إلى تثبيت نواة معرّضة للثغرة. إرشادات حول كيفية القيام بذلك: [https://askubuntu.com/a/700221]
  2. ثبّت الحزم libpolkit-gobject-1-0=0.105-26ubuntu1 libpolkit-agent-1-0=0.105-26ubuntu1 policykit-1=0.105-26ubuntu1.
  3. أنشئ ملف flag.txt مملوكًا للمستخدم root:root في الدليل الحالي.
  • أنشئ مستخدمًا بصلاحيات محدودة. بدّل إلى هذا المستخدم.
  • نزّل الملفات الموجودة في مجلد challenge إلى الدليل الحالي.
  • شغّل chal كمستخدم بصلاحيات محدودة.
  • التحليل الأعمى

    تشغيل الملف الثنائي chal يعطينا فكرة مبهمة عما يفعله هذا الملف الثنائي:``` WELCOME TO THE HUB CTRL+ALT+DELICIOUS We're not just a sandwich hub. We are the beacon of flavors, serving a symphony in every byte

    1. ENTER THE HUB
    2. QUIT 1 Order number: 0x7ffde93681f0 Enter your name: tin
    3. ADD NEW ORDER
    4. EDIT ORDER
    5. SHOW ORDER
    6. CANCEL ORDER
    7. CHECKOUT
    8. DONE 1 Pick your bread: aaaa Select your spread: bbbb Choose your veg: cccc Slam your meat & egg: dddc Any side notes for the cook? 0000
    9. ADD NEW ORDER
    10. EDIT ORDER
    11. SHOW ORDER
    12. CANCEL ORDER
    13. CHECKOUT
    14. DONE 1 Pick your bread: AAAA Select your spread: BBBB Choose your veg: CCCC Slam your meat & egg: DDDD Any side notes for the cook? 1111
    15. ADD NEW ORDER
    16. EDIT ORDER
    17. SHOW ORDER
    18. CANCEL ORDER
    19. CHECKOUT
    20. DONE 3 Enter order index: 0 aaaa, bbbb, cccc, dddc, 0000
    21. ADD NEW ORDER
    22. EDIT ORDER
    23. SHOW ORDER
    24. CANCEL ORDER
    25. CHECKOUT
    26. DONE 3 Enter order index: 1 AAAA, BBBB, CCCC, DDDD, 1111
    27. ADD NEW ORDER
    28. EDIT ORDER
    29. SHOW ORDER
    30. CANCEL ORDER
    31. CHECKOUT
    32. DONE 4 Enter order index: 1
    33. ADD NEW ORDER
    34. EDIT ORDER
    35. SHOW ORDER
    36. CANCEL ORDER
    37. CHECKOUT
    38. DONE 3 Enter order index: 1 Invalid index!
    39. ADD NEW ORDER
    40. EDIT ORDER
    41. SHOW ORDER
    42. CANCEL ORDER
    43. CHECKOUT
    44. DONE 5 /notes ./tin/notes
    45. ADD NEW ORDER
    46. EDIT ORDER
    47. SHOW ORDER
    48. CANCEL ORDER
    49. CHECKOUT
    50. DONE 6
    51. ENTER THE HUB
    52. QUIT 2 Come again :)
    root@kitploit:~
    التلاعب بحجم الإدخال في دالة `Add` ومؤشرات دوال `Cancel` لا يعطينا أي شيء مميز (لا تجاوز سعة المخزن المؤقت ولا خطأ تجزئة). ومع ذلك، هناك بعض الأشياء المثيرة للاهتمام:
      - يبدو أن الطلبات تُوضع في قائمة (ربما قائمة مرتبطة) وأنها ذات فهرسة تبدأ من الصفر؟
      - يوجد هذا `Order number: 0x7ffde93681f0` والذي يبدو أنه يطبع موقعًا ما على المكدس؟
      - أيضًا، يقوم البرنامج بإنشاء مجلد باسمنا الذي أدخلناه مع 3 ملفات تنفيذية، أحدها هو الملف الثنائي المساعد الذي حصلنا عليه:```sh
    peasant@Tin-VM:~/Desktop$ ls
    chal  chal.c  Dockerfile shelly.so  solve.py  tin
    peasant@Tin-VM:~/Desktop$ ls -l tin/
    total 28
    -rwxrwx--- 1 peasant vboxsf     5 Feb  4 14:33 notes
    -rwxrwx--- 1 peasant vboxsf    20 Feb  4 14:33 recipe
    -rwxr-x--- 1 peasant vboxsf 16488 Feb  4 14:33 shelly.so
    peasant@Tin-VM:~/Desktop$ cat tin/notes 
    0000
    peasant@Tin-VM:~/Desktop$ cat tin/recipe 
    aaaa
    bbbb
    cccc
    dddc
    
    

    تحتوي الملفات التنفيذية على مدخلاتنا.


    تحليل Ghidra

    يمكنك العبث بالوظائف الأخرى وتأمل أن تصادف بعض الأخطاء (وهو أمر محتمل)، لكنني سأختصر الأمور وأفتح البرنامج في Ghidra.

    بمقارنة السلاسل النصية التي تظهر أثناء تشغيل البرنامج مع السلاسل النصية الموجودة في Ghidra، يمكننا إعادة تسمية بعض دوال FUN_* إلى أسماء مألوفة:```C undefined8 main(void)

    { int iVar1; size_t sVar2; undefined2 *puVar3; long in_FS_OFFSET; int opt; int local_1c; char *local_18; long local_10;

    local_10 = *(long *)(in_FS_OFFSET + 0x28); local_18 = "/recipe"; print("WELCOME TO THE HUB CTRL+ALT+DELICIOUS\n"); print( "We're not just a sandwich hub. We are the beacon of flavors, serving a symphony in every by te\n\n" ); while( true ) { print("1. ENTER THE HUB\n"); print("2. QUIT\n"); __isoc99_scanf(&DAT_001030c5,&opt); getc(stdin); if (opt != 1) break; printf("Order number: %p\n",&local_18); print("Enter your name: "); __isoc99_scanf(&DAT_0010334f,&DAT_00105120); sVar2 = strlen(&DAT_00105120); puVar3 = (undefined2 *)malloc(sVar2 + 2); DAT_00105100 = puVar3; *puVar3 = 0x2f2e; *(undefined *)(puVar3 + 1) = 0; strcpy((char *)(DAT_00105100 + 1),&DAT_00105120); iVar1 = FUN_001022f0(DAT_00105100,&DAT_00105060); if (iVar1 == -1) { mkdir((char *)DAT_00105100,0x1c0); } DAT_00105140 = 0; order_cnt = 0; for (local_1c = 0; local_1c < 10; local_1c = local_1c + 1) { *(undefined8 *)(&ptr_array + (long)local_1c * 8) = 0; } main_menu(); } print("Come again :)\n");

    root@kitploit:~
    The `printf("Order number: %p\n",&local_18);` **يطبع موقع متغير محلي على المكدس.**
    
    يُظهر فحص سريع في gdb أن العنوان المُسرَّب هو عنوان المؤشر إلى السلسلة الثابتة `/recipe`:```gdb
    ...
    Order number: 0x7fffffffdfc0
    ...
    gef➤  x/gx 0x7fffffffdfc0
    0x7fffffffdfc0:	0x0000555555559020
    gef➤  x/s 0x0000555555559020
    0x555555559020:	"/recipe"
    

    نرى استدعاء mkdir، والذي ينشئ مجلدًا باسم مدخلاتنا في الدليل الحالي.

    هذه متوافقة مع ملاحظتنا عندما قمنا بتشغيل البرنامج. ثم يقوم بتهيئة بعض المتغيرات قبل استدعاء دالة main_menu، والتي تبدو كالتالي:```C while( true ) { while( true ) { while( true ) { while( true ) { while( true ) { while( true ) { print("1. ADD NEW ORDER\n"); print("2. EDIT ORDER\n"); print("3. SHOW ORDER\n"); print("4. CANCEL ORDER\n"); print("5. CHECKOUT\n"); print("6. DONE\n"); __isoc99_scanf(&DAT_001030c5,&local_40); getc(stdin); if (local_40 != 1) break; add_order(); } if (local_40 != 2) break; edit_order(); } if (local_40 != 3) break; show_order(); } if (local_40 != 4) break; cancel_order(); } if (local_40 != 5) break; checkout(); } if (local_40 == 6) break; if (local_40 == 0x539) { print( "\nGORDON RAMSAY: Finally, a worthy opponent, our battle will be legendary! I BET YOU CAN 'T GUESS THE SECRET RECIPE.\n" ); fgets(inp,0x20,stdin); getrandom(random-bytes,0x10,0); for (local_3c = 0; local_3c < 0x10; local_3c = local_3c + 1) { if (inp[local_3c] != random-bytes[local_3c]) { print("...Nuh Uh!...\n"); /* WARNING: Subroutine does not return */ exit(0); } print("...Ooh Yes.. sCruMpTioUs..."); } print("Fine... I'll give you a taste.\n"); FUN_00101504(); }

    root@kitploit:~
    إذا لم تكن معتادًا على REV، فهذا هو التفكيك (decompilation) لتعليمة `switch` في لغة C. يوجد خيار مثير للاهتمام يُعرف بـ `0x539`. يتيح للاعبين تخمين `0x10` بايتات عشوائية. إذا كانت جميع البايتات متساوية، فإنه يستدعي `FUN_00101504();` الذي يستدعي `system("cat flag.txt");`. وإلا، يخرج البرنامج.
    
    ومع ذلك، فإن تخمين 16 بايتًا عشوائيًا بالقوة العمياء يعادل تجربة كل الاحتمالات البالغة 256**16 = 340282366920938463463374607431768211456 احتمالًا. حظًا موفقًا مع هذا ههه.
    
    حتى لو تجاوزت كل الاحتمالات، فإن البرنامج لا يزال غير ممنوح صلاحيات (unprivileged)، لذا لا يمكنه قراءة العلم. عبارة `cat flag.txt` هذه مقصودة، ليس فقط لخداع اللاعبين عديمي الخبرة لاختيار خيار القائمة `0x539` هذا، ولكن أيضًا لمنع اللاعبين من مجرد استدعاء هذه الدالة لقراءة العلم، وهو ما سنتعمق فيه لاحقًا.
    ***
    ## إضافة```C
      int iVar1;
      undefined8 *puVar2;
      void *pvVar3;
      undefined8 *ptr2;
      
      if (order_cnt < 10) {
        puVar2 = (undefined8 *)malloc(0x30);
        print("Pick your bread: ");
        readline(puVar2 + 1,8);
        print("Select your spread: ");
        readline(puVar2 + 2,8);
        print("Choose your veg: ");
        readline(puVar2 + 3,8);
        print("Slam your meat & egg: ");
        readline(puVar2 + 4,9);
        iVar1 = order_cnt;
        pvVar3 = malloc(0x30);
        *(void **)(&ptr_array + (long)iVar1 * 8) = pvVar3;
        *puVar2 = *(undefined8 *)(&ptr_array + (long)order_cnt * 8);
        print("Any side notes for the cook? ");
        readline(*puVar2,0x30);
        puVar2[5] = 0;
        if (DAT_00105140 != (undefined8 *)0x0) {
          for (ptr2 = DAT_00105140; ptr2[5] != 0; ptr2 = (undefined8 *)ptr2[5]) {
          }
          ptr2[5] = puVar2;
          puVar2 = DAT_00105140;
        }
        DAT_00105140 = puVar2;
        order_cnt = order_cnt + 1;
      }
    ...
    

    إذا كانت بعض المتغيرات لا تبدو سهلة القراءة بهذا الشكل بالنسبة لك، فذلك لأنني أخذت بعض الوقت لإعادة تسميتها إلى هذه الأسماء، وهو ما يجب عليك فعله أثناء الهندسة العكسية لتتبّع الأمور. حسنًا، لننتقل إلى النقاط الرئيسية:

    • puVar2 هو قطعة malloc بحجم 0x30.
    • الحقل 0 هو مؤشر puVar3، طوله 8 بايت ويشير إلى notes الخاصة بالقطعة الحالية.
    • الحقول الأربعة التالية طول كل منها 8 بايت، ومع ذلك يمكننا قراءة 9 بايت في الحقل الرابع، وبالتالي يمكننا تجاوز بايت واحد إلى الحقل الخامس.
    • ومع ذلك، فإن السطر puVar2[5] = 0; يكتب فوق تجاوز البايت الواحد بقيمة NULL على أي حال.
    • ثم إذا كان المتغير العام DAT_00105140 == 0، فإننا نضبطه فقط على القطعة الجديدة، لذا قد يكون هذا مؤشر head للقائمة؟
    • وإلا، فإننا نكرر الحلقة حتى آخر قطعة في القائمة الحالية، ونضبط حقلها الخامس على القطعة الجديدة. => الحقل الخامس هو المؤشر إلى القطعة التالية في القائمة المرتبطة.

    تعديل```C

    ... print("Enter order index: "); __isoc99_scanf(&DAT_001030c5,&local_20); getc(stdin); if ((local_20 < 0) || (order_cnt <= local_20)) { print("Invalid index!\n"); } else { local_18 = head; for (local_1c = 0; local_1c != local_20; local_1c = local_1c + 1) { local_18 = (undefined8 *)local_18[5]; } print("Pick your bread: "); readline(local_18 + 1,8); print("Select your spread: "); readline(local_18 + 2,8); print("Choose your veg: "); readline(local_18 + 3,8);WE print("Slam your meat & egg: "); readline(local_18 + 4,9); print("Any side notes for the cook? "); readline(*local_18,0x30); } ...

    root@kitploit:~
    هناك تحقق من فهرس الإدخال الخاص بنا، لذا لا يمكننا تعديل مواقع عشوائية. 
    
    **ومع ذلك، فإن القراءة التي تبلغ 9 بايت في الحقل الرابع والتي تتجاوز بايتًا واحدًا إلى الحقل الخامس ما زالت موجودة!!! يمكننا الكتابة فوق بايت واحد في مؤشر `nxt`**
    ***```C
    ...
        if (local_18 != (undefined8 *)0x0) {
          printf("%s, %s, %s, %s, %s\n",local_18 + 1,local_18 + 2,local_18 + 3,local_18 + 4,*local_18);
        }
    ...
    

    %s سيطبع حتى حرف الـ NULL، لذا إذا كان الحقل الرابع لدينا بطول 8 بايت، فإن %s الرابع سيطبع الحقل الرابع من القطعة (chunk) الخاصة بنا + قيمة مؤشر nxt في الحقل الخامس. => نحصل على تسريب heap!!!


    إلغاء

    لا شيء مثير للاهتمام هنا.


    الدفع (Checkout)```C

    strcpy(local_d8,dir_name); sVar2 = strlen(dir_name); strcpy(local_d8 + sVar2,"/recipe"); creat(local_d8,0x1c0); iVar1 = open(local_d8,2); if (iVar1 == -1) { print("Error opening file f.\n"); /* WARNING: Subroutine does not return / exit(0); } chmod(local_d8,0x1f8); strcpy(local_98,dir_name); sVar2 = strlen(dir_name); strcpy(local_98 + sVar2,"/notes"); printf("%s %s\n","/notes",local_98); creat(local_98,0x1c0); __fd = open(local_98,2); if (__fd == -1) { print("Error opening file f_notes.\n"); / WARNING: Subroutine does not return */ exit(0); } chmod(local_98,0x1f8);

    root@kitploit:~
    إذن يقوم بإنشاء الملفين `recipe` و `notes` في المجلد الذي يحمل اسم الإدخال الخاص بنا. عبارات `chmod` تضع هذه الملفات في وضع التنفيذ: `0x1f8` و `0x1c0` هما `0700` و `0770` في الأساس الثماني على التوالي.```C
    ...
      for (local_ec = 0; (local_e0 != (char **)0x0 && (local_ec < order_cnt)); local_ec = local_ec + 1)
      {
        sVar2 = strlen((char *)(local_e0 + 1));
        write(iVar1,local_e0 + 1,sVar2);
        write(iVar1,&DAT_00103127,1);
        sVar2 = strlen((char *)(local_e0 + 2));
        write(iVar1,local_e0 + 2,sVar2);
        write(iVar1,&DAT_00103127,1);
        sVar2 = strlen((char *)(local_e0 + 3));
        write(iVar1,local_e0 + 3,sVar2);
        write(iVar1,&DAT_00103127,1);
        sVar2 = strlen((char *)(local_e0 + 1));
        write(iVar1,local_e0 + 4,sVar2);
        write(iVar1,&DAT_00103127,1);
        sVar2 = strlen(*local_e0);
        write(__fd,*local_e0,sVar2);
        write(__fd,&DAT_00103127,1);
        local_e0 = (char **)local_e0[5];
      }
      iVar1 = close(iVar1);
      if (-1 < iVar1) {
        iVar1 = close(__fd);
        if (-1 < iVar1) {
          local_58 = 0x2f706d742f207063;
          local_50 = 0x732e796c6c656873;
          local_48 = 0x206f;
          local_40 = 0;
          local_38 = 0;
          local_30 = 0;
          local_28 = 0;
          local_20 = 0;
          strcpy((char *)((long)&local_48 + 2),dir_name);
          system((char *)&local_58);
          if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                        /* WARNING: Subroutine does not return */
            __stack_chk_fail();
          }
          return;
        }
      }
    ...
    

    هذه الكتلة الضخمة الفوضوية من الكود تكتب بشكل أساسي المحتوى الموجود في قطعنا إلى هذه الملفات، ثم تنفذ الأمر cp /tmp/shelly.so dir_name، حيث dir_name هو الاسم الذي نعطيه للبرنامج في البداية.


    FUN_00101e84```C

    void FUN_00101e84(void)

    { long in_FS_OFFSET; char *local_18; long local_10;

    local_10 = *(long *)(in_FS_OFFSET + 0x28); puts("HOLD UP, LET HIM COOK."); local_18 = (char *)0x0; execve("/usr/bin/pkexec",&local_18,(char **)&ptr_array); if (local_10 != *(long )(in_FS_OFFSET + 0x28)) { / WARNING: Subroutine does not return */ __stack_chk_fail(); } return; }

    root@kitploit:~
    هذه دالة مثيرة للاهتمام. إنها تستدعي `pkexec` مع `NULL argv` ومؤشر مصفوفة `notes` كمتغيرات بيئة، وهو الأمر القابل للاستغلال في CVE-2021-4034. هل يشير هذا إلى أنه علينا صياغة `notes` الخاصة بنا وإعادة توجيه التنفيذ إلى هذه الدالة بطريقة ما؟
    
    ***
    ## shelly.so
    فتح هذا الملف في Ghidra سيُخبرنا أنه من الواضح مكتبة `set-UID-root`، وهي مشابهة لتلك المستخدمة في استغلال CVE.
    
    ***
    # الملخص
    بعض النقاط الرئيسية:
    1. لدينا تسريب للمكدس (رقم الطلب) في بداية البرنامج، والذي يحمل عنوان السلسلة الثابتة `/recipe` المستخدمة في إنشاء ملف `recipe`.
    2. الحقل 0 من الكتلة (chunk) هو مؤشر `notes` الخاص بها.
    3. الحقل الخامس من الكتلة هو مؤشر `nxt` إلى الكتلة التالية في القائمة المرتبطة.
    4. يمكن لـ `Show` تسريب عنوان كومة (heap address) وهو الحقل الخامس في الكتلة.
    5. يمكن لـ `Edit` الكتابة فوق 1 بايت في الحقل الخامس. **هذه هي ثغرة الكتابة التعسفية الوحيدة!!**
    6. `FUN_00101e84` تستدعي `pkexec` مع `NULL argv` ومتغيرات البيئة التي نتحكم فيها (مصفوفة `notes`).
    
    ***
    # الاستغلال
    
    ## تخطيط كتلة الكومة
    يمكن أن يخبرنا فحص سريع في gdb بالفارق (offset) بين الكتل المختلفة في برنامجنا:```gdb
    ...
    Pick your bread: aaaa
    Select your spread: a
    Choose your veg: a
    Slam your meat & egg: a
    Any side notes for the cook? 0000
    1. ADD NEW ORDER
    2. EDIT ORDER
    3. SHOW ORDER
    4. CANCEL ORDER
    5. CHECKOUT
    6. DONE
    1
    Pick your bread: bbbb
    Select your spread: b
    Choose your veg: b
    Slam your meat & egg: b
    Any side notes for the cook? 1111
    ...
    gef➤  search-pattern aaaa
    [+] Searching 'aaaa' in memory
    [+] In '[heap]'(0x55555555a000-0x55555557b000), permission=rw-
      0x55555555aae8 - 0x55555555aaec  →   "aaaa" 
    gef➤  x/30gx 0x55555555aae8-0x18
    0x55555555aad0:	0x0000000000000000	0x0000000000000041
    0x55555555aae0:	0x000055555555ab20	0x0000000061616161
    0x55555555aaf0:	0x0000000000000061	0x0000000000000061
    0x55555555ab00:	0x0000000000000061	0x000055555555ab60
    0x55555555ab10:	0x0000000000000000	0x0000000000000041
    0x55555555ab20:	0x0000000030303030	0x0000000000000000
    0x55555555ab30:	0x0000000000000000	0x0000000000000000
    0x55555555ab40:	0x0000000000000000	0x0000000000000000
    0x55555555ab50:	0x0000000000000000	0x0000000000000041
    0x55555555ab60:	0x000055555555aba0	0x0000000062626262
    0x55555555ab70:	0x0000000000000062	0x0000000000000062
    0x55555555ab80:	0x0000000000000062	0x0000000000000000
    0x55555555ab90:	0x0000000000000000	0x0000000000000041
    0x55555555aba0:	0x0000000031313131	0x0000000000000000
    0x55555555abb0:	0x0000000000000000	0x0000000000000000
    

    إذن حجم الكتل هو 0x40 (بما في ذلك 0x10 بايت من البيانات الوصفية)، وهي مفصولة (من بداية الكتلة الحالية إلى بداية الكتلة التالية) بإزاحة مقدارها 0x40+0x40 = 0x80. وذلك لأن notes تُخصَّص كلما خصصنا كتلة، لذا فهي تقع دائمًا بين الكتل المتتالية.

    الكتابة إلى موقع عشوائي

    قبل أن ننتقل إلى الاستغلال، كيف يمكننا إساءة استخدام ثغرات تجاوز السعة ذات البايت الواحد للكتابة إلى عنوان عشوائي؟ يمكننا استخدام الاستراتيجية التالية:

    1. خصّص 3 كتل A وB وC.
    2. سرّب مؤشر nxt للكتلة الثانية (النقطة الثانية في قسم الملخص).
      • العنوان المسرَّب (وهو الكتلة التالية) هو C.
      • عندها سيكون عنوان الكتلة الحالية هو B = C-0x80.
      • لنفترض أن البايت الأقل أهمية في B هو x.
    3. اكتب البايت x + 8 عبر تجاوز السعة إلى الحقل الخامس من الكتلة الحالية. سيكون مؤشر nxt الناتج هو B + 8.
      • علينا أن نكون حذرين هنا: باستخدام المقطع من الذاكرة أعلاه، إذا جعلنا الكتلة الحالية هي الأولى أي A = 0x55555555aae0، فإن العنوان المسرَّب سيكون B = 0x55555555ab60 وx = 0xe8.
      • إذا كتبنا البايت الأخير في الحقل الخامس من A عند 0x55555555ab08 بالقيمة x، فإن مؤشر nxt الناتج سيكون 0x55555555**ab**e8 != 0x55555555**aa**e8، وهذا ليس ما أردناه.
      • لذلك، علينا اختيار كتلة حالية وكتلة تالية بحيث يكون البايت الثاني الأقل أهمية لهما متطابقًا.
    4. إذن الآن لدينا الكتلة الثالثة عند B + 8 بدلاً من C. عدّل الكتلة B بحيث يحتوي حقلها الأول (bread) على العنوان target الذي نريد الكتابة إليه.
    5. عدّل الكتلة الثالثة، وهذا سيستبدل المحتوى ابتداءً من B + 8. مؤشر notes في هذه الكتلة الثالثة هو target! لذا أي شيء نكتبه إلى notes سيُكتب إلى target!

    الطريقة الأولى - القفز إلى "cat flag.txt"

    هناك طريقة للقفز إلى هذه الدالة، لن أناقشها. لكن قبل أن تجرب هذا، ألقِ نظرة على Dockerfile. هل تلاحظ أي شيء بخصوص flag.txt؟``` ... chown root:root /home/ctf/flag.txt ... USER peasant CMD ["/home/ctf/start.sh"]

    root@kitploit:~
    ***إنه مملوك من root، بينما البرنامج يُشغَّل ويكون مملوكًا من مستخدم غير مميّز***.
    لذا حتى لو نفّذت "cat flag.txt"، فلن تتم طباعة العلم لأن البرنامج لا يملك صلاحية الوصول إلى الملف. وهذا يعني أننا بحاجة إلى تصعيد الصلاحيات إلى root لنتمكن من قراءة العلم.
    ## الطريقة الثانية - CVE-2021-4034
    لاستغلال CVE-2021-4034، نحتاج إلى الإعداد التالي:
    1. مجلد باسم `GCONV_PATH=.`
    2. في هذا المجلد، نحتاج إلى ملف قابل للتنفيذ يحمل اسم المجلد الذي سيحتوي ملف الإعداد `gconv-modules`. ليكن هذا `recipe` في تحدّينا.
    3. في مجلد `recipe`، نحتاج إلى ملف باسم `gconv-modules` بمحتوى نتحكم فيه، ومكتبة ديناميكية تمنحنا شل (ربما تكون هذه هي الثنائية المعطاة `shelly.so`)?
    4. بعد ذلك، في ملف `gconv-modules` الخاص بنا، نحتاج إلى نفس `CHARSET` كما في الخطوة 5 أدناه، واسم مكتبتنا الديناميكية `shelly.so` على هذا النحو: `module UTF-8// SHELLY// shelly 2`.
    5. بعد كل هذا، نستدعي `pkexec` مع `argv` بقيمة NULL ومصفوفة متغيرات البيئة المصنوعة = `{"recipe", "PATH=GCONV_PATH=.", "CHARSET=SHELLY", "SHELL=shelly", NULL}`.
    
    الآن، نستغل هذا التحدي للحصول على الإعداد أعلاه.
    
    ***
    ### الهدف 1-2
    الخطوتان 1-2 سهلتان: يكفي أن نسجّل الدخول باسم مستخدم `GCONV_PATH=.` لإنشاء هذا المجلد. ثم ننفّذ `checkout` لإنشاء الملف القابل للتنفيذ `recipe` في هذا المجلد. ثم نعود إلى القائمة الأولى.
    ***
    ### الهدف 3-4
    نسجّل الدخول باسم المستخدم `recipe` لإنشاء هذا المجلد. الآن، نحن *نحتاج* إلى إنشاء ملف باسم `gconv-modules`، لكن `checkout` ينشئ فقط الملفين `recipe` و`notes`.
    
    ماذا لو استبدلنا موقع الذاكرة الخاص بالسلسلة `/notes` لتصبح `/gconv-modules`؟ قد ينجح هذا، لكنه يتطلب أن تكون تلك الذاكرة قابلة للكتابة. لنتحقق من ذلك في gdb:```gdb
    gef➤  search-pattern /notes
    [+] Searching '/notes' in memory
    [+] In '/home/peasant/Desktop/chal'(0x555555559000-0x55555555a000), permission=rw-
      0x555555559010 - 0x555555559016  →   "/notes" 
    

    وهو قابل للكتابة!

    كيف ننفذ عملية الكتابة؟

    1. نستخدم استراتيجية الكتابة التعسفية الموصوفة أعلاه لتنفيذ قراءة تعسفية لموقع عنوان المكدس المتسرب. وهذا يعطينا مكان /recipe.
    2. يمكن لفحص سريع في gdb أن يخبرنا أن الإزاحة من /recipe إلى /notes هي 0x10. اطرح هذا من عنوان /recipe في الخطوة 1 لتحصل على عنوان /notes.
    3. استخدم استراتيجية الكتابة التعسفية أعلاه لاستبدال /notes بـ /gconv-modules.

    أما بخصوص كتابة المحتوى الصحيح إلى ملف gconv-modules، فنحن نعلم أنه عند checkout، سيتم كتابة محتوى notes الخاص بالقطع إلى /notes، وهو الآن /gconv-modules. وللتأكد، يمكننا ببساطة توفير السلسلة module UTF-8// SHELLY// shelly 2 لكل notes.

    إذن الآن، كلما استدعينا checkout، سينشئ ملفين recipe وgconv-modules ويكتب بعض الأسطر module UTF-8// SHELLY// shelly 2 (نستخدم shelly لأن هذا هو اسم مكتبة .so ذات صلاحيات set-uid-root المعطاة) إلى ملف gconv-modules. وسيقوم أيضًا بنسخ shelly.so إلى نفس الدليل.

    الهدف 5

    1. نخصص 4 قطع بحيث تكون notes الخاصة بها هي متغيرات البيئة المناظرة. ستحتوي مصفوفة مؤشرات notes على 4 مؤشرات لهذه الـ 4 قِطع ومؤشر NULL في النهاية، وهو بالضبط ما أردناه.
    2. نخصص قطعًا إضافية ونستخدم نفس الاستراتيجية التي استخدمناها في الهدف 3-4 لاستبدال RIP الخاص بالدالة main بالدالة السرية FUN_00101e84.
      • كيف نعرف أين يقع RIP؟ هل تتذكر عنوان المكدس المتسرب في بداية البرنامج؟ ادخل إلى gdb لمعرفة إزاحته إلى RIP، ثم أضف تلك الإزاحة إلى عنوان المكدس المتسرب الفعلي لتحصل على RIP.
      • كيف نعرف أين يقع main؟ هل تتذكر عنوان /recipe المتسرب من الخطوة 2 من الهدف 3-4؟ افعل الشيء نفسه هنا!
    3. استمتع بالشيل :)

    نص الحل مع التعليقات متوفر في هذا المستودع. لا تتردد في التواصل معي إذا كان لديك أي أسئلة :)

    تنزيل الأداة