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

From aldeid
Jump to: navigation, 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:

[email protected]:/opt/mobisec/Android/sdk/tools$ ./emulator -avd MobisecLab

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

[email protected]:/data/flareon$ adb install android.apk

Here is what the application looks like:

Flare-on-challenge-2015-l06-emulator-android-01.png Flare-on-challenge-2015-l06-emulator-android-02.png

Decompilation

Let's use apktool to decompile the apk:

[email protected]:/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

Info.png
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

Info.png
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

Flare-on-challenge-2015-l6-libvalidate so-check-length.png

0xECC

Flare-on-challenge-2015-l6-libvalidate so-0xECC.png
  • 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:

Flare-on-challenge-2015-0x2214-primes.png

0xEEE

Flare-on-challenge-2015-l6-libvalidate so-0xEEE.png

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

Flare-on-challenge-2015-l6-0xf14.png

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.

Flare-on-challenge-2015-l6-offsets array.png

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.

Flare-on-challenge-2015-l6-libvalidate so-final-test.png

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
[email protected] 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:

Flare-on-challenge-2015-l6-factorization-04.png

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]

Flare-on-challenge-2015-l6-android-solution.png


Comments

blog comments powered by Disqus

Keywords: reverse-engineering challenge flare fireeye android apk arm