The-FLARE-On-Challenge-2015/Challenge-6

From aldeid
Jump to navigation Jump to search
You are here
Challenge 6

Introduction

File

Uncompress 63C64502837A89CA0147095726DF8262.zip (password is "flare") and you will get a file named android.apk with following properties:

MD5 8afcfdae4ddc16134964c1be3f741191
SHA1 07e1333d5fc331f416e144078ea4293356719bb1
SHA256 72d8f9b322d46c3a1eaa98ae8bb02d173ee835dc7d6a88034e9bf7b48e3a9608
File type Java archive data (JAR)

What does the Android application look like?

Obviously, we have to deal with an Android application. Let's see what it looks like. I used MobiSec in a virtual machine to test the application in an emulator:

mobisec@ubuntu:/opt/mobisec/Android/sdk/tools$ ./emulator -avd MobisecLab

Once the emulator has started, I installed the Android application as follows:

mobisec@ubuntu:/data/flareon$ adb install android.apk

Here is what the application looks like:

Decompilation

Let's use apktool to decompile the apk:

mobisec@ubuntu:/data/flareon$ apktool d android.apk
I: Using Apktool 2.0.0-RC3 on android.apk
I: Loading resource table...
I: Decoding AndroidManifest.xml with resources...
I: Loading resource table from file: /home/mobisec/apktool/framework/1.apk
I: Regular manifest package...
I: Decoding file-resources...
I: Decoding values */* XMLs...
I: Baksmaling classes.dex...
I: Copying assets and libs...
I: Copying unknown files...
I: Copying original files...

Decompiled files

AndroidManifest.xml

From the AndroidManifest.xml file, we can see that the application has 2 user interfaces: MainActivity and ValidateActivity

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.flare_on.flare" platformBuildVersionCode="22" platformBuildVersionName="5.1.1-1819727">
    <application android:allowBackup="true" android:icon="@drawable/icon" android:label="@string/app_name" android:theme="@style/AppTheme">
        <activity android:label="@string/app_name" android:name="com.flareon.flare.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <activity android:label="@string/title_activity_validate_email" android:name="com.flareon.flare.ValidateActivity" android:parentActivityName="com.flareon.flare.MainActivity">
            <meta-data android:name="android.support.PARENT_ACTIVITY" android:value="com.flareon.flare.MainActivity"/>
        </activity>
    </application>
</manifest>

MainActivity.smali

Note
For details about the Smali syntax, you can refer to this page.

In the MainActivity.smali file, we find a validateEmail() method that sends the user input to the ValidateActivity.

.method public validateEmail(Landroid/view/View;)V
    .locals 4
    .param p1, "view"    # Landroid/view/View;

    .prologue
    .line 21
    new-instance v2, Landroid/content/Intent;

    const-class v3, Lcom/flareon/flare/ValidateActivity;

    invoke-direct {v2, p0, v3}, Landroid/content/Intent;-><init>(Landroid/content/Context;Ljava/lang/Class;)V

    .line 22
    .local v2, "intent":Landroid/content/Intent;
    const v3, 0x7f0c004f

    invoke-virtual {p0, v3}, Lcom/flareon/flare/MainActivity;->findViewById(I)Landroid/view/View;

    move-result-object v1

    check-cast v1, Landroid/widget/EditText;

    .line 23
    .local v1, "emailAddress":Landroid/widget/EditText;
    invoke-virtual {v1}, Landroid/widget/EditText;->getText()Landroid/text/Editable;

    move-result-object v3

    invoke-virtual {v3}, Ljava/lang/Object;->toString()Ljava/lang/String;

    move-result-object v0

    .line 24
    .local v0, "email":Ljava/lang/String;
    const-string v3, "com.flare_on.flare.MESSAGE"

    invoke-virtual {v2, v3, v0}, Landroid/content/Intent;->putExtra(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;

    .line 25
    invoke-virtual {p0, v2}, Lcom/flareon/flare/MainActivity;->startActivity(Landroid/content/Intent;)V

    .line 26
    return-void
.end method

activity_main.xml

The ValidateEmail() method is referenced in res/layout/activity_main.xml which indicates that this method is called when the Validate button is clicked:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:background="#ffffffff" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingBottom="@dimen/activity_vertical_margin" android:layout_width="fill_parent" android:layout_height="fill_parent"
  xmlns:android="http://schemas.android.com/apk/res/android">
    <EditText android:gravity="center" android:id="@id/emailAddress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:hint="Enter Text" android:ems="16" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:inputType="textEmailAddress" />
    <Button android:id="@id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Validate" android:layout_below="@id/emailAddress" android:layout_centerHorizontal="true" android:onClick="validateEmail" style="?android:attr/buttonStyleSmall" />
    <ImageView android:id="@id/imageView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="10.0dip" android:src="@drawable/flareon" android:layout_above="@id/emailAddress" android:layout_centerHorizontal="true" />
</RelativeLayout>

ValidateActivity.smali

Note
For details about the Smali syntax, you can refer to this page.

The ValidateActivity.smali file informs us that the user input is passed to the validate() method that we will be able to find in a shared library named libvalidate.so:

.class public Lcom/flareon/flare/ValidateActivity;
.super Landroid/support/v7/app/ActionBarActivity;
.source "ValidateActivity.java"


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

    .prologue
    .line 42
    const-string v0, "validate"

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

    .line 43
    return-void
.end method

.method public constructor <init>()V
    .locals 0

    .prologue
    .line 14
    invoke-direct {p0}, Landroid/support/v7/app/ActionBarActivity;-><init>()V

    return-void
.end method


# virtual methods
.method protected onCreate(Landroid/os/Bundle;)V
    .locals 6
    .param p1, "savedInstanceState"    # Landroid/os/Bundle;

    .prologue
    .line 18
    invoke-super {p0, p1}, Landroid/support/v7/app/ActionBarActivity;->onCreate(Landroid/os/Bundle;)V

    .line 20
    new-instance v3, Landroid/widget/TextView;

    invoke-direct {v3, p0}, Landroid/widget/TextView;-><init>(Landroid/content/Context;)V

    .line 21
    .local v3, "textView":Landroid/widget/TextView;
    const/high16 v5, 0x42200000    # 40.0f

    invoke-virtual {v3, v5}, Landroid/widget/TextView;->setTextSize(F)V

    .line 22
    const/16 v5, 0x11

    invoke-virtual {v3, v5}, Landroid/widget/TextView;->setGravity(I)V

    .line 25
    invoke-virtual {p0}, Lcom/flareon/flare/ValidateActivity;->getIntent()Landroid/content/Intent;

    move-result-object v1

    .line 26
    .local v1, "intent":Landroid/content/Intent;
    const-string v5, "com.flare_on.flare.MESSAGE"

    invoke-virtual {v1, v5}, Landroid/content/Intent;->getStringExtra(Ljava/lang/String;)Ljava/lang/String;

    move-result-object v4

    .line 27
    .local v4, "userInput":Ljava/lang/String;
    const-string v5, "US-ASCII"

    invoke-static {v5}, Ljava/nio/charset/Charset;->forName(Ljava/lang/String;)Ljava/nio/charset/Charset;

    move-result-object v5

    invoke-virtual {v5}, Ljava/nio/charset/Charset;->newEncoder()Ljava/nio/charset/CharsetEncoder;

    move-result-object v0

    .line 28
    .local v0, "asciiEnc":Ljava/nio/charset/CharsetEncoder;
    invoke-virtual {v0, v4}, Ljava/nio/charset/CharsetEncoder;->canEncode(Ljava/lang/CharSequence;)Z

    move-result v5

    if-eqz v5, :cond_0

    .line 30
    invoke-virtual {p0, v4}, Lcom/flareon/flare/ValidateActivity;->validate(Ljava/lang/String;)Ljava/lang/String;

    move-result-object v2

    .line 31
    .local v2, "js":Ljava/lang/String;
    invoke-virtual {v3, v2}, Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;)V

    .line 35
    .end local v2    # "js":Ljava/lang/String;
    :goto_0
    invoke-virtual {p0, v3}, Lcom/flareon/flare/ValidateActivity;->setContentView(Landroid/view/View;)V

    .line 36
    return-void

    .line 33
    :cond_0
    const-string v5, "No"

    invoke-virtual {v3, v5}, Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;)V

    goto :goto_0
.end method

.method public native validate(Ljava/lang/String;)Ljava/lang/String;
.end method

libvalidate.so

String length

The program starts with some initialization and checks that the user input is 46 characters long. If it fails, it jumps to offset 0xEAC where the message "No" is displayed. Else, it continues to 0xECC

0xECC

  • Starting from 0xECC, a buffer of size 6952 bytes (3476 words) is created and set to 0.
  • Starting from 0xF40, operations are applied to the user input string to concatenate 2 consecutive characters (e.g. if string is "example", the first loop will save "ex")
  • At 0xEE0, we see the use of a local variable (unk_2214). At this location, we can identify an array of 3476 prime numbers:

0xEEE

Starting from 0xEEE, we see a loop that performs a division of the 2 consecutive characters mentioned before with the primes taken from the primes array. If the remainder of the division is not 0, then it loops to the next prime. Else, the quotient of the division is used as the next numerator until the quotient is higher than 1. A prime factorization is actually performed, as explained here: http://www.mathsisfun.com/prime-factorization.html. This technique can be used in cryptography.

0xF14

Starting from 0xF14, the resulting factorisation is compared to the expected values (offsets_array gives the addresses of the expected factorization array of each memory locations). If the test succeeds, the variable should_be_23 is incremented, else, the variable should_not_be_0 is set to 0.

Final test

Then the code jumps to the final test where we can see that the string "That's it" will be displayed if should_be_23 equals 23 and should_not_be_0 is not equal to 0.

Script and solution

Some math

As explained here, "Prime Factorization is finding which prime numbers multiply together to make the original number.".

In our example, the user input is broken down into groups of 2 characters as follows (provided the user input is "[email protected]"):

ASCII Hex dec prime factorization
ex 0x6578 25976 23 * 171 x 1911
am 0x616d 24941 72 * 509
pl 0x706c 28780 22 * 5 * 1439
e@ 0x6540 25920 26 * 34 * 5
fl 0x666c 26220 22 * 3 * 5 * 19 * 23
ar 0x6172 24946 2 * 12,473
e- 0x652d 25901 59 * 439
on 0x6f6e 28526 2 * 17 * 839
.c 0x2e63 11875 54 * 19
om 0x6f6d 28525 52 * 7 * 163

In the code, the expected factorization arrays are hard coded. Here are the addresses:

  • 0x2214: prime numbers array
  • 0x5004: factorization offsets array

If we refer to the array at offset 0x5004, factorization for the 4th group is located at 0x25458. Here is how it looks like:

prime[0]3 * prime[13]1 * prime[19]1 = 23 * 431 * 711 = 24424 = 0x5F68

0x5F68 translates to _h. So we know that the 4th position is _h. Now, let's script this for the other 22 memory locations.

Script

The following script has been written based on the previous analysis. You can run it inside IDA-Pro (File > Script file...)

#!/usr/bin/env python
import struct

PRIME_NUMBERS_ADDR = 0x2214
FACTORIZATION_ARRAY_ADDR = 0x5004

secret = []

# find prime based on index p
def get_prime(p):
    return Word(PRIME_NUMBERS_ADDR+2*p)

for i in range(23):
    
    # find prime table from offset i:
    base_fact_addr = Dword(FACTORIZATION_ARRAY_ADDR+4*i)
    
    res_fact = 1
    p = 0
    for j in range(3476):
        prime = get_prime(p)
    
        pow = Word(base_fact_addr+2*j)
        if pow != 0:
            res_fact *= prime**pow
        p+=1
    
    secret.append(res_fact)

print ''.join([struct.pack('>H', i) for i in secret])

Solution

The solution is the following email address:

[email protected]


Comments

Keywords: reverse-engineering challenge flare fireeye android apk arm