
Эксплойт LPE уровня proof-of-concept для Android Binder UAF, который использует iovec spraying и перезапись addr_limit для достижения произвольного чтения/записи в ядре.
Переработанный Proof-of-Concept / эксплойт для локального повышения привилегий (LPE), нацеленный на CVE-2019-2215, уязвимость типа use-after-free (использование после освобождения) в драйвере Android Binder.
Я предоставил уязвимую сборку ядра в папке vulnerable_kernel_builds. Создайте эмулятор AOSP с Android 10 и запустите ядро с ней
emulator -show-kernel -no-window -no-snapshot -wipe-data -avd research -kernel bzImage
С техническими деталями уязвимости можно ознакомиться в этом замечательном блоге: https://projectzero.google/2019/11/bad-binder-android-in-wild-exploit.html
Структура task_struct содержит важный член addr_limit типа mm_segment_t. addr_limit хранит самый старший допустимый адрес пользовательского пространства. addr_limit является частью struct thread_info или struct thread_struct в зависимости от целевой архитектуры. Поскольку мы сейчас имеем дело с 64-битной системой x86_64, addr_limit определён в struct thread_struct.

Если нам удастся перезаписать этот addr_limit значением 0xFFFFFFFFFFFFFFFF, мы сможем читать и записывать в любую часть памяти пространства ядра. Для лучшей совместимости эксплойта на x86_64 и arm64 лучше установить addr_limit в 0xFFFFFFFFFFFFFFFE
struct iovec используется для векторного ввода-вывода (Vectored I/O), также известного как Scatter/Gather I/O. Одна из основных проблем struct iovec заключается в том, что они недолговечны. Они выделяются системными вызовами при работе с буферами и немедленно освобождаются при возврате в пользовательский режим.
Мы хотим, чтобы структура iovec оставалась в ядре, когда мы инициируем операцию unlink и перезаписываем указатель iov_base адресом binder_thread->wait.head, чтобы получить ограниченное чтение и запись. Один из способов — использовать системные вызовы, такие как readv, writev, на файловом дескрипторе pipe, поскольку они могут блокироваться, если pipe полон или пуст. pipe — это однонаправленный канал данных, который можно использовать для межпроцессного взаимодействия. Блокирующая особенность pipe даёт нам значительное временное окно для повреждения структуры iovec в пространстве ядра.
Точно так же мы можем использовать системный вызов recvmsg для блокировки, передав MSG_WAITALL в качестве параметра флага.
Так как размер структуры binder_thread составляет 408 байт, она попадёт в кэш kmalloc-512.

нам потребуется уложить в стек 25 iovec структур, чтобы перераспределить висячий фрагмент. 408 / 16 = 25.5

Как мы видим из изображения выше, iovecStack[10].iov_len и iovecStack[11].iov_base будут перезаписаны.

Итак, нам нужно обработать iovecStack[10], заблокировать системный вызов writev, а затем инициировать операцию unlink. Это гарантирует, что когда iovecStack[11].iov_base будет перезаписан, мы возобновим системный вызов writev. Затем, наконец, выполним утечку содержимого фрагмента binder_thread обратно в пользовательское пространство и прочитаем из него указатель на task_struct.

Для достижения ограниченного write мы будем использовать системный вызов recvmsg для блокировки, передав MSG_WAITALL в качестве параметра флага. Системный вызов recvmsg может блокироваться так же, как и системный вызов writev.

Поскольку размер mm_segment_t составляет 0x8 байт, мы хотим перезаписать его значением 0xFFFFFFFFFFFFFFFE, так как это самый старший допустимый адрес пространства ядра, и это не приведёт к краху процесса при возникновении страничного сбоя в системе arm64.

