Android: уязвимость StrandHogg и обход ограничений на доступ к камере и микрофону

Сегодня в выпуске: подробности уязвимости StrandHogg, которая позволяет подменить любое приложение в любой версии Android; обход защиты на доступ к камере, микрофону и местоположению; обход защиты на снятие скриншотов; хорошие и плохие приемы программирования на Kotlin; псевдонимы типов данных и инлайновые классы в Kotlin; а также статистика по версиям Android от PornHub и подборка библиотек для программистов.

Почитать

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

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

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

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


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

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

Во-вторых, эксплуатация уязвимости требует, чтобы и зловредное приложение, и приложение-жертва были уже запущены. Последовательность действий пользователя должна быть такой: он запускает приложение-жертву, возвращается на рабочий стол, затем запускает зловредное приложение, затем снова запускает приложение-жертву. В этот момент вместо холодного запуска 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 после выполнения кода лямбды.

Продолжение доступно только участникам

Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», увеличит личную накопительную скидку и позволит накапливать профессиональный рейтинг Xakep Score! Подробнее

Вариант 2. Открой один материал

Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.


Евгений Зобнин: Редактор рубрики X-Mobile. По совместительству сисадмин. Большой фанат Linux, Plan 9, гаджетов и древних видеоигр.
Похожие материалы