Order Of Six Angles

Main Logo

A security researcher's blog about reverse-engineering, malware and malware analysis

Home | RU | Translations | Tools | Art | About

17 December 2019

tags: android - crackme

Решаем "OWASP UnCrackable App for Android Level 2", с помощью FRIDA и Binary Ninja!

Intro

Первая часть тут. Советую начать с нее, так как многие моменты описаные там, здесь описываться не будут.

Tools used: FRIDA, Binary Ninja

Продолжаем решать задания от OWASP. Эти задания даются в качестве демонстративных примеров в OWASP Mobile Security Testing Guide. Я не читал этот гайд и буду пытаться сделать задания, опираясь только на свои обрывки знаний об андроиде. Скорей всего есть более быстрые и короткие пути решения. Решение ниже просто является моим.

UnCrackable App for Android Level 2

Описание задания второго уровня

This app holds a secret inside. May include traces of native code.

Objective: A secret string is hidden somewhere in this app. Find a way to extract it.

Описание уже содержит хорошую подсказку, но не будем забегать вперед. Скачиваем apk, устанавливаем на android эмулятор, запускаем. Видим, как и в первом задании, детектирование рута

Давайте обойдем эти проверки другим способом (не выпиливанием кода из apk), с помощью Frida. Это фреймворк, который позволяет вмешиваться в работу приложения, во время выполнения. Инструкция по установке проста:

pip install frida-tools

Далее нам необходимо скачать подходящий сервер frida отсюда. У меня эмулятор android x86, поэтому я скачал архив с названием frida-server-12.7.26-android-x86.xz. Распаковываем и закидываем на эмулятор

adb push frida-server-12.7.26-android-x86_64 /data/local/tmp/frida

Запускаем

chmod +x /data/local/tmp/frida
/data/local/tmp/frida &

После этого, мы должны командой frida-ps -U получить список процессов на эмуляторе, чтобы убедиться, что Frida установлена правильно и сервер запущен

Теперь возвращаемся к приложению и детектированию рута. Оно по прежнему происходит при помощи трех функций, которые мы видим после invoke-static

    invoke-static {}, Lsg/vantagepoint/a/b;->a()Z

    move-result v0

    if-nez v0, :cond_0

    invoke-static {}, Lsg/vantagepoint/a/b;->b()Z

    move-result v0

    if-nez v0, :cond_0

    invoke-static {}, Lsg/vantagepoint/a/b;->c()Z

    move-result v0

С помощью Frida, мы можем заставить эти функции всегда возвращать false и обходить проверку. Для этого нам необходимо написать скрипт на javascript. Чтобы работать с java классами, мы должны использовать обертку, для нашего кода (подробнее о API к java, читайте в официальной документации)

Java.perform(function() {
    //our code...
})

Чтобы получить доступ к классу sg.vantagepoint.a.b (в котором находятся наши проверки), мы должны использовать метод Java.use(classname)

Java.perform(function() {

	var antiRootClass = Java.use("sg.vantagepoint.a.b");
})

Теперь имея доступ к нужному классу, зная имя метода, мы можем переопределить его поведение на возврат false

antiRootClass.a.implementation = function() {
		return false;
	}

Повторим данный код, для всех трех методов и в итоге получим

console.log("Script started...");

Java.perform(function() {

	var antiRootClass = Java.use("sg.vantagepoint.a.b");
	antiRootClass.a.implementation = function() {
		return false;
	}
	console.log("sg.vantagepoint.a.b.a modified");
	
	antiRootClass.b.implementation = function() {
		return false;
	}
	console.log("sg.vantagepoint.a.b.b modified");
	
	antiRootClass.c.implementation = function() {
		return false;
	}
	console.log("sg.vantagepoint.a.b.c modified");
})

Чтобы запустить этот скрипт, вам необходимо знать package name приложения. Его можно получить из манифеста или запустив приложение и посмотрев все той же командой frida-ps.

Сохраняем код скрипта в файл с именем owasp2.js и выполняем такую команду

frida -U -l owasp2.js --no-paus -f owasp.mstg.uncrackable2

которая сама стартует приложение и выполняет скрипт. В итоге мы не получаем окно, так как все наши проверки рута были отключены

Мы получили доступ к вводу секретной строки. Вводим что-нибудь

появилось окно. Ищем в исходниках место, где встречается эта строка и идет проверка. Таким местом оказался метод verify() в классе MainActivity. Его сокращенная версия


    ...

    invoke-virtual {p1}, Landroid/widget/EditText;->getText()Landroid/text/Editable;

    move-result-object p1

    iget-object v1, p0, Lsg/vantagepoint/uncrackable2/MainActivity;->m:Lsg/vantagepoint/uncrackable2/CodeCheck;

    invoke-virtual {v1, p1}, Lsg/vantagepoint/uncrackable2/CodeCheck;->a(Ljava/lang/String;)Z

    move-result p1

    if-eqz p1, :cond_0

    const-string p1, "Success!"

Здесь мы видим, что введенный нами текст в EditText, отправляется в функцию sg/vantagepoint/uncrackable2/CodeCheck;->a и если все удачно, нам говорят “Success!”. Очевидно, что именно там идет проверка нашего ввода на правильность. Откроем класс sg.vantagepoint.uncrackable2.CodeCheck

.method private native bar([B)Z
.end method


# virtual methods
.method public a(Ljava/lang/String;)Z
    .locals 0

    invoke-virtual {p1}, Ljava/lang/String;->getBytes()[B

    move-result-object p1

    invoke-direct {p0, p1}, Lsg/vantagepoint/uncrackable2/CodeCheck;->bar([B)Z

    move-result p1

    return p1
.end method

Наш ввод, передается в функцию bar()

invoke-direct {p0, p1}, Lsg/vantagepoint/uncrackable2/CodeCheck;->bar([B)Z

Обратите внимание на модификатор native функции

.method private native bar([B)Z

Модификатор native означает, что реализация метода находится в библиотеках, написанных на других языках. Чтобы работать с такими библиотеками, используется механизм JNI (Java Native Interface). С помощью этого интерфейса, андроид приложение может вызывать код на С/С++. Значит, наше приложение использует внешнюю библиотеку. Найдем доказательство этому и сделаем поиск по исходникам, по слову loadLibrary. Находим следующий код в MainActivity

# direct methods
.method static constructor <clinit>()V
    .locals 1

    const-string v0, "foo"

    invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V

    return-void
.end method

Этот код говорит нам о том, что приложение загружает библиотеку с именем foo. Если мы распакуем наше приложение, то в папке lib мы видим библиотеку libfoo.so. Теперь пришло время изучить ее. Так как у нас нет исходных кодов библиотеки и написана она на С/С++, то нам необходимо воспользоваться дизассемблером, в нашем случае - Binary Ninja!

*он платный, но у него также есть бесплатная web версия. вы можете использовать любой другой дизассемблер

Открываем файл нашей библиотеки и находим в списке нашу функцию bar

Открываем функцию. Углубляться в анализ ассемблерного кода мы сегодня не будем, возможно в другой жизни это сделаем. Просто обратите внимание на выделенный код и в особенности на hex значения

на адреса они не похожи, а похожи они на строки. Попробуем посмотреть опцией “отобразить, как символы”

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

Проделаем для всех значений

Эти части складываются в единную строку “Thanks for all the fish”. Далее по коду мы видим сравнение нашего ввода с этой строкой

Проверим строку в приложении

Это и есть наша секретная строка!

Вверх