• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Внедрение в Android приложения с помощью Frida

Katant

floppy-диск
Пользователь
Регистрация
23.10.2020
Сообщения
1
Реакции
24
Депозит
50
1604126470503.png

Frida - универсальный динамический фреймворк, позволяющий внедрять сторонний код в приложения БЕЗ изменения самих приложений.
Сегодня мы разберём работу с Frida на примере Android приложений. Я не буду рассказывать тут про frida-trace, frida-discover, etc.
Тут я расскажу про написание своих скриптов под Frida. В первом примере мы будем внедряться в наше приложение,
а в других - будем работать на практике (обходить кастомный SSL Pinning, решать crackme и не только)
Итак, инструментарий:

1. Frida
2. Эмулятор Android Genymotion
3. Python 3
4. Декомпилятор Jadx
5.
Genymotion ARM Translation Kit

Дисклеймер:
Я не несу ответственности за ваши действия. Вы все делаете на свой страх и риск

Установка:
1604126482774.png

(я же не символы тут набиваю)


Первое приложение - калькулятор
Итак, начнём мы с внедрения в простое приложение, которое 1 раз в секунду выводит произведение двух чисел
Java:
package xss.pro.app;

import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        while (true) {
            try {Thread.sleep(250);} catch (InterruptedException ignored) {}
            System.out.println("Total=" + multiply(10, 123));
        }
    }


    int multiply(int x , int y){
        System.out.println("X=" + x);
        System.out.println("Y=" + y);
        return x * y;
    }
}

Запустив, смотрим вывод. X=10, Y=123. Теперь мы будем получать эти значения из Frida
1604126508128.png

Создаем .js файл и пишем основу для внедрения в Java:
JavaScript:
Java.perform(() => {

});
Далее нам надо указать класс, в функцию которого мы будем внедрять код. Для этого будем использовать Java.use("класс").
В нашем примере это xss.pro.app.MainActivity, следовательно, пишем Java.use("xss.pro.app.MainActivity").
Чтобы переназначить функцию, мы будем использовать функцию implementation: Java.use("xss.pro.app.MainActivity").multiply.implementation = function (x, y) {} и добавим туда простой console.log.
В итоге:
JavaScript:
Java.perform(() => {
    Java.use("xss.pro.app.MainActivity").multiply.implementation = function (x, y) {
        console.log("X=", x, "Y=", y);
    };
});

Теперь запускаем: frida -U -f xss.pro.app -l script.js --no-pause
Немного подробнее об аргументах:
-U - подключения к frida-server на удаленном устройстве (в нашем случае на эмуляторе)
-f xss.pro.app - имя пакета приложения
-l script.js - скрипт для внедрения
--no-pause - сразу запускает приложение

Запустив, увидим что мы смогли получить X и Y:
1604126517160.png

Но у нас есть ошибка: мы ничего не возвращаем. Предлагаю исправить это - добавим return.
Мы имеем доступ ко всем функциям в данном классе, и можем использовать this.multiply: var ret = this.multiply(x,y); и return ret;
Сохраняем код (Frida автоматически обновит его при изменении файла) и видим что ошибка ушла (я добавил ещё один console.log):
1604126524032.png

Теперь предлагаю заменить вывод. Как Вы все поняли (надеюсь), для этого достаточно заменить переменную в return: return 1337;
Смотрим в logcat:
1604126544020.png

Аналогично мы можем подменять и начальные переменные (x, y):
1604126549830.png


Второе приложение - Мегафон Банк
Итак, это конечно все хорошо, но это очень просто. Предлагаю начать с обхода SSL Pinning в приложении Мегафон Банк (ТОЛЬКО В ОЗНАКОМИТЕЛЬНЫХ ЦЕЛЯХ).
Запускаем сниффер и видим печальную картину:
1604126557605.png

Из ошибки сниффера точно становится ясно, что это SSL Pinning
1604126623479.png

Итак, загружаем наш APK в Jadx, включаем деобфускацию и начинаем поиск. Искать сертификаты стоит по BEGIN и sha256/
Смотрим по BEGIN и видим класс com.megafon.bank.utils.SslUtils
1604126630104.png

В нем есть переменная TRUSTED_CERTS, в которой и содержаться все сертификаты. Мы могли бы просто пересобрать приложение, но кто знает какие там ещё есть защиты (ведь при модификации мы теряем подпись разработчика и проверку Safetynet).
Поэтому и будем использовать Frida. Смотрим немного вниз и видим где используется наша строка с сертификатами
1604126649316.png

Нажимаем Ctrl и нажимаем на нашу функцию .mo27468a (она у Вас будет называться по другому, это всё после деобфускатора).
Откроется сама функция
1604126666907.png

Обязательно запоминаем /* renamed from: a */ потому что нам надо использовать оригинальные названия функций, а не из деобфускатора
Смотрим в начало класса и тоже запоминаем оригинальное название:
1604126672238.png

JavaScript:
Java.use("h0.j").a.implementation = function (x, y) {
    console.log(x,y)
    return this.a(x,y);
};
Сохраняем код и видим ошибку: Frida не может найти именно ту функцию, которая нам нужна (там несколько функций с одинаковым именем)
1604126677083.png

В нашей функции типы переменных String и Charset, следовательно, выбираем .overload('java.lang.String', 'java.nio.charset.Charset') и добавляем перед .implementation.
Далее подменяем первый аргумент (String) на наш сертификат (сниффера) в формате PEM. Делаем запрос на авторизацию и смотрим:
1604126690325.png

