SharifCTF-2016/android-app

From aldeid
Jump to navigation Jump to search

[[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:

MainActivity.class
package com.example.ctf2;

import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends Activity
{
  Button a;
  EditText b;
  TextView c;
  int d = 123;
  String e = "Code";

  static
  {
    System.loadLibrary("adnjni");
  }

  public native int IsCorrect(String paramString);

  public void onCreate(Bundle paramBundle)
  {
    super.onCreate(paramBundle);
    setContentView(2130903040);
    this.a = ((Button)findViewById(2131230721));
    this.b = ((EditText)findViewById(2131230720));
    this.c = ((TextView)findViewById(2131230722));
    this.e = Build.SERIAL;
    this.d = 114366;
    this.a.setOnClickListener(new a(this));
  }

  public native int processObjectArrayFromNative(String paramString);
}
  
a.class
package com.example.ctf2;

import android.text.Editable;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;
import android.widget.TextView;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

class a
  implements View.OnClickListener
{
  a(MainActivity paramMainActivity)
  {
  }

  public void onClick(View paramView)
  {
    new String(" ");
    paramView = this.a.b.getText().toString();
    Log.v("EditText", this.a.b.getText().toString());
    new String("");
    int i = this.a.processObjectArrayFromNative(paramView);
    int j = this.a.IsCorrect(paramView);
    paramView = this.a.d + i + " ";
    try
    {
      Object localObject = MessageDigest.getInstance("MD5");
      ((MessageDigest)localObject).update(paramView.getBytes());
      paramView = ((MessageDigest)localObject).digest();
      localObject = new StringBuffer();
      i = 0;
      while (true)
      {
        if (i >= paramView.length)
        {
          if ((j == 1) && (this.a.e != "unknown"))
            this.a.c.setText("Sharif_CTF(" + ((StringBuffer)localObject).toString() + ")");
          if ((j == 1) && (this.a.e == "unknown"))
            this.a.c.setText("Just keep Trying :-)");
          if (j != 0)
            break;
          this.a.c.setText("Just keep Trying :-)");
          return;
        }
        ((StringBuffer)localObject).append(Integer.toString((paramView[i] & 0xFF) + 256, 16).substring(1));
        i += 1;
      }
    }
    catch (NoSuchAlgorithmException paramView)
    {
      paramView.printStackTrace();
    }
  }
}

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