Android: security-чип в смартфоне Pixel 3, защита от ROP в ядре и уроки фаззинга

0
15

Содержание статьи

Сегодня в выпуске: новый security-чип в смартфоне Pixel 3, защита от атак с использованием ROP в ядре Linux, уроки фаззинга, смартфон, ворующий рекламные деньги, прошлое, настоящее и будущее пакетов APK. А также несколько статей для программистов: управление приоритетами потоков, разбор системы типов Kotlin и рассказ о Contracts — новой функции Kotlin 1.3.

Инструменты

  • android-device-check — скрипт для проверки настроек безопасности смартфона на Android;
  • AES Killer — плагин Burp Suite для расшифровки трафика мобильных приложений с помощью ключа, извлеченного из приложения;
  • awesome-iot-hacks — коллекция ссылок на информацию о багах различных IoT-устройств.

Почитать

Titan M: security-чип в смартфонах Pixel

Building a Titan: Better security through a tiny chip — рассказ о security-чипе Titan M, который используется в смартфонах Pixel 3 и Pixel 3 XL. Чип разработан и производится самой Google, а в число его функций входят:

  • хранение счетчиков откатов, используемых системой доверенной загрузки Android Verified Boot;
  • хранение секретных данных и ограничение попыток доступа к ним с помощью Weaver API;
  • реализация функций модуля Strongbox Keymaster, который отвечает в том числе за хранение и генерацию ключей шифрования, а также за функцию Android Protected Confirmation, позволяющую математически доказать, что пользователь действительно увидел тот или иной диалог подтверждения и что ответ на этот диалог не был перехвачен и каким-либо образом изменен;
  • обеспечение работы механизмов защиты от сброса до заводских настроек, который не позволяет третьим сторонам использовать потерянный или украденный смартфон.

По сути, Titan M — это аналог Secure Enclave, который Apple уже несколько лет предустанавливает в свои смартфоны. Благодаря тому что Titan M — это выделенный микрокомпьютер (на базе ARM Cortex-M3), не связанный с основным процессором, он гораздо более устойчив к любым атакам, включая «неисправляемые» Rowhammer, Spectre и Meltdown.

Google обещает открыть код прошивки Titan M в скором времени.

Titan (слева) и Titan M (справа)Titan (слева) и Titan M (справа)

Control Flow Integrity для ядра Linux

Control Flow Integrity in the Android kernel — статья разработчиков Android о применении технологии Control Flow Integrity для защиты ядра Linux от атак с использованием метода ROP (Return Oriented Programming).

Современные эксплоиты в своей работе часто полагаются на модификацию указателей на функцию и адресов возврата. Это позволяет обойти ограничение на исполнение стека и сегмента данных с помощью переиспользования кусков самого приложения.

Технология Control Flow Integrity (CFI) предназначена для борьбы с такими эксплоитами. При ее включении компилятор строит граф вызовов функций и встраивает код сверки с этим графом перед каждым вызовом функции. Если вызов происходит по отклоняющемуся от графа адресу, приложение завершается.

Ранее разработчики Android уже включили CFI для нескольких системных компонентов в Android 8. В Android P покрытие расширилось и теперь включает в себя медиафреймворки, а также стек NFC и Bluetooth. Теперь поддержка реализована для ядра версий 4.9 и 4.14.

Уроки фаззинга

Writing the worlds worst Android fuzzer, and then improving it — занимательная статья о том, как обычно проводят фаззинг и какую информацию взломщик может получить с его помощью.

Началось все с того, что автор решил устроить фаззинг файловой системы Android и попробовать прочитать и записать во все встреченные файлы случайный набор байтов. Расчет здесь на то, что в Android (а точнее, Linux) несколько виртуальных файловых систем (/dev, /proc, /sys) хранят не реальные, а синтетические файлы, с помощью которых можно изменять настройки ядра и работать с железом.

