[](ctf=trend-micro-ctf-2017)[](type=analysis,reverse)[](tags=android)[](tools=jd-gui,dex2jar)[](techniques=bruteforce)
# analysis-offensive 300
*Disclaimer : This is not the smart way to do this, but its easy if you have an android development setup.*
So we have an apk file.```bash$ file TrendGacha.apkTrendGacha.apk: Zip archive data, at least v2.0 to extract$ unzip -qq TrendGacha.apk -d TrendGacha$ ls TrendGachaAndroidManifest.xml  classes.dex  lib  META-INF  res  resources.arsc$ cd TrendGacha$ ~/tools/dex2jar/d2j-dex2jar.sh classes.dexdex2jar classes.dex -> ./classes-dex2jar.jar```
I also installed the apk on a device to test it.
![Main](menu.png)
The functions are quite simple. When you click on `GET GACHA!` it'll choose a character of the flag and give it to you. All your leaked chars can then be seen later in the `HISTORY`.
Looking in the output of jd-gui on the jar. The code for `GET GACHA!`.
```javapublic void run()      {        Intent localIntent = new Intent("com.tm.ctf.trendgacha.GET_GACHA");        if (a.N() == null) {          a.b(a.this.i().getApplicationContext());        }        a.N().sendBroadcast(localIntent);        a.a(a.this, true);      }```So the button sends a com.tm.ctf.trendgacha.GET_GACHA intent via broadcast which is then handled by GachaBroadcastReceiver.
```javapublic void onReceive(Context paramContext, Intent paramIntent)  {    Log.i(a, "onReceive: action=" + paramIntent.getAction());    if (!paramIntent.getAction().equals("com.tm.ctf.trendgacha.GET_GACHA")) {      return;    }    paramIntent = paramIntent.getExtras();    if (paramIntent != null) {}    for (int i = Integer.valueOf(paramIntent.getString("TryLoop", "1")).intValue();; i = 1)    {      for (int j = 0; j < i; j++)      {        d.b(paramContext.getApplicationContext());        int n = d.a(paramContext.getApplicationContext());        paramIntent = GachaAPI.getGacha(n);        int k = paramIntent[0];        char c1 = (char)paramIntent[1];        d.a(paramContext.getApplicationContext(), k, c1);        int m = d.b(paramContext.getApplicationContext(), k, c1);        Log.i(a, "TrialCount: " + n + ", Order: " + k + ", Gacha: " + c1 + ", Count: " + m);        paramIntent = c.iterator();        while (paramIntent.hasNext()) {          ((b)paramIntent.next()).b_();        }        Toast.makeText(paramContext, "Got \"" + k + "-" + c1 + "\" !", 0).show();        if (n < 10) {          Toast.makeText(paramContext, "Check History!", 0).show();        }      }      break;    }  }}```This will leak a byte of the flag. We also see a variable `TryLoop` which will repeat the leak `TryLoop` times. Whenever we press the `GET GACHA!` button `TRIAL_COUNT` is increased in the SharedPreferences. The `GachaAPI.getGacha` is called on the value of `TRIAL_COUNT`. Its good that we can broadcast intents via adb so that we can set `TRIAL_COUNT` to a very high value.
```bash$ adb shell am broadcast -a com.tm.ctf.trendgacha.GET_GACHA -e TryLoop 100Broadcasting: Intent { act=com.tm.ctf.trendgacha.GET_GACHA (has extras) }Broadcast completed: result=0```Hoever this only leaks a few bytes of the flag which we can view in History. I tried to set the number to 10000 and more but the process was slow and I got bored.
![History](history.png)
Looking into the source of `GachaAPI.getGacha` we see that it loads a native library.
```javapackage com.tm.ctf.trendgacha;
public class GachaAPI{  static  {    System.loadLibrary("native-lib");  }
  public static native int[] getGacha(int paramInt);}```If only I could run `getGacha` with my parameters. I never did much development in Android, so I downloaded Android Studio, added `com.tm.ctf.trendgacha` in a sample app from the decompiled source and added the jniLibs to include `native-lib`. I added the following code in an activity to retrieve and save the flag to a file.
```javaprotected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_card_view);        Log.i(TAG,"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");        int i = 1;        for(i = 1 ; i < 1000000 ; ++i){            int[] x = GachaAPI.getGacha(i);            appendLog("Result : " +(char)x[1] + " : " + Arrays.toString(x) );        }    }```This succesfully gave me the flag. Just later I realized that I could have controlled the parameter to `GachaAPI.getGacha` from the `TRIAL_COUNT` in SharedPreferences. SharedPreferences are usually saved in XML format in /data/data/< appname >/shared_prefs/*.xml. But we need root on the device to pull and push these files. So to test this I set up a new AVD (already rooted!) and installed the app.
```bash$ ~/Android/Sdk/platform-tools/adb pull /data/data/com.tm.ctf.trendgacha/shared_prefs/PREFERENCES.xml/data/data/com.tm.ctf.trendgacha/shared_... pulled. 0.2 MB/s (1892 bytes in 0.009s)```In PREFERENCES.xml I increased the `TRIAL_COUNT`.```xml<int name="TRIAL_COUNT" value="3000863" />```And then pushed it back and restarted the application.```bash$  ~/Android/Sdk/platform-tools/adb push PREFERENCES.xml /data/data/com.tm.ctf.trendgacha/shared_prefs/PREFERENCES.xmlPREFERENCES.xml: 1 file pushed. 0.2 MB/s (1893 bytes in 0.009s)$  ~/Android/Sdk/platform-tools/adb  shell am broadcast -a com.tm.ctf.trendgacha.GET_GACHA -e TryLoop 100Broadcasting: Intent { act=com.tm.ctf.trendgacha.GET_GACHA (has extras) }Broadcast completed: result=0```
This will give us the flag.
![Flag](final.png)