SharifCTF-2016/android-app
[[Category:Architecture/Android]
You are here | srm (50 points)
|
Description
The file (Sharif_CTF.apk) is an Android application:
MD5 | e8f19d21e6fd91fb134bca8bdc5dd3b6 |
---|---|
SHA1 | 5c6005941a32fed7a874836ddd8eca87e48f085a |
SHA256 | f52c38abf6a52d3d430e5d940e3aab1a8269fa80e409691a2211f7ababcfd40c |
File | Java archive data (JAR) |
Running the application
When run, the application shows a form to enter a serial and a Login button. If the serial is invalid, the following message appears:
Analysis
Use dex2jar and JD-GUI
Let's first use dex2jar to convert the *.apk to a *.jar file that we can read with JD-GUI:
$ /data/tools/dex2jar-2.0/d2j-dex2jar.sh Sharif_CTF.apk dex2jar Sharif_CTF.apk -> ./Sharif_CTF-dex2jar.jar
Now, we can read Sharif_CTF-dex2jar.jar with JD-GUI:
$ $ /data/tools/jd-gui/jd-gui Sharif_CTF-dex2jar.jar
It results in the 2 following files:
|
|
The MainActivity.class file is responsible for the layout. It displays the form and calls the setOnClickListener function when the Login button is clicked. On line #20, we also see that the adnjni.so library is loaded with a call to System.loadLibrary.
The code for the Login button is in the second file (a.class). We see that the serial is saved to paramView on line #22 and passed to the IsCorrect function of the external library on line #26.
Extract files with apktool
Now, let's extract files with apktool:
$ java -jar /data/tools/apktool/apktool_2.0.1.jar d Sharif_CTF.apk I: Using Apktool 2.0.1 on Sharif_CTF.apk I: Loading resource table... I: Decoding AndroidManifest.xml with resources... I: Loading resource table from file: /home/unknown/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...
It will result in the following files:
Sharif_CTF ├── AndroidManifest.xml ├── apktool.yml ├── lib │ └── armeabi │ └── libadnjni.so ├── original │ ├── AndroidManifest.xml │ └── META-INF │ ├── CERT.RSA │ ├── CERT.SF │ └── MANIFEST.MF ├── res │ ├── drawable-hdpi-v4 │ │ └── ic_launcher.png │ ├── drawable-mdpi-v4 │ │ └── ic_launcher.png │ ├── drawable-xhdpi-v4 │ │ └── ic_launcher.png │ ├── drawable-xxhdpi-v4 │ │ └── ic_launcher.png │ ├── layout │ │ └── activity_main.xml │ ├── menu │ │ └── main.xml │ ├── values │ │ ├── dimens.xml │ │ ├── ids.xml │ │ ├── public.xml │ │ ├── strings.xml │ │ └── styles.xml │ ├── values-v11 │ │ └── styles.xml │ ├── values-v14 │ │ └── styles.xml │ └── values-w820dp-v13 │ └── dimens.xml └── smali └── com └── example └── ctf2 ├── a.smali └── MainActivity.smali
Analysis of libadnjni.so in IDA-Pro
Let's open libadnjni.so in IDA-Pro and jump to the Java_com_example_ctf2_MainActivity_IsCorrect method. Using the pseudo-code module (F5) really helps. It produces the following code:
signed int __fastcall Java_com_example_ctf2_MainActivity_IsCorrect(int a1, int a2, int a3)
{
int v3; // r5@1
signed int v4; // r6@1
signed int v5; // r0@1
signed int v6; // r1@3
signed int result; // r0@14
int v8; // [sp+10h] [bp-50h]@1
char *v9; // [sp+14h] [bp-4Ch]@1
int v10; // [sp+1Ch] [bp-44h]@1
signed int v11; // [sp+20h] [bp-40h]@0
char v12; // [sp+24h] [bp-3Ch]@1
char v13; // [sp+25h] [bp-3Bh]@1
char v14; // [sp+26h] [bp-3Ah]@1
char v15; // [sp+27h] [bp-39h]@1
char v16; // [sp+28h] [bp-38h]@1
char v17; // [sp+29h] [bp-37h]@1
char v18; // [sp+2Ah] [bp-36h]@1
char v19; // [sp+2Bh] [bp-35h]@1
char v20; // [sp+2Ch] [bp-34h]@1
char v21; // [sp+2Dh] [bp-33h]@1
char v22; // [sp+2Eh] [bp-32h]@1
char v23; // [sp+2Fh] [bp-31h]@1
char v24; // [sp+30h] [bp-30h]@1
char v25; // [sp+31h] [bp-2Fh]@1
char v26; // [sp+32h] [bp-2Eh]@1
char v27; // [sp+33h] [bp-2Dh]@1
char v28; // [sp+34h] [bp-2Ch]@1
char v29; // [sp+35h] [bp-2Bh]@1
char v30; // [sp+36h] [bp-2Ah]@1
char v31; // [sp+37h] [bp-29h]@1
char v32; // [sp+38h] [bp-28h]@1
char v33; // [sp+39h] [bp-27h]@1
char v34; // [sp+3Ah] [bp-26h]@1
char v35; // [sp+3Bh] [bp-25h]@1
char v36; // [sp+3Ch] [bp-24h]@1
char v37; // [sp+3Dh] [bp-23h]@1
char v38; // [sp+3Eh] [bp-22h]@1
char v39; // [sp+3Fh] [bp-21h]@1
char v40; // [sp+40h] [bp-20h]@1
char v41; // [sp+41h] [bp-1Fh]@1
char v42; // [sp+42h] [bp-1Eh]@1
char v43; // [sp+43h] [bp-1Dh]@1
char v44; // [sp+44h] [bp-1Ch]@1
char v45; // [sp+45h] [bp-1Bh]@1
int v46; // [sp+48h] [bp-18h]@1
v8 = a3;
v3 = a1;
v46 = _stack_chk_guard;
v9 = (char *)(*(int (**)(void))(*(_DWORD *)a1 + 676))();
v12 = 101;
v13 = 102;
v14 = 53;
v15 = 55;
v16 = 102;
v17 = 51;
v18 = 102;
v19 = 101;
v20 = 51;
v21 = 99;
v22 = 102;
v23 = 54;
v24 = 48;
v25 = 51;
v26 = 99;
v27 = 48;
v28 = 51;
v29 = 56;
v30 = 57;
v31 = 48;
v32 = 101;
v33 = 101;
v34 = 53;
v35 = 56;
v36 = 56;
v37 = 56;
v38 = 55;
v39 = 56;
v40 = 99;
v41 = 48;
v42 = 101;
v43 = 99;
v44 = 0;
v4 = 0;
v45 = 53;
v10 = j_j_strcmp(v9, &v12);
v5 = 1701458332;
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
v6 = v5;
v5 = 809244963;
if ( v6 <= 1701458331 )
break;
if ( v6 != 1701458332 )
goto LABEL_16;
v5 = -333293478;
if ( v10 )
v5 = -158041539;
}
if ( v6 <= 809244962 )
break;
if ( v6 != 809244963 )
goto LABEL_16;
(*(void (__fastcall **)(int, int, char *))(*(_DWORD *)v3 + 680))(v3, v8, v9);
v11 = v4;
v5 = -326599761;
}
if ( v6 != -158041539 )
{
v4 = 1;
v5 = -158041539;
if ( v6 != -333293478 )
break;
}
}
if ( v6 != -326599761 )
{
while ( 1 )
LABEL_16:
;
}
result = v11;
if ( _stack_chk_guard != v46 )
j_j___stack_chk_fail(v11);
return result;
}
We immediately notice the use of variables containing ASCII values that we can use to build a string.
Script and solution
The following script:
#!/usr/bin/env python2
s = [101, 102, 53, 55, 102, 51, 102, 101,
51, 99, 102, 54, 48, 51, 99, 48,
51, 56, 57, 48, 101, 101, 53, 56,
56, 56, 55, 56, 99, 48, 101, 99]
print ''.join([chr(i) for i in s])
...will output:
ef57f3fe3cf603c03890ee588878c0ec
Which is actually the solution to get the flag. If you enter this serial, the following flag is given:
833489ef285e6fa80690099efc5d9c9d
Comments
Keywords: sharif 2016 challenge reversing