Mobile

Как получить доступ к камере и микрофону Android телефона.

Перехват информации всегда был в тренде и по множеству шпионких фильмов мы не раз видели  как спец службы могут подключится к любому мобильному устройству. На самом деле это задача очень не простая и по просочившимся в прессу  кейсам мы понимаем что это могут позволить себе далеко не рядовые полицейские. А если посмотреть с другой стороны? Могут ли хакеры сделать тоже самое? Как показывает практика иследования уязвимости StrandHogg в Android это вполне осуществимая задача. О том как получить доступ к камере и микрофону  телефона сегодня и пойдет речь.

StrandHogg — уязвимость с «подменой» приложений

The StrandHogg vulnerability — подробности нашумевшей уязвимости всех версий Android (включая 10), позволяющей подменить экран (активность) одного приложения на экран другого.

Что произошло: специалисты норвежской компании Promon обнаружили в Android уязвимость, которая позволяет подменить экран (активность) легитимного приложения, подсунув вместо нее экран зловредного приложения. В результате у злоумышленников появляется возможность провести атаку с использованием фишинга или, например, запросить для зловредного приложения дополнительные полномочия якобы от лица другого приложения.

Что произошло на самом деле: в Android у активностей есть флаг taskAffinity. Он позволяет указать имя задачи (task), в стек активностей которой попадет указанное приложение. Это нужно для более тонкого управления стеками обратных переходов. Но есть одна проблема: по умолчанию значение taskAffinity равно имени пакета приложения, а это значит, что в ряде случаев можно незаметно всунуть активность своего приложения в стек активностей чужого. А если еще и указать флаг allowTaskReparenting="true", эту активность можно передвинуть на самый верх и при следующем клике по иконке целевого приложения она появится на экране первой (то есть будет находиться на вершине стека).

Страшно? Конечно, но, как и обычно, стоит копнуть глубже — и страх пройдет.

Во-первых, об этой «уязвимости» известно уже много лет. Предыдущая ее вариация называлась Android Task Hijacking.

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

В-третьих, Google действительно отказывается исправлять уязвимость на протяжении нескольких лет. Просто потому, что это так называемый design flaw, то есть ошибка проектирования, исправление которой сломает существующий софт.

Наконец, в-четвертых, у проблемы уже давно есть решение. Достаточно указать taskAffinity="" в элементе Application, и все активности твоего приложения станут неуязвимы к атаке.

Мораль: не читайте советских газет, читайте доки по безопасности.

Обход защиты на снятие скриншотов

Android Frida hooking: disabling FLAG_SECURE — статья о том, как с помощью Frida отключить защиту на снятие скриншота приложения.

Кратко: Android позволяет разработчику приложения запретить снимать скриншоты определенных активностей приложения. Для этого необходимо установить флаг FLAG_SECURE для окна:

public class FlagSecureTestActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
        setContentView(R.layout.main);
        ...
    }
}

 

Обойти эту защиту можно несколькими способами. Например, использовать модуль Xposed DisableFlagSecure, который перехватывает функцию setFlags и просто отфильтровывает флаг FLAG_SECURE:

@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
    XposedHelpers.findAndHookMethod(Window.class, "setFlags", int.class, int.class, mRemoveSecureFlagHook);
}

private final XC_MethodHook mRemoveSecureFlagHook = new XC_MethodHook() {
    @Override
    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
        Integer flags = (Integer) param.args[0];
        flags &= ~WindowManager.LayoutParams.FLAG_SECURE;
        param.args[0] = flags;
    }
};

 

Однако Xposed требует права root на устройстве (есть проект VirtualXposed, не требующий root, но многие модули в нем не работают). Frida, с другой стороны, может работать на любом устройстве без необходимости получать права root.

Вот как выглядит тот же код отключения в варианте для Frida:

Java.perform(function () {
    var FLAG_SECURE = 0x2000;
    var Window = Java.use("android.view.Window");
    var setFlags = Window.setFlags;

    setFlags.implementation = function (flags, mask) {
       console.log("Disabling FLAG_SECURE...");
       flags &= ~FLAG_SECURE;
       setFlags.call(this, flags, mask);
    };
});

Использовать так:

$ frida -U -l наш_скрипт.js -f имя.пакета.приложения --no-pause

Обход ограничения на доступ к камере, микрофону и местоположению в фоне

Androids Invisible Foreground Services and How to (Ab)use Them — доклад с конференции Black Hat Europe 2019 с описанием очень простой техники обхода защиты на доступ к камере, микрофону и местоположению в Android 9 и 10.

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