Как оказалось, долго фаззер работать не мог и в определенный момент просто блокировался при попытке чтения файла. Чтобы решить эту проблему, автор распараллелил фаззер на 128 потоков, и… смартфон ушел в kernel panic. То же повторилось с каждым из протестированных смартфонов.

Анализ лога из /proc/last_kmsg показал, что произошло что-то вроде heap corruption, и, если поковырять дальше, можно написать эксплоит для получения прав root в системе. А DoS-эксплоит фактически уже существует.

Но что, если файла /proc/last_kmsg нет? Например, в Galaxy S5 такой файл отсутствует, а значит, мы не сможем узнать, обращение к какому файлу привело к панике ядра. В этом случае для начала сокращаем область работы фаззера, пробуем, например, только каталог /sys. Это срабатывает, проблема в одном из его файлов. Проходимся по подкаталогам и выясняем, что ядро падает при записи в один из файлов /sys/kernel, а если точнее — /sys/kernel/debug/.

Чтобы не перебирать все файлы этого подкаталога вручную, пишем сервер, который будет работать на компе и принимать имена файлов от фаззера по мере его работы. Какое последнее имя получим перед падением, то нам и нужно.

Последним файлом оказался /sys/kernel/debug/smp2p_test/ut_remote_gpio_inout. Теперь написать DoS-эксплоит проще простого.

Автор отмечает, что в современном мире остается все меньше телефонов, которые можно отправить в панику таким способом. Все дело в правилах SELinux, которые закрывают доступ к большинству системных файлов. Однако некоторые устройства до сих пор уязвимы.

Смартфон, ворующий рекламные деньги

Avast Threat Labs finds Android device firmware that reroutes ad network revenue to unknown accounts — интересная история в блоге Avast о прошивке дешевого китайского телефона, которая не только содержит привычную всем рекламу на экране блокировки и умеет скрыто устанавливать приложения, но и перенаправляет выручку от рекламы в сторонних приложениях на другие аккаунты.

Прошивка включает в себя несколько хуков, которые срабатывают, когда то или иное приложение обращается к настройкам (SharedPreferences), файлам внутри своего пакета (AssetManager и ZipEntry) или передает данные между своими компонентами в бандлах (Bundle). Код хука ищет в полученных данных токен рекламной сети и подменяет его своим, так что все деньги от показа рекламы получает не разработчик приложения, а третье лицо.

Специалисты из Avast не сообщают модель устройства, на которое была предустановлена эта прошивка. Известно лишь, что смартфон работал на Android 6 и продавался в Европе. Хотя, судя по файлу с информацией о приложениях, токены которых перехватывала прошивка, основной целью были китайские пользователи.

Файл с информацией, какие где найти токены и на какие значения их заменитьФайл с информацией, какие где найти токены и на какие значения их заменить

Разработчику

Kotlin и его типы

Typical Kotlin — статья о системе типов языка Kotlin и ее особенностях. Несколько интересных моментов:

  • Nullable и не nullable типы в Kotlin — это действительно разные типы данных, причем не nullable тип — это всегда подтип nullable. Например, Double — это подтип типа Double?, а тот, в свою очередь, подтип типа Number?, который подтип типа Any?. Таким образом, значение типа Double можно сохранить в переменной типа Number?.
  • Когда ты создаешь какой-либо класс, ты автоматически создаешь его nullable или наоборот близнеца. Другими словами, объявив класс Hello, ты также объявишь тип Hello?.
  • Технически любая функция в Kotlin имеет тип возвращаемого значения. Если не указано явно, этим типом становится Unit. Вот его исходный код:
public object Unit { override fun toString() = "kotlin.Unit"
}

  • В Koltin есть специальный тип, который не может иметь никакого значения. Это тип Nothing (и его близнец Nothing?). Функция, объявленная с возвращаемым типом Nothing, никогда не закончит исполнение, так как значение, которое нужно вернуть, не существует. Nothing удобно использовать, чтобы подсказать компилятору и среде разработки, что функция никогда не закончит свое исполнение (или закончит выбросом исключения). В этом случае среда разработки будет подсвечивать такие функции, подсказывая разработчику, что ее исполнение не закончится.
  • Nothing — это так называемый bottom type, то есть подтип любых других типов. А return и throw — это полноценные выражения, которые возвращают тип Nothing. Комбинируем эти два утверждения вместе и получаем два вполне рабочих примера кода:
fun calculate(someParam: Int?) { val x = someParam ?: throw IllegalArgumentException("someParam must not be null") val y = x * 2 println(y)
}

fun calculate(someParam: Int?) { someParam ?: return val y = someParam * 2 println(y)
}

Иерархия типов в KotlinИерархия типов в Kotlin

Управляем приоритетами потоков

Exploring Android Thread Priority — статья о том, как Android распределяет приоритеты между потоками и как эти приоритеты изменить. Суть в следующем. В Android (а точнее, в Linux) у каждого потока исполнения есть собственный приоритет, который варьируется в пределах от –20 до 19, где меньшее число означает более высокий приоритет.

Плюс ко всему ядро Linux объединяет потоки в так называемые контрольные группы (cgroups) в зависимости от разных условий, таких, например, как видимость приложения на экране. Размещение потоков того или иного приложения в определенной группе автоматически накладывает на них ограничения. Так, потоки, помещенные в группу Background (то есть относящиеся к приложениям в фоне), получают всего 5% от общего процессорного времени.

По умолчанию любые потоки одного приложения получают равный приоритет с основным потоком, а значит, могут влиять на его исполнение. Чтобы этого избежать, следует снизить приоритет фоновых потоков с помощью одного из двух методов:

  • Thread.setPriority() — принимает значения от 1 до 10, где 10 — самый высокий приоритет;
  • Process.setThreadPriority() — принимает стандартные для Android/Linux значения от –20 до 19.

Соотношение этих двух шкал приоритетов такое:

Пример изменения приоритета потока:

public class ThreadPriority { Thread thread = new Thread(new Runnable() { @Override public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); // Твой код здесь } }, "worker-thread"); thread.start(); }
}

В оригинальной статье также приведены примеры изменения приоритета HandlerThread, Intent Service и других.

Contracts — новая функция Kotlin 1.3

Discovering Kotlin Contracts — статья о новинке Kotlin 1.3 под названием «контракты» (contracts). Они решают проблему, с которой сталкивался любой разработчик. Простой пример:

@Test fun testMyTokenLength() { val token : String? = getMyToken(); assertNotNull(token) assertEquals(42, token.length)
}

Ты не сможешь его скомпилировать, потому что компилятор скажет следующее:

Error(5, 22): Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?

Так происходит потому, что, по мнению компилятора, token может иметь значение null, а значит, мы должны проверить его на null перед тем, как вызывать метод length. Ну либо обойти предупреждение компилятора, написав так: token!!.lenght. А это не очень красиво.

В то же время мы, в отличие от компилятора, знаем, что token к моменту вызова метода length уже не может быть null, потому что проверка на null была в прошлой строке. Контракты нужны как раз для того, чтобы передать наше знание компилятору.

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

