WWW2Exec - atexit()
Last updated
Last updated
Вивчайте та практикуйте хакінг AWS:Школа хакінгу HackTricks AWS Red Team Expert (ARTE) Вивчайте та практикуйте хакінг GCP: Школа хакінгу HackTricks GCP Red Team Expert (GRTE)
Сьогодні дуже дивно експлуатувати це!
atexit()
- це функція, до якої передаються інші функції в якості параметрів. Ці функції будуть виконані при виконанні exit()
або поверненні з головної функції.
Якщо ви можете змінити адресу будь-якої з цих функцій, щоб вказувати на shellcode, наприклад, ви отримаєте контроль над процесом, але зараз це складніше.
Наразі адреси функцій, які мають бути виконані, приховані за декількома структурами, і, нарешті, адреса, на яку вони вказують, не є адресами функцій, а шифруються за допомогою XOR та зсувів з випадковим ключем. Таким чином, наразі цей вектор атаки не є дуже корисним принаймні на x86 та x64_86.
Функція шифрування - PTR_MANGLE
. Інші архітектури, такі як m68k, mips32, mips64, aarch64, arm, hppa... не реалізують функцію шифрування, оскільки вона повертає те саме, що отримала на вході. Таким чином, ці архітектури можна атакувати за допомогою цього вектора.
Ви можете знайти докладне пояснення того, як це працює за посиланням https://m101.github.io/binholic/2017/05/20/notes-on-abusing-exit-handlers.html
Як пояснено у цьому пості, якщо програма завершується за допомогою return
або exit()
, вона виконає __run_exit_handlers()
, яка викличе зареєстровані деструктори.
Якщо програма завершується через функцію _exit()
, вона викличе системний виклик exit
і обробники виходу не будуть виконані. Тому, щоб підтвердити виконання __run_exit_handlers()
, ви можете встановити точку зупинки на цій функції.
Важливий код (джерело):
Зверніть увагу, як map -> l_addr + fini_array -> d_un.d_ptr
використовується для обчислення позиції масиву функцій для виклику.
Є кілька варіантів:
Перезаписати значення map->l_addr
, щоб воно вказувало на фальшивий fini_array
з інструкціями для виконання довільного коду
Перезаписати записи l_info[DT_FINI_ARRAY]
та l_info[DT_FINI_ARRAYSZ]
(які приблизно послідовні у пам'яті), щоб вони вказували на підроблену структуру Elf64_Dyn
, яка знову зробить array
вказівником на пам'ять, яку контролює зловмисник.
Цей опис перезаписує l_info[DT_FINI_ARRAY]
адресою керованої пам'яті в .bss
, що містить фальшивий fini_array
. Цей фальшивий масив містить спочатку адресу одного гаджета, яка буде виконана, а потім різницю між адресою цього фальшивого масиву та значенням map->l_addr
, щоб *array
вказував на фальшивий масив.
Згідно з основним повідомленням цієї техніки та цим описом ld.so залишає вказівник на стеку, який вказує на бінарний link_map
в ld.so. З допомогою довільного запису можна перезаписати його і зробити його вказівником на фальшивий fini_array
, керований зловмисником, з адресою одного гаджета, наприклад.
Після попереднього коду ви можете знайти ще один цікавий розділ з кодом:
У цьому випадку можливо перезаписати значення map->l_info[DT_FINI]
, що вказує на сфальсифіковану структуру ElfW(Dyn)
. Знайдіть більше інформації тут.
__run_exit_handlers
Як пояснено тут, якщо програма завершується за допомогою return
або exit()
, вона виконає __run_exit_handlers()
, яка викличе будь-які зареєстровані функції деструкторів.
Код з _run_exit_handlers()
:
Код з __call_tls_dtors()
:
Для кожної зареєстрованої функції в tls_dtor_list
, він розшифрує вказівник з cur->func
та викличе його з аргументом cur->obj
.
Використовуючи функцію tls
з цього форку GEF, можна побачити, що dtor_list
насправді дуже близько до стекового канарейки та PTR_MANGLE кукі. Таким чином, з переповненням цього буде можливо перезаписати куку та стекову канарейку.
Перезаписуючи PTR_MANGLE куку, буде можливо обійти функцію PTR_DEMANLE
, встановивши її на 0x00, що означатиме, що xor
, використаний для отримання реальної адреси, буде лише адресою, яка налаштована. Потім, записуючи в dtor_list
, можливо ланцюжити кілька функцій з адресою функції та її аргументом.
Зауважте, що збережений вказівник не лише буде xor з кукою, але також буде обертатися на 17 біт:
Отже, вам потрібно врахувати це перед додаванням нової адреси.
Знайдіть приклад у оригінальному пості.
__run_exit_handlers
Ця техніка пояснюється тут і знову залежить від програми виходу, яка викликає return
або exit()
, тоді викликається __run_exit_handlers()
.
Давайте перевіримо більше коду цієї функції:
Змінна f
вказує на структуру initial
, і в залежності від значення f->flavor
будуть викликані різні функції.
Залежно від значення, адреса функції для виклику буде знаходитися в різних місцях, але завжди буде розкодована.
Крім того, у параметрах ef_on
та ef_cxa
також можна контролювати аргумент.
Можливо перевірити структуру initial
під час сеансу налагодження з GEF, запустивши gef> p initial
.
Для зловживання цим потрібно або витікати або стерти куку PTR_MANGLE
і потім перезаписати запис cxa
в initial на system('/bin/sh')
.
Приклад цього можна знайти в оригінальному дописі в блозі про техніку.