CVE-2023-20938
English
Анализ уязвимости
- Клиент A и клиент B устанавливают Binder-соединение через менеджер сервисов servicemanager.
- A создаёт узел 0xbeef (node = binder_new_node(proc, fp);), B ссылается на узел 0xbeef через ref->target_node.
- B корректно обрабатывает target_node 0xbeef, помещая транзакцию binder_transaction, ссылающуюся на target_node 0xbeef, в очередь транзакций A (binder_enqueue_work_ilocked(&t->work, &proc->todo);), что позволяет A впоследствии повторно обратиться к освобождённому висячему указателю.
- B использует невыровненный offsets_size, что приводит к входу в код обработки ошибок (IS_ALIGNED(tr->offsets_size, sizeof(binder_size_t))). Из-за этого buffer_offset, равный 0, передаётся в функцию binder_transaction_buffer_release, что инициирует уязвимость.
- Функция binder_transaction_buffer_release через binder_dec_node вызывает binder_dec_node_nilocked, уменьшая счётчик ссылок local_strong_refs узла 0xbeef. Однако в экземпляре узла имеется несколько счётчиков ссылок и списков ссылок; освобождение происходит только когда все эти счётчики обнуляются.
- Закрытие Binder процесса B вызывает функцию binder_cleanup_ref_olocked. При этом снова вызывается binder_dec_node_nilocked. Если binder_dec_node_nilocked возвращает true, указатель ref->node на узел (0xbeef) не обнуляется.
- Затем будет вызвана binder_free_ref. Внутри этой функции узел 0xbeef освобождается.
- В функции binder_thread_read процесс A снова обращается к освобождённому узлу 0xbeef.
Дополнительное пояснение:
В реальной среде Android обычное приложение не имеет прав на регистрацию служб через servicemanager, но можно использовать ITokenManager для установления связи между двумя процессами. Данный тестовый пример использует именно ITokenManager.