Stack Canaries
StackGuard та StackShield
StackGuard вставляє спеціальне значення, відоме як канарейка, перед EIP (Extended Instruction Pointer), зокрема 0x000aff0d
(представляє собою нуль, новий рядок, EOF, повернення каретки), щоб захистити від переповнення буфера. Однак функції, такі як recv()
, memcpy()
, read()
, та bcopy()
, залишаються вразливими, і вона не захищає EBP (Base Pointer).
StackShield використовує більш складний підхід, ніж StackGuard, зберігаючи глобальний стек повернень, який зберігає всі адреси повернень (EIP). Ця настройка забезпечує, що будь-яке переповнення не завдасть шкоди, оскільки вона дозволяє порівняння збережених та фактичних адрес повернень для виявлення випадків переповнення. Крім того, StackShield може перевіряти адресу повернення на межеве значення, щоб виявити, чи вказує EIP за межі очікуваного простору даних. Однак цей захист можна обійти за допомогою технік, таких як Return-to-libc, ROP (Return-Oriented Programming) або ret2ret, що свідчить про те, що StackShield також не захищає локальні змінні.
Stack Smash Protector (ProPolice) -fstack-protector
:
-fstack-protector
:Цей механізм розміщує канарейку перед EBP та переорганізовує локальні змінні так, щоб буфери знаходилися на вищих адресах пам'яті, що запобігає їх перезаписуванню іншими змінними. Він також безпечно копіює аргументи, передані на стек вище локальних змінних, і використовує ці копії як аргументи. Однак він не захищає масиви з менш ніж 8 елементами або буфери в структурі користувача.
Канарейка - це випадкове число, отримане з /dev/urandom
або значення за замовчуванням 0xff0a0000
. Вона зберігається в TLS (Thread Local Storage), що дозволяє спільним просторам пам'яті між потоками мати потокові глобальні або статичні змінні. Ці змінні спочатку копіюються з батьківського процесу, і дочірні процеси можуть змінювати їх дані, не впливаючи на батька або сестер. Однак, якщо використовується fork()
без створення нової канарейки, всі процеси (батько та діти) ділять одну й ту саму канарейку, що робить її вразливою. На архітектурі i386 канарейка зберігається за адресою gs:0x14
, а на x86_64 - за адресою fs:0x28
.
Ця локальна захист ідентифікує функції з буферами, які вразливі до атак, та впроваджує код на початку цих функцій для розміщення канарейки, а в кінці - для перевірки її цілісності.
Коли веб-сервер використовує fork()
, це дозволяє проводити атаку методом брутфорсу для вгадування байта канарейки по одному. Однак використання execve()
після fork()
перезаписує простір пам'яті, анулюючи атаку. vfork()
дозволяє дочірньому процесу виконуватися без дублювання до тих пір, поки він не спробує записати, після чого створюється дубльований процес, пропонуючи інший підхід до створення процесу та роботи з пам'яттю.
Довжини
У бінарних файлах x64
куки канарейки - це 0x8
байт qword. Перші сім байтів є випадковими, а останній байт - нульовий.
У бінарних файлах x86
куки канарейки - це 0x4
байт dword. Перші три байти є випадковими, а останній байт - нульовий.
Найменш значущий байт у обох канарейках - це нульовий байт, оскільки він буде першим у стеку, що йде з менших адрес, і тому функції, які читають рядки, зупиняться перед його читанням.
Обхід
Витікання канарейки та подальше перезаписування її (наприклад, переповнення буфера) її власним значенням.
Якщо канарейка розгалужена в дочірніх процесах, можливо брутфорсити її по одному байту:
Якщо в бінарному файлі є якась цікава витік або довільна уразливість для читання, можливо її витікати:
Перезапис вказівників, збережених у стеку
Стек, який вразливий до переповнення стеку, може містити адреси рядків або функцій, які можна перезаписати, щоб експлуатувати уразливість без необхідності досягнення канарейки стеку. Перевірте:
Модифікація як майстер та канарейки потоку
Переповнення буфера в потоковій функції, захищеній канарейкою, може бути використане для модифікації майстер-канарейки потоку. В результаті захист стає некорисним, оскільки перевірка використовується з двома канарейками, які є однаковими (хоча зміненими).
Більше того, переповнення буфера в потоковій функції, захищеній канарейкою, може бути використане для модифікації майстер-канарейки, збереженої в TLS. Це тому, що можливо досягти позиції пам'яті, де зберігається TLS (і, отже, канарейка) через bof в стеку потоку. В результаті захист стає некорисним, оскільки перевірка використовується з двома канарейками, які є однаковими (хоча зміненими). Цю атаку виконано в описі: http://7rocky.github.io/en/ctf/htb-challenges/pwn/robot-factory/#canaries-and-threads
Перевірте також презентацію https://www.slideshare.net/codeblue_jp/master-canary-forging-by-yuki-koike-code-blue-2015, в якій зазначено, що зазвичай TLS зберігається за допомогою mmap
, і коли створюється стек потоку, він також генерується за допомогою mmap
, що може дозволити переповнення, як показано в попередньому описі.
Зміна запису GOT для
__stack_chk_fail
Якщо бінарний файл має Partial RELRO, то можна використовувати довільний запис для зміни GOT-запису __stack_chk_fail
на функцію-пустун, яка не блокує програму, якщо канарейка змінена.
Цю атаку виконано в описі: https://7rocky.github.io/en/ctf/other/securinets-ctf/scrambler/
Посилання
Last updated