Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
Инструменты/GitHubGitHub/wechicken456/cve-2021-4034-ctf-writeup
ЭксплуатацияОбратная инженерияCTFОбучение и ОбразованиеЭксплуатация Бинарных ФайловЛаборатории и Практика
GitHubwechicken456/cve-2021-4034-ctf-writeup

CVE-2021-4034-CTF-writeup

Репозиторий

Популярное

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

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

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

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

Смотреть все инструменты →
Поделиться
22 лет назадЕщё не проверено

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, в текущем каталоге.
  4. Создайте непривилегированного пользователя. Переключитесь на этого пользователя.
  5. Загрузите файлы из каталога challenge в текущий каталог.
  6. Запустите 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 значением , результирующий указатель будет , а это не то, что нам нужно.

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. Наслаждайтесь вашей оболочкой :)

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

Скачать инструмент
x
nxt
0x55555555**ab**e8 != 0x55555555**aa**e8
  • Поэтому мы должны выбрать текущий чанк и следующий чанк так, чтобы их 2-е наименее значимые байты совпадали.
  • Итак, теперь 3-й чанк находится по адресу B + 8, а не C. Отредактируем чанк B так, чтобы его 1-е поле (bread) содержало целевой адрес target, в который мы хотим писать.
  • Отредактируем 3-й чанк, что перезапишет содержимое, начиная с B + 8. Указатель notes этого 3-го чанка — это target! Так что всё, что мы записываем в notes, будет записано в target!