Исключение дается только приложениям, имеющим активный foreground-сервис, который, в свою очередь, обязан вывести видимое пользователю уведомление и иконку в строке состояния.

Если мы хотим создать гипотетическую малварь, лучше оставаться скрытными и никаких уведомлений не показывать. Но как это сделать?

Решение: начиная с пятой версии в Android есть механизм JobScheduler, который позволяет запускать код периодически или при наступлении определенных событий (например, подключение к зарядному устройству). Мы могли бы использовать его для периодического запуска наших шпионских функций, но задачи JobScheduler’а тоже выполняются в фоне.

Выход состоит в том, чтобы при наступлении события JobScheduler’а запустить сервис, затем сделать его foreground-сервисом с помощью startForeground, быстро получить доступ к камере, микрофону и местоположению и остановить сервис:

@Override
public int onStartCommand(Intent intent, int flags, int startId){
    Notification notification = createCustomNotification();
    this.startForeground(1, notification)
    accessMicrophone();
    stopForeground(true);
    return START_STICKY;
}

 

Трюк состоит в том, что, если сделать работу достаточно быстро (по утверждению автора доклада — за пять секунд), система просто не покажет уведомление.

Proof of concept есть на GitHub. Баг уже исправлен, теперь уведомление будет показано в любом случае.

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

Хорошие и плохие приемы программирования на Kotlin

Good And Bad Practices Of Coding In Kotlin — статья о практиках программирования на Kotlin, хороших и плохих.

Первый пример

fun main {
    val file = File("/my_file")
    val writer = file.printWriter()
    try {
        writer.println("Hello World")
    } catch(__: Exception){
        // Обработка исключения
    } finally {
       writer.close()
    }
}

 

Это стандартный способ записи строки в файл: открываем файл и с помощью PrintWriter записываем строку. В завершение закрываем PrintWriter даже в том случае, если будет выброшено исключение.

Этот код можно переписать так:

fun main () {
    val file = File("/my_file")
    file.printWriter().use {
        it.println("Hello World")
    }
}

 

В случае с объектами, реализующими интерфейс Closeable (здесь PrintWriter), функция-расширение use автоматически вызывает метод close после выполнения кода лямбды.

Второй пример

val list = listOf("Ahsen", "Bob", "Alex", "Jasmine")
val anotherList = listOf(1, 2, 3, 4)
fun main() {
    val size = list.size
    for(i in 0..size)
       println("Name -> ${list[i]} and number -> ${anotherList[i]}")
}

 

Этот код проходит в цикле сразу по двум спискам. Но в нем есть одна проблема: если второй список будет длиннее первого, приложение упадет с исключением ArrayIndexOutOfBoundsException.

Kotlin позволяет решить эту проблему и заодно сделать код гораздо более элегантным:

val list = listOf("Ahsen", "Bob", "Alex", "Jasmine")
val anotherList = listOf(1, 2, 3, 4, 5, 6, 7)
fun main() {
    for((name, number) in list.zip(anotherList))
        println("Name -> $name and number -> $number")
}

 

Метод zip создает набор пар (pair), где первый элемент пары — значение из первого списка, второй элемент — значение из второго с тем же индексом. В этом примере пара автоматически раскладывается на переменные name и number с помощью деструктивного оператора.

Третий пример

val firstList = listOf("Ahsen", "Bob", "Alex", "Jasmine")
val secondList = listOf("Ahsen", "Jack", "Alen", "Jasmine")
fun main() {
    val result = mutableListOf<String>().apply {
        addAll(secondList)
    }
    for(name in firstList)
       if(!secondList.contains(name))
           result.add(name)
    println(result)
}

 

Данный код объединяет два однотипных списка так, чтобы их значения не повторялись. Но в Kotlin у списков есть метод union, предназначенный именно для этого:

val firstList = listOf("Ahsen", "Bob", "Alex", "Jasmine")
val secondList = listOf("Ahsen", "Jack", "Alen", "Jasmine")
fun main() {
    val result = firstList.union(secondList)
    println(result)
}

 

Четвертый пример

const val NAME = "Ahsen"
val x = NAME as Int
fun main() {
    println(x)
}

 

Данный код выбросит исключение ClassCastException, потому что константа NAME имеет тип не Int. Чтобы такого не происходило, можно использовать оператор as?:

const val NAME = "Ahsen"
val x = NAME as? Int
fun main() {
    println(x)
}

 

