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

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

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

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

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

Категории

Все категории
Loading categories
CVE-2017-2370 — на Mac 10.12.2 | Kitploit
Инструменты/GitHubGitHub/peterpan0927/cve-2017-2370
Повышение привилегийБезопасность iOSКриминалистика памятиЭксплуатацияСбор информацииЭксплуатация Бинарных Файлов
GitHubpeterpan0927/cve-2017-2370

CVE-2017-2370

на Mac 10.12.2

Репозиторий
203178 лет назадПроверено Kitploit

Популярное

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

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

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

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

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

0x00. Введение

На этот раз для повышения привилегий используется уязвимость в mach_voucher_extract_attr_recipe_trap, а основной метод эксплуатации — через сообщения MACH_MSG_OOL_PORTS_DESCRIPTOR.

Если кратко о mach_msg ool: при отправке msg, содержащего ool descriptor, ядро копирует указанные данные из пользовательского пространства в пространство ядра, и ядро хранит эти данные до тех пор, пока целевая задача не обработает сообщение. Аналогично, когда целевой процесс получает сообщение с ool descriptor, ядро копирует данные из пространства ядра в пользовательское пространство (не обязательно реальное копирование). Поэтому эту технику можно использовать для записи данных в кучу ядра или чтения данных из ядра.

Поскольку эксплуатация этой уязвимости значительно сложнее, чем Trident, я буду разбирать её шаг за шагом: от точки возникновения уязвимости до поэтапной эксплуатации. Код можно найти в моём github.

0x01. Точка возникновения уязвимости

Среди новых функций, добавленных в iOS 10 и macOS 10.12, есть функция под названием mach_voucher_extract_attr_recipe_trap, которая является Mach trap, вызываемым из песочницы. Исходный код этой функции:

kern_return_t
  mach_voucher_extract_attr_recipe_trap(struct mach_voucher_extract_attr_recipe_args *args)
  {
    ipc_voucher_t voucher = IV_NULL;
    kern_return_t kr = KERN_SUCCESS;
    mach_msg_type_number_t sz = 0;
	// Копируем адрес recipe_size в sz, теперь sz содержит значение kalloc_size
    if (copyin(args->recipe_size, (void *)&sz, sizeof(sz)))     <---------- (a)
      return KERN_MEMORY_ERROR;

    if (sz > MACH_VOUCHER_ATTR_MAX_RAW_RECIPE_ARRAY_SIZE)
      return MIG_ARRAY_TOO_LARGE;

    voucher = convert_port_name_to_voucher(args->voucher_name);
    if (voucher == IV_NULL)
      return MACH_SEND_INVALID_DEST;

    mach_msg_type_number_t __assert_only max_sz = sz;

    if (sz < MACH_VOUCHER_TRAP_STACK_LIMIT) {
      /* сохраняем небольшие рецепты на стеке для скорости */
      uint8_t krecipe[sz];
      if (copyin(args->recipe, (void *)krecipe, sz)) {
        kr = KERN_MEMORY_ERROR;
        goto done;
      }
      kr = mach_voucher_extract_attr_recipe(voucher, args->key,
                                            (mach_voucher_attr_raw_recipe_t)krecipe, &sz);
      assert(sz <= max_sz);

      if (kr == KERN_SUCCESS && sz > 0)
        kr = copyout(krecipe, (void *)args->recipe, sz);
    } else {
      uint8_t *krecipe = kalloc((vm_size_t)sz);                 <---------- (b)
      if (!krecipe) {
        kr = KERN_RESOURCE_SHORTAGE;
        goto done;
      }

      if (copyin(args->recipe, (void *)krecipe, args->recipe_size)) {         <----------- (c)
        kfree(krecipe, (vm_size_t)sz);
        kr = KERN_MEMORY_ERROR;
        goto done;
      }

      kr = mach_voucher_extract_attr_recipe(voucher, args->key,
                                            (mach_voucher_attr_raw_recipe_t)krecipe, &sz);
      assert(sz <= max_sz);

      if (kr == KERN_SUCCESS && sz > 0)
        kr = copyout(krecipe, (void *)args->recipe, sz);
      kfree(krecipe, (vm_size_t)sz);
    }

    kr = copyout(&sz, args->recipe_size, sizeof(sz));

  done:
    ipc_voucher_release(voucher);
    return kr;
  }
  1. Анализ показывает, что в точке (a) 4-байтовый указатель пользовательского пространства args->recipe_size записывается в sz.
  2. В точке (b), если размер sz находится между MACH_VOUCHER_ATTR_MAX_RAW_RECIPE_ARRAY_SIZE (5120) и MACH_VOUCHER_TRAP_STACK_LIMIT (256), то в соответствии со значением sz выделяется буфер в куче ядра.
  3. В точке (c) память из пользовательского пространства копируется в только что выделенную область, но размер копирования передаётся не sz, использованный для выделения кучи, а указатель из пользовательского пространства. Таким образом возникает переполнение кучи, которое мы используем для атаки. Кроме того, функция copyin имеет особенность: при встрече с unmapped страницей она прекращает копирование. Эта особенность будет использована в нашем poc:

