iOS Exploiting
Physical use-after-free
Це резюме з посту з https://alfiecg.uk/2024/09/24/Kernel-exploit.html, крім того, додаткову інформацію про експлойт, що використовує цю техніку, можна знайти в https://github.com/felix-pb/kfd
Memory management in XNU
Віртуальний адресний простір пам'яті для користувацьких процесів на iOS охоплює від 0x0 до 0x8000000000. Однак ці адреси не відображаються безпосередньо на фізичну пам'ять. Натомість, ядро використовує таблиці сторінок для перетворення віртуальних адрес на фактичні фізичні адреси.
Levels of Page Tables in iOS
Таблиці сторінок організовані ієрархічно на трьох рівнях:
L1 Page Table (Рівень 1):
Кожен запис тут представляє великий діапазон віртуальної пам'яті.
Він охоплює 0x1000000000 байт (або 256 ГБ) віртуальної пам'яті.
L2 Page Table (Рівень 2):
Запис тут представляє меншу область віртуальної пам'яті, а саме 0x2000000 байт (32 МБ).
Запис L1 може вказувати на таблицю L2, якщо він не може відобразити весь регіон самостійно.
L3 Page Table (Рівень 3):
Це найдрібніший рівень, де кожен запис відображає одну 4 КБ сторінку пам'яті.
Запис L2 може вказувати на таблицю L3, якщо потрібен більш детальний контроль.
Mapping Virtual to Physical Memory
Пряме відображення (Блокове відображення):
Деякі записи в таблиці сторінок безпосередньо відображають діапазон віртуальних адрес на безперервний діапазон фізичних адрес (як скорочення).
Вказівник на дочірню таблицю сторінок:
Якщо потрібен більш детальний контроль, запис на одному рівні (наприклад, L1) може вказувати на дочірню таблицю сторінок на наступному рівні (наприклад, L2).
Example: Mapping a Virtual Address
Припустимо, ви намагаєтеся отримати доступ до віртуальної адреси 0x1000000000:
L1 Table:
Ядро перевіряє запис таблиці L1, що відповідає цій віртуальній адресі. Якщо він має вказівник на таблицю L2, воно переходить до цієї таблиці L2.
L2 Table:
Ядро перевіряє таблицю L2 для більш детального відображення. Якщо цей запис вказує на таблицю L3, воно продовжує туди.
L3 Table:
Ядро шукає фінальний запис L3, який вказує на фізичну адресу фактичної сторінки пам'яті.
Example of Address Mapping
Якщо ви запишете фізичну адресу 0x800004000 у перший індекс таблиці L2, тоді:
Віртуальні адреси від 0x1000000000 до 0x1002000000 відображаються на фізичні адреси від 0x800004000 до 0x802004000.
Це блокове відображення на рівні L2.
Альтернативно, якщо запис L2 вказує на таблицю L3:
Кожна 4 КБ сторінка у віртуальному адресному діапазоні 0x1000000000 -> 0x1002000000 буде відображена окремими записами в таблиці L3.
Physical use-after-free
Фізичне використання після звільнення (UAF) відбувається, коли:
Процес виділяє певну пам'ять як читабельну та записувану.
Таблиці сторінок оновлюються, щоб відобразити цю пам'ять на конкретну фізичну адресу, до якої процес може отримати доступ.
Процес звільняє пам'ять.
Однак, через помилку, ядро забуває видалити відображення з таблиць сторінок, хоча воно позначає відповідну фізичну пам'ять як вільну.
Ядро може потім перевиділити цю "звільнену" фізичну пам'ять для інших цілей, таких як дані ядра.
Оскільки відображення не було видалено, процес все ще може читати та записувати в цю фізичну пам'ять.
Це означає, що процес може отримати доступ до сторінок пам'яті ядра, які можуть містити чутливі дані або структури, що потенційно дозволяє зловмиснику маніпулювати пам'яттю ядра.
Exploitation Strategy: Heap Spray
Оскільки зловмисник не може контролювати, які конкретні сторінки ядра будуть виділені для звільненої пам'яті, вони використовують техніку, звану heap spray:
Зловмисник створює велику кількість об'єктів IOSurface у пам'яті ядра.
Кожен об'єкт IOSurface містить магічне значення в одному з його полів, що полегшує його ідентифікацію.
Вони сканують звільнені сторінки, щоб перевірити, чи потрапив якийсь з цих об'єктів IOSurface на звільнену сторінку.
Коли вони знаходять об'єкт IOSurface на звільненій сторінці, вони можуть використовувати його для читання та запису пам'яті ядра.
Більше інформації про це в https://github.com/felix-pb/kfd/tree/main/writeups
Step-by-Step Heap Spray Process
Spray IOSurface Objects: Зловмисник створює багато об'єктів IOSurface з особливим ідентифікатором ("магічне значення").
Scan Freed Pages: Вони перевіряють, чи були виділені якісь з об'єктів на звільненій сторінці.
Read/Write Kernel Memory: Маніпулюючи полями в об'єкті IOSurface, вони отримують можливість виконувати произвольні читання та записи в пам'яті ядра. Це дозволяє їм:
Використовувати одне поле для читання будь-якого 32-бітного значення в пам'яті ядра.
Використовувати інше поле для запису 64-бітних значень, досягаючи стабільного примітиву читання/запису ядра.
Генерувати об'єкти IOSurface з магічним значенням IOSURFACE_MAGIC для подальшого пошуку:
Шукайте IOSurface
об'єкти на одній звільненій фізичній сторінці:
Досягнення читання/запису ядра з IOSurface
Після отримання контролю над об'єктом IOSurface в пам'яті ядра (відображеним на звільнену фізичну сторінку, доступну з простору користувача), ми можемо використовувати його для произвольних операцій читання та запису в ядрі.
Ключові поля в IOSurface
Об'єкт IOSurface має два важливих поля:
Вказівник на кількість використань: Дозволяє 32-бітне читання.
Вказівник на індексований часовий штамп: Дозволяє 64-бітний запис.
Перезаписуючи ці вказівники, ми перенаправляємо їх на произвольні адреси в пам'яті ядра, що дозволяє можливості читання/запису.
32-Бітне читання з ядра
Щоб виконати читання:
Перезапишіть вказівник на кількість використань, щоб він вказував на цільову адресу мінус зсув 0x14 байт.
Використовуйте метод
get_use_count
, щоб прочитати значення за цією адресою.
64-Бітний Ядро Запис
Щоб виконати запис:
Перезапишіть індексований вказівник часу на цільову адресу.
Використовуйте метод
set_indexed_timestamp
, щоб записати 64-бітне значення.
Резюме потоку експлуатації
Виклик фізичного використання після звільнення: Вільні сторінки доступні для повторного використання.
Розподіл об'єктів IOSurface: Виділити багато об'єктів IOSurface з унікальним "магічним значенням" у пам'яті ядра.
Визначити доступний IOSurface: Знайти IOSurface на звільненій сторінці, якою ви керуєте.
Зловживання використанням після звільнення: Змінити вказівники в об'єкті IOSurface, щоб дозволити довільне читання/запис ядра через методи IOSurface.
З цими примітивами експлуатація забезпечує контрольовані 32-бітні читання та 64-бітні записи в пам'ять ядра. Подальші кроки джейлбрейка можуть включати більш стабільні примітиви читання/запису, які можуть вимагати обходу додаткових захистів (наприклад, PPL на новіших пристроях arm64e).
Last updated