The-FLARE-On-Challenge-2015/Challenge-6
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
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
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
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