  • Returns(value) — функция успешно завершается и/или возвращает значение такого-то типа;
  • ReturnsNotNull — функция возвращает не null значение;
  • ConditionalEffect(effect, booleanExpression) — если эффект сработал, то следующее выражение верно;
  • CallsInPlace(lambda, kind) — ограничение на место и количество вызовов лямбды.

Не стоит пытаться это понять, просто взгляни на следующий код:

fun assertNotNull(actual: Any?) { contract { returns() implies (actual != null) } org.junit.Assert.assertNotNull(actual)
}

Это обертка для стандартной функции assertNotNull из пакета JUnit. Мы добавили к ней контракт, который сообщает компилятору и среде разработки, что если функция успешно завершается, значит, переданное ей в аргументе значение не равно null. Если подставить эту обертку вместо оригинала в приведенный в начале пример, компилятор ругаться не будет.

Цифровые подписи APK: прошлое и настоящее

Android APK signature scheme v3: context and new opportunities — краткая история цифровых подписей приложений для Android и объяснение нового формата цифровой подписи в Android 9.

Изначально пакет с приложением для Android (APK) представлял собой почти точную копию пакета JAR, который, в свою очередь, был просто архивом ZIP с несколькими дополнительными файлами в каталоге META-INF. Эти файлы содержали список всех файлов пакета, их криптографические хеши, а также открытый криптографический ключ, с помощью которого были подписаны списки хешей.

Все это позволяло убедиться в целостности содержащихся в пакете файлов, а также подтвердить, что пакет не был изменен после создания автором. Другими словами, устанавливая обновление приложения для Android, можно быть уверенным — оно создано тем же человеком, что и предыдущая версия.

Но есть проблема. Атака Janus показала, что существует возможность внедрить код в APK, не изменяя его цифровую подпись. Для этого можно дописать DEX-файл в начало секций ZIP-файла, и Android не учтет их при верификации файла, но при этом файл можно будет запустить как исполняемый: это будет одновременно и пакет с приложением, и исполняемый файл.

Для решения этой и схожих проблем Google предложила формат цифровой подписи APK signature scheme v2. Его суть в том, чтобы добавить к APK-файлу дополнительный блок, который будет содержать цифровую подпись всего APK целиком, а не отдельных его частей.

Поддержка scheme v2 появилась в Android 7, а уже в Android 9 добавилась поддержка APK signature scheme v3. Новый формат цифровой подписи похож на предыдущий, но обладает одной отличительной чертой: он поддерживает ротацию криптографических ключей. Это позволяет разработчикам без проблем менять цифровую подпись для своих приложений, не заставляя пользователей удалять старую версию приложения перед установкой новой.

APK signature scheme v1APK signature scheme v1

Пять библиотек анимации

Android Top Animation Libraries — краткий обзор пяти библиотек, позволяющих реализовать анимацию в приложении:

  • Lottie for Android — парсит и рендерит анимации в формате Adobe After Effects (экспортированные в JSON с помощью Bodymovin);
  • Material Animations — эффекты перехода между активностями;
  • Android View Animations — коллекция различных эффектов анимации;
  • RecyclerView Animators — набор анимаций для RecyclerView;
  • Rebound — реализация реалистичных эффектов пружины.

Библиотеки

  • FeatureAdapter — RecyclerView-адаптер, предназначенный для создания комплексных списков;
  • FBToast — библиотека для создания сложных кастомных сообщений;
  • view-effects — добавлять различные визуальные эффекты для фона виджета (например, блюр);
  • FingerprintIdentify — библиотека для работы с датчиками отпечатков пальцев, способная работать не только с нативным API Android, но и с API Samsung и Meizu (до Android 6.0);
  • Protein — плагин Android Studio, генерирующий код — заглушки для Retrofit2 и RxJava2;
  • AppJoint — фреймворк для создания модульных приложений;
  • Krate — враппер для работы с настройками из Kotlin, выполненный с использованием делегированных переменных;
  • SnapTabLayout — реализация табов приложения в стиле Snapchat;
  • Android-GoldenEye — простая в использовании библиотека для работы с камерой;
  • Android-Goldfinger — библиотека для работы с датчиком отпечатков пальцев;
  • android-gpuimage — библиотека для обработки изображений на графическом процессоре, аналог GPUImage для iOS;
  • ok-gradle — плагин для Android Studio, запускаешь, вбиваешь имя нужной библиотеки и получаешь строку, которую необходимо добавить в build.gradle для ее подключения.

ОСТАВЬТЕ ОТВЕТ

Please enter your comment!
Please enter your name here