Мы обошли SSL Pinning!
JavaScript:
Java.perform(() => {
    Java.use("h0.j").a.overload('java.lang.String', 'java.nio.charset.Charset').implementation = function (x, y) {
        x = "-----BEGIN CERTIFICATE-----...-----END CERTIFICATE-----\n"
        return this.a(x,y);
    };
});

Работа с Python (или почти безграничные возможности)
Конечно, для базовых скриптов, какие мы рассмотрели, спокойно и хватает возможностей V8, но мы не будем на этом останавливаться.
Нам понадобится начальный код для запуска скрипта:
Python:
import time, frida

device = frida.get_usb_device()  # получаем девайс с Frida
pid = device.spawn(["owasp.mstg.uncrackable1"])  # запускаем приложение
device.resume(pid)  # убираем приложение с паузы
time.sleep(1)  # ждём загрузки
session = device.attach(pid)  # подключаем Frida к приложению
with open("script.js") as f:
    script = session.create_script(f.read())  # читаем скрипт из файла
script.load()  # загружаем скрипт
input()  # вечно ждём
Далее создаем функцию, которая будет принимать сообщения от нашего скрипта:

Python:
def on_message(message, *args):
    print(message)
И подключаем её перед запуском скрипта:
Python:
script.on("message", on_message)
Отправлять данные мы можем через функцию script.send. Пишем её в нашу функцию, которая будет принимать сообщения:
Python:
script.post({"x": 1337, "y": 1234})
Теперь предлагаю вернуться к нашему скрипту. Чтобы отправить из него данные, используем функцию send:
JavaScript:
send({"x": x, "y": y})
Смотрим что нам пришло в функцию:
1604152743738.png

Теперь будем принимать в самом скрипте значения из Python. Для начала обозначим переменные: let new_x, new_y;
Дальше с функцией recv будем принимать наши значения:

JavaScript:
recv(function (update) {
    new_x = update.x;
    new_y = update.y;
}).wait();
Далее добавим console.log и передадим новые значения в return:
1604152939552.png

Смотрим в logcat:
1604152975150.png


Третье приложение - Crackme
Вот теперь мы можем начать делать серьезные вещи. Для примера я возьму OWASP MSTG Android Crackme Level 1
Из условий у нас - This app holds a secret inside. Can you find it?.
Предлагаю в начале установить приложение. Запускаем и видим проверку на Root:
1604153267757.png

Начинаем прямо с неё. Загружаем APK в Jadx и смотрим MainActivity:
1604153355204.png

Тут видно что ещё есть проверка на дебаггер, но она нас не интересует. Смотрим функции m2a, m3b, m4c:
1604153577833.png

Они возвращают false, если Root не найден. Меняем все выводы на false:
JavaScript:
Java.use("sg.vantagepoint.a.c").a.implementation = function () {return false};
Java.use("sg.vantagepoint.a.c").b.implementation = function () {return false};
Java.use("sg.vantagepoint.a.c").c.implementation = function () {return false};
Итак, приложение запустилось и требует секретную фразу
1604153841124.png

Смотрим чуть ниже и видим где идёт сама проверка. А именно функция m6a:
1604153883492.png

Смотрим её код и видим что она сверяет результат дешифрования AES и строку пользователя:
1604153974231.png

AES у нас дешифрует функция m0a, предлагаю вытащить её вывод:
1604154074407.png

JavaScript:
Java.use("sg.vantagepoint.a.a").a.implementation = function (bArr, bArr2) {
    let ret = this.a(bArr, bArr2);
    console.log(ret)
    return ret;
}
Вводим любой текст и видим... [object Object]?
1604154227430.png

Предлагаю преобразовать его в текст:
JavaScript:
let decoded = '';
for (let i = 0; i < ret.length; i++) {
    decoded += String.fromCodePoint(ret[i]);
}
console.log(decoded)
Опять вводим текст и видим уже читаемую строку I want to believe:
1604154424205.png

Вводим эту строку и проверяем что мы правильно решили Crackme!
1604154591708.png


JavaScript:
Java.perform(() => {
    Java.use("sg.vantagepoint.a.c").a.implementation = function () {return false};
    Java.use("sg.vantagepoint.a.c").b.implementation = function () {return false};
    Java.use("sg.vantagepoint.a.c").c.implementation = function () {return false};
    Java.use("sg.vantagepoint.a.a").a.implementation = function (bArr, bArr2) {
        let ret = this.a(bArr, bArr2);
        let decoded = '';
        for (let i = 0; i < ret.length; i++) {
             decoded += String.fromCodePoint(ret[i]);
        }
        console.log(decoded)
        return ret;
    }
});

Итоги
Конечно, хотелось бы рассказать про работу с нативными библиотеками, но уже никак бы не успел написать это на этот конкурс. (может на следующий и напишу, но это не точно)
Мы рассмотрели меньше 10% возможностей Frida и это одна из must have утилит для работы с Android (и не только) приложениями.
Возможно я немного не так понял условия конкурса, но если бы не попробовал участвовать, жалел бы ещё потом
 

Вложения

  • 1604154054597.png
    1604154054597.png
    6.8 КБ · Просмотры: 28
неплохая статья, не заметил чтоб автор упомянул про библиотеки фриды где есть стандартный обход ссл и много разных плющек, типа скрытия рута и тд, если бы в качестве примера рассмотрел бы смена камер на фото из памяти думаю проголосовавших было бы намного больше) , а так ждем продолжения и удачи в конкурсе
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх