Найти в Дзене
mamaich

Патчим ядро Андроид чтобы стать рутом

Ранее я рассказал, как отключить Selinux, пропатчив ядро. Теперь опишу второй шаг - как пропатчить ядро, чтобы иметь возможность, в нужный нам момент, поднять приоритет своего процесса до root.

Как известно, большинство эксплоитов "превращают" текущий процесс в процесс с uid=0, gid=0 с помощью вызова ядром такой конструкции:

commit_creds(prepare_kernel_cred(NULL));

Почитать как это работает можно, например, тут.
Почему-то
commit_creds(&init_cred) (тоже встречается в некоторых эксплоитах) у меня не заработало.

Идея следующая: найти в ядре редкоиспользуемую функцию, пропатчить ее, чтобы она вызывала commit_creds(...) и обратиться к ней из своей программы.

Какую функцию выбрать, я "подсмотрел" в эксплоитах от securitylab. Они выбрали sel_read_enforce, которая вызывается при чтении файла "/sys/fs/selinux/enforce".

Вот так функция выглядит в оригинале:

Кроме адреса этой функции, нужны адреса commit_creds:

-2

и prepare_kernel_cred:

-3

Так как я не силен в ARM64 ассемблере - то код своего эксплоита пишу на C:

-4

16-ричные константы, это адреса commit_creds и prepare_kernel_cred соответственно. Благо, ядро у нас всегда грузится по одному адресу, и константы можно просто захардкодить.

Откуда взялась проверка "count==123"?
В процессе загрузки, компоненты андроида сами обращаются к файлу "/sys/fs/selinux/enforce", и если я подниму им права до root, то операционная система остановит загрузку. Так что я добавил данную проверку, чтобы эскалация прав происходила только для программ, передавших "магический" размер буфера в 123 байта.

Компилируем:

aarch64-linux-android30-clang -Os -S ./code.c
aarch64-linux-android-as ./code.s
aarch64-linux-android-objdump -d ./a.out

Компилируем C файл с ключом "-Os", чтобы получить наиболее компактный код. Ключи, включающие генерацию position independent code не обязательны, так как код ARM64 по умолчанию получается позиционно независимый.
Скомпилированные инструкции выглядят так:

-5

Вставляем эти байты в образ ядра по адресу sel_read_enforce, перезагружаемся.

Делаем cat "/sys/fs/selinux/enforce" или вызываем getenforce - получаем ошибку. Ошибка вполне ожидаема, и возникает из-за того, что функция sel_read_enforce в реальности ничего не записывает в буфер, а возвращает, что прочитано 0 байт. На загрузку операционной системы такое поведение никак не влияет.

Теперь нам нужно запустить программу, которая стриггерит эксплоит:

-6

Компилируем, запускаем:

-7

Проверяем - закрытые ранее разделы нам теперь доступны.