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

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

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

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

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

Категории

Все категории
Loading categories
empty_list — empty_list — эксплойт для p0 issue 1564 (CVE-2018-4243): чтение/запись ядра iOS 11.0 - 11.3.1 | Kitploit
Инструменты/GitHubGitHub/jailbreaks/empty_list
Безопасность iOSКриминалистика памятиАнализ уязвимостейЭксплуатацияПост-эксплуатацияЭксплуатация Бинарных Файлов
GitHubjailbreaks/empty_list

empty_list

empty_list — эксплойт для p0 issue 1564 (CVE-2018-4243): чтение/запись ядра iOS 11.0 - 11.3.1

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

Популярное

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

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

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

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

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

empty_list - эксплойт для p0 issue 1564 (CVE-2018-4243) iOS 11.0 - 11.3.1, r/w ядра @i41nbeer

ОШИБКА: getvolattrlist принимает управляемый пользователем аргумент bufferSize через системный вызов fgetattrlist.

При выделении буфера ядра для сериализации списка атрибутов имеется следующий комментарий:

/*

  • Allocate a target buffer for attribute results.
  • Note that since we won't ever copy out more than the caller requested,
  • we never need to allocate more than they offer. */ ab.allocated = ulmin(bufferSize, fixedsize + varsize); if (ab.allocated > ATTR_MAX_BUFFER) { error = ENOMEM; VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab.allocated, ATTR_MAX_BUFFER); goto out; } MALLOC(ab.base, char *, ab.allocated, M_TEMP, M_ZERO | M_WAITOK);

Проблема в том, что код затем некорректно обрабатывает случай, когда предоставленный пользователем размер буфера меньше запрошенного размера заголовка. Если передать ATTR_CMN_RETURNED_ATTRS, мы попадём на следующий код:

/* Return attribute set output if requested. / if (return_valid) { ab.actual.commonattr |= ATTR_CMN_RETURNED_ATTRS; if (pack_invalid) { / Only report the attributes that are valid */ ab.actual.commonattr &= ab.valid.commonattr; ab.actual.volattr &= ab.valid.volattr; } bcopy(&ab.actual, ab.base + sizeof(uint32_t), sizeof (ab.actual)); }

Нет проверки того, что выделенного буфера достаточно хотя бы для этого.

Эксплуатация: Надеюсь опубликовать более развёрнутый отчёт об этой уязвимости; ниже — краткие заметки о том, как работает эксплойт:

Эта ошибка даёт возможность записать 8 нулевых байтов за конец выделения в kalloc.16. Хотя похоже, что вы можете контролировать несколько битов в этих байтах, я не уверен, что это действительно так, поэтому я сосредоточился на эксплуатации так, как если бы это была запись NULL-указателя за конец.

Это довольно ограниченный примитив, поэтому первый шаг — перечислить возможные действия:

  • воздействовать на счётчик ссылок, пытаясь превратить переполнение в баг типа UaF
  • воздействовать на блокировку, пытаясь превратить переполнение в состояние гонки
  • воздействовать на указатель, пытаясь раскрыть счётчик ссылок
  • воздействовать на валидируемую структуру данных, где 0 — интересное значение для изменения

В итоге я выбрал первый вариант. Тогда появляются два дополнительных требования:

  • цель должна иметь счётчик ссылок в первых 8 байтах
  • в цель должно быть возможно переполниться из kalloc.16

Я решил целиться в struct ipc_port, у которого счётчик ссылок находится во втором dword, что удовлетворяет первому требованию. Однако он выделяется не в kalloc.16, а в собственной зоне (ipc_ports).

Это означает, что нам нужно выровнять блок зоны kalloc.16 непосредственно перед блоком ipc_ports, затем переполниться из последнего выделения kalloc.16 в блоке kalloc.16 в первое выделение в блоке ipc_ports.

Есть два приёма, которые облегчают задачу:

  1. реверсирование списка свободных блоков (freelist)
  2. безопасные для переполнения выделения

Реверсирование списка свободных блоков: Выделения из зоны в первую очередь берутся из промежуточных (частично заполненных) страниц. Это значит, что если мы начнём освобождать и выделять объекты k.16 где-то в середине груминга, они не будут переиспользованы, пока текущая промежуточная страница не станет полностью заполненной или пустой.

