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

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

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

11 месяцев ago

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

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

12 месяцев ago

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

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

12 месяцев ago

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

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

12 месяцев ago

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

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

12 месяцев ago

Как обнаружить угрозы в памяти

Elastic Security стремится превзойти противников в инновациях и обеспечить защиту от новейших технологий злоумышленников. В…

1 год ago