Если приведение типов будет невозможно, переменная x получит значение null.

Typealias в Kotlin

Maintaining crisp code with type aliases — заметка о ключевом слове typealias, которое можно использовать для назначения произвольных имен различным типам данных.

Представим, что у нас есть список юзеров List<User>. Использовать его в коде неудобно, автодополнение работает плохо. Поэтому мы хотели бы придумать для него более простое имя.

Традиционный способ сделать это — завернуть список в класс:

data class Users(val userList: List<User>)

 

Но в Kotlin есть более простой и лишенный оверхеда вариант:

typealias Users = List<User>

 

Typealias не вводит новый тип данных и не создает классов, это просто второе имя для определенного типа данных. Его можно использовать не только для сокращения записи, но и для разделения неймспейсов, если несколько пакетов содержат классы с одинаковыми именами.

Другие полезные примеры использования typealias:

  • Более наглядный способ объявления лямбд:

    typealias Result<T> = (T) -> Unit
    
    fun fetchData(callback: Result<String>)
    
  • Маркировка nullable-типов:

    typealias MaybeBook = Book?
    
  • Быстрый доступ к вложенным классам:

    typealias DialogBuilder = AlertDialog.Builder
    
    val dialog = DialogBuilder().build()
    

Инлайновые классы в Kotlin

Low-overhead wrappers using inline classes — заметка, рассказывающая, как использовать инлайновые классы Kotlin, чтобы защититься от ошибок и улучшить читаемость кода.

Представим, что у нас есть такая функция:

fun renderLocation(latitude: Double, longitude: Double) {
    map.render(latitude, longitude)
}

 

На первый взгляд все хорошо, но есть проблема: что, если ты случайно передашь ей долготу вместо широты и широту вместо долготы? Они обе имеют тип Double, поэтому у компилятора не будет возможности предупредить тебя об ошибке.

Помогут классы-врапперы:

class Latitude(val value: Double)
class Longitude(val value: Double)

fun renderLocation(latitude: Latitude, longitude: Longitude) {
    map.render(latitude, longitude)
}

 

Но так ты получишь оверхед, компилятору придется создать, по сути, два лишних объекта и выделить для них память в хипе.

Для решения этой проблемы в Kotlin 1.3 появились так называемые инлайновые классы:

inline class Latitude(val value: Double)
inline class Longitude(val value: Double)

fun renderLocation(latitude: Latitude, longitude: Longitude) {
    map.render(latitude, longitude)
}

 

Теперь компилятор сможет автоматически заменить все использования объектов классов Latitude и Longitude на Double. Вот так будет выглядеть функция renderLocation, декомпилированная в Java:

public static final void renderLocation-vKZqJUM(double latitude, double longitude) {
    map.render-vKZqJUM(latitude, longitude);
}

 

Обрати внимание на -vKZqJUM в конце имени функции. Это хеш, который добавляет компилятор ко всем функциям, принимающим инлайновые классы в качестве аргументов. Он нужен, чтобы в коде случайно не появились две одинаковые функции с одинаковыми аргументами (например, если ты создаешь еще одну функцию renderLocation с двумя аргументами типа Double).

Корректное выделение текста на фоне

Contrasting text and icons over background — статья о том, как сделать текст и иконки читаемыми на разном фоне. Автор предлагает алгоритм автоматического выбора темного или светлого цвета текста в зависимости от используемого фона.

Для начала создадим простую функцию-расширение для определения «уровня темности» фона:

fun @receiver:ColorInt Int.isDark(): Boolean = ColorUtils.calculateLuminance(this) < 0.5

 

Функция использует класс ColorUtils из библиотеки androidx.core и возвращает true, если фон достаточно темный, чтобы белый текст на нем читался хорошо.

Использовать эту функцию можно так:

val color = Color.parseColor("#e91e63")

if (color.isDark()) {
    colorHex.setTextColor(R.color.white)
    favIcon.setImageResource(R.drawable.ic_favorite_border_white_24dp)
} else {
    colorHex.setTextColor(R.color.black)
    favIcon.setImageResource(R.drawable.ic_favorite_border_black_24dp)
}

 

Здесь color — это цвет фона, а colorHex и favIcon — текстовый виджет и виджет иконки. Если цвет темный, текст и иконка окрашиваются в белый, иначе — в черный.

Но что, если в качестве фона используется картинка? В этом случае мы можем извлечь из картинки наиболее значимые цвета (палитру) с помощью класса Palette и проверить их яркость с помощью функции isDark:

Palette.from(bitmap)
    .maximumColorCount(3)
    .clearFilters()
    .setRegion(bitmap.width - iconSize , 0, bitmap.width, iconSize)
    .generate { palette ->
        val lightness = ColorUtils.isDark(palette)
        val isDark = if (lightness == ColorUtils.LIGHTNESS_UNKNOWN) {
            ColorUtils.isDark(bitmap, bitmap.width - iconSize / 2, iconSize / 2)
        } else {
            lightness == ColorUtils.IS_DARK
        }


        if (isDark) { // Make back icon dark on light images
            favIcon.setImageResource(R.drawable.ic_favorite_border_white_24dp)
        } else {
            favIcon.setImageResource(R.drawable.ic_favorite_border_black_24dp)
        }
    }

 

Предполагается, что мы размещаем нашу иконку в правом верхнем углу картинки, поэтому с помощью функции setRegion указываем правый верхний угол. Затем с помощью ColorUtils.isDark мы выясняем, темная ли это палитра. Если выяснить это не удалось, пробуем определить яркость отдельно взятого пикселя посередине интересующей нас области. Затем мы меняем цвет иконки в зависимости от полученных результатов.

Стоит отметить, что в стандартном классе ColorUtils из пакета androidx.core нет метода isDark. В данном примере используется класс ColorUtils из проекта Plaid.

Статистика распределения версий Android

В мае этого года Google перестала публиковать статистику по версиям Android. Однако PornHub исправил этот недочет, опубликовав собственную статистику.

Что следует иметь в виду при изучении этих данных:

  • Статистика Google покрывает все устройства, которые так или иначе подключались к Google Play. Среди этих устройств есть множество «звонилок», старых смартфонов, которые люди продолжают использовать спустя много лет после релиза. Ведь звонить, писать и просматривать сводку погоды можно с комфортом и на устройстве десятилетней давности.
  • Статистика PornHub, кроме устройств с сервисами Google, также покрывает несертифицированные устройства и устройства с кастомными прошивками, но без сервисов Google.
  • Основная масса пользователей PornHub — люди в возрасте от 18 до 34 лет (данные из той же статистики). Другими словами, это те самые «активные» пользователи смартфонов, которые регулярно покупают новые устройства.

Инструменты

  • Android Malware Sandbox — виртуальная машина для быстрого запуска малвари.

Библиотеки

  • Keigen — быстрая библиотека линейной алгебры;
  • Extensions-list — список всех функций-расширений, доступных в библиотеках Android-KTX;
  • Store — библиотека для загрузки, кеширования и обновления данных из сети;
  • Tinker — библиотека для обновления кода и ресурсов приложения без обновления всего пакета;
  • FlowBinding — аналог RxBinding на основе Kotlin Flow;
  • AestheticDialogs — различные виды диалогов;
  • Flourish — библиотека для показа/скрытия лайотов с анимацией;
  • MaterialDialog-Android — библиотека для создания диалогов в стиле Material Design;
  • ParallaxScrollingView — галерея с эффектом параллакса.
Click to rate this post!
[Total: 2 Average: 5]
cryptoworld

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

View Comments

  • Мне очень нравится VirtualXposed. Это позволяет мне использовать модуль Youtube Adaway Xposed без доступа пользователя root.

Recent Posts

Атака на приложение Google Cloud Native

Облачные приложения по своей природе используют микросервисы, контейнеризацию, динамическую оркестровку и в значительной степени полагаются…

5 дней ago

Лучший адаптер беспроводной сети для взлома Wi-Fi

Чтобы взломать сеть Wi-Fi с помощью Kali Linux, вам нужна беспроводная карта, поддерживающая режим мониторинга…

2 года ago

Как пользоваться инструментом FFmpeg

Работа с консолью считается более эффективной, чем работа с графическим интерфейсом по нескольким причинам.Во-первых, ввод…

2 года ago

Как создать собственный VPN-сервис

Конечно, вы также можете приобрести подписку на соответствующую услугу, но наличие SSH-доступа к компьютеру с…

2 года ago

ChatGPT против HIX Chat: какой чат-бот с искусственным интеллектом лучше?

С тех пор как ChatGPT вышел на арену, возросла потребность в поддержке чата на базе…

2 года ago

Разведка по Wi-Fi и GPS с помощью Sparrow-wifi

Если вы когда-нибудь окажетесь в ситуации, когда вам нужно взглянуть на спектр беспроводной связи, будь…

2 года ago