Это создаёт сложность, потому что списки свободных блоков свежих страниц заполняются полу-случайным образом, так что их выделения идут изнутри наружу:

| 9 8 6 5 2 1 3 4 7 10 | <-- example "randomized" allocation order from a fresh all-free page

Это означает, что наши итоговые промежуточные страницы k.16 и ports будут выглядеть примерно так:

| - - - 5 2 1 3 4 - - | - - - 4 1 2 3 5 - - | kalloc.16 ipc_ports

Если использовать переполнение для повреждения записи в списке свободных блоков, при её выделении произойдёт паника, поэтому нам нужно этого избегать.

Трюк в том, что, контролируя порядок выделения и освобождения, мы можем развернуть списки свободных блоков так, что итоговые промежуточные страницы будут выглядеть скорее так:

| 1 4 - - - - - 5 3 2 | 2 5 - - - - - 4 3 1 | kalloc.16 ipc_ports

На этом этапе гораздо вероятнее, что мы сможем освободить kalloc.16 и перевыделить его для переполнения так, чтобы попасть в первое qword объекта ipc_port.

Безопасные для переполнения выделения: поскольку, скорее всего, будет много кандидатов на переполнение, прежде чем мы попадём в целевую (которая находится в самом конце, непосредственно перед ipc_port), нужно убедиться, что выделенные объекты на странице kalloc.16 безопасно повреждать NULL-указателем.

Для этого я использую дескрипторы ool_port в mach message, поскольку NULL — допустимое значение.

Ход эксплуатации: Мы выполняем груминг, чтобы развернуть списки свободных блоков kalloc.16, и начинаем пытаться переполниться в ipc_port.

Мы знаем приблизительный диапазон имён mach-портов, содержащих порт, который будет повреждён; после каждой попытки переполнения мы проверяем каждый из этих портов, чтобы узнать, был ли порт повреждён. Побочный эффект успешного повреждения — флаг io_active порта становится равным нулю. Мы можем обнаружить это без побочных эффектов с помощью метода MIG mach_port_kobject.

Как только мы находим повреждённый порт, нам нужно вызвать взятие и сброс ссылки на него; и, что более важно, нам нужно, чтобы путь выполнения, который это делает, не проверял флаг io_active. Это сделает за нас mach_port_set_attributes.

Теперь мы превратили запись NULL-указателя за конец kalloc.16 в висячий mach-порт :)

Мы запускаем сборку мусора зоны (zone gc), стремясь получить повторное использование памяти порта как страницы kalloc.4096. Сначала добиваемся её повторного использования в качестве дескриптора ool_ports, где поле ip_context перекрывается с send-right, который мы отправляем себе на канареечный порт. Это позволяет нам узнать приблизительный адрес наших объектов в ядре. Затем мы заменяем ool_desc на pipe-буфер, и, немного повозившись, можем вычислить, где в памяти находится висячий mach-порт.

Мы создаём там поддельный порт задачи ядра (kernel task port) и затем прибираем за собой.

Надёжность: Эксплойт действительно работает, что и было моей целью :) Надёжность — где-то порядка 30%, всё зависит от того, насколько быстро вы можете выполнить начальное переполнение и цикл проверки. Если что-то ещё вмешается и начнёт выделять или освобождать память в kalloc.16, вы увеличиваете вероятность того, что повредите запись в списке свободных блоков или что-то ещё, и произойдёт паника.

Я уверен, что эксплойт можно сделать более надёжным; я довёл его лишь до точки, где продемонстрировал, что эта ошибка эксплуатируема. Если вы хотите взять это за отправную точку и показать, как повысить надёжность, я с удовольствием прочитаю пост в блоге! Думаю, для этого нужно реально отслеживать выделения в kalloc.16 и понимать, каковы сценарии отказа и как их предотвратить.

Процент успеха, похоже, максимален, когда устройство перезагружено и некоторое время простаивает.

Очистка: Если эксплойт сработает, он должен убрать за собой и не вызвать панику устройства. Поддельный порт задачи ядра останется живым.

Используйте функции из kmem.h для чтения и записи памяти ядра. Сохраните там send-right на tfp0, если хотите сохранить доступ к памяти ядра после завершения этого процесса.

Я тестировал на: iPod Touch 6G, iPhone 6S, iPhone SE, iPhone 7, iPhone 8 Должно работать на iOS 11 и до iOS 11.3.1 включительно.

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