copyin

0x01. Этапы эксплуатации

  1. Прежде всего необходимо сделать кучу управляемой. Здесь мы используем технику Heap Feng Shui, так как после рандомизации freelist мы не знаем положение заново выделенных блоков памяти.

Сначала нужно разобраться, как ядро обрабатывает MACH_MSG_OOL_PORTS_DESCRIPTOR в mach msg. Когда ядро получает сложное сообщение и обнаруживает ports descriptor, оно передаёт его функции ipc_kmsg_copyin_ool_ports_descriptor (вызываемой из ipc_kmsg_copyin), которая читает все объекты port. Эта функция вызывает kalloc для выделения необходимой памяти (в 64-битной системе выделяемый размер в 2 раза больше входного, длина name — 4 байта), затем преобразует валидные port из name в реальный адрес объекта ipc_port и сохраняет их. Для name, равного MACH_PORT_NULL или MACH_PORT_DEAD, они остаются без изменений.

/* calculate length of data in bytes, rounding up */
if (os_mul_overflow(count, sizeof(mach_port_t), &ports_length)) { 
	*mr = MACH_SEND_TOO_LARGE; 
	return NULL; 
} 

if (os_mul_overflow(count, sizeof(mach_port_name_t), &names_length)) { 
    *mr = MACH_SEND_TOO_LARGE;
	return NULL; 
} 

if(ports_length == 0){
    return user_desc;
}

data = kalloc(ports_length); // Выделение пространства 
... 
objects = (ipc_object_t *) data; 

dsc->address = data; 

for ( i = 0; i < count; i++) { 
    mach_port_name_t name = names[i]; 
    ipc_object_t object;
    if (!MACH_PORT_VALID(name)) {
        objects[i] = (ipc_object_t)CAST_MACH_NAME_TO_PORT(name);// IPC_PORT_DEAD continue; 
    } 
...
}

Поэтому при атаке мы будем отправлять большое количество MACH_PORT_DEAD, заполняя область памяти значением 0xFFFFFFFFFFFFFFFF (MACH_PORT_DEAD), затем инициируем уязвимость, изменяя один из IPC_PORT_DEAD на заранее подготовленную нами область памяти. Если эта область является валидной структурой ipc port, то после приёма сообщения OOL PORTS мы сможем в пользовательском пространстве получить port name, соответствующий этому ipc_port, и продолжить атаку.

Heap Feng Shui

  1. Конструирование объекта ipc_object

Сначала мы получили этот fake port. Для дальнейшей утечки информации необходимо знать, по каким параметрам ядро обрабатывает его по-разному. Рассмотрим структуру ipc_port:

struct ipc_port {
	// Указатель на ipc_object находится в первых восьми байтах, это объект нашей атаки переполнением
	struct ipc_object ip_object; // тип объекта port struct ipc_mqueue,ip_messages;
	struct ipc_mqueue ip_messages; // очередь сообщений
	union {
               struct ipc_space *receiver;
               struct ipc_port *destination;
               ipc_port_timestamp_t timestamp;
    }data;
	union {
    	ipc_importance_task_t imp_task;
    	ipc_kobject_t kobject; // объект ядра, соответствующий port
    	uintptr_t alias;
	}kdata;
	...
} __attribute__((__packed__));

Один из них — объект ядра, соответствующий порту. Тип объекта ядра, которому соответствует ipc_port, определяется свойствами ipc_object. Таким образом, мы фактически конструируем ipc_object.

fakeport->io_bits = IO_BITS_ACTIVE | IKOT_CLOCK; // устанавливаем как объект IKOT_CLOCK и активируем
fakeport->io_lock_data[12] = 0x11;	// устанавливаем блокировку порта активной, чтобы избежать deadlock

Ядро будет воспринимать этот ipc_port как порт для связи с объектом IKOT_CLOCK. Следующая цель — утечка базового адреса ядра.

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