Rating:

# Fire in the Androiddd (6 solves / 497 points)
To decrypt something we need encrypted stuff

## Attachments:
* [chall.apk](./chall.apk)

## Source Code:
* [Source](https://github.com/karma9874/My-CTF-Challenges/tree/main/DarkCON-ctf/Reverse/Fire_in_the_Androiddd)

## Solution

On running the app there is just a basic with some text and nothing else.

![alt text](./app.PNG)

On reversing the apk we have some java files so following the source code of `MyReceiver` we see that its actually waiting for an intent with argument as flag and is latter forwarded to the `loader` class (AsyncTask) which does the checking between the intent's argument and `magic function from native library`, if the checking is correct then it say correct flag if not then wrong flag.

```java
package com.application.darkcon;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.widget.Toast;

public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String data = intent.getStringExtra("flag");

if(data_receiver.data != null && data != null){
new loader(context).execute(data,data_receiver.data);
}else{
Toast.makeText(context, "Something went wrong", Toast.LENGTH_LONG).show();
}
}

class loader extends AsyncTask<String,Void,Boolean> {
Context context;

public loader(Context context){
this.context = context;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
Toast.makeText(context, "Checking your flag", Toast.LENGTH_SHORT).show();
}

@Override
protected Boolean doInBackground(String... strings) {
String[] parts = strings[1].split(",");
long[] ints = new long[parts.length];
for (int i = 0; i < parts.length; i++)ints[i] = Long.parseLong(parts[i]);
return magic(strings[0].getBytes(),ints);
}

@Override
protected void onPostExecute(Boolean aBoolean) {
if(aBoolean){
Toast.makeText(context, "Well thats the correct flag ", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(context, "Nope wrong flag :(", Toast.LENGTH_SHORT).show();
}

}
}
static {
System.loadLibrary("native-lib");
}
public native boolean magic(byte[] str,long[] str1);
}

```

We can find the more details in AndroidManifest.xml file
```xml
<receiver android:name="com.application.darkcon.MyReceiver" android:exported="true" android:enabled="true">
<intent-filter>
<action android:name="flag_checker"/>
</intent-filter>
</receiver>
```

As we can see the `android:exported` is true so that we can call the `flag_checker` intent from anywhere

By running this we can get the prompt of wrong flag
```bash
adb shell am broadcast -a flag_checker -n com.application.darkcon/.MyReceiver --es flag "testing123"
```
So moving on to the native-lib

Magic function takes two arguments byte string array and long array, the first argument is the intents flag input and 2nd argument is taken from static variable of `data_receiver.data`, so from `data_receiver` class we see that it is again the basic firebase data fetch method but this time the fetched data is stored into the static variable `data`,

```java
public class data_receiver {
public static String data;

public void getData() {

FirebaseFirestore firebaseFirestore = FirebaseFirestore.getInstance();
firebaseFirestore.collection("encryption").get().addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
data = null;
}
}).addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
@Override
public void onComplete(@NonNull Task<QuerySnapshot> task) {
if(task.isSuccessful()){
QuerySnapshot snapshot = task.getResult();
for(DocumentSnapshot snapshot1 : snapshot){
data = snapshot1.getString("encrypted_flag");
}
}
}
});
}
}
```

So to check the data we can use `frida dynamic hooking method` to catch the value from `data` variable using a simple frida script

```py
import frida, sys
import time

def my_message_handler(message , payload):
print(message)
print(payload)

jscode = """
setImmediate(function() {
setTimeout(test, 1000);
});

function test(){
Java.perform(function(){
var a=Java.use("com.application.darkcon.data_receiver");
console.log(a.data.value);
});
}
"""
process = frida.get_usb_device().attach('com.application.darkcon')
script = process.create_script(jscode)

print('[ * ] Frida Running')

script.on("message" , my_message_handler)

script.load()
input()
```

This script will return the value of `data` variable of class data_receiver as

```
101,96,112,110,77,101,202,470,1506,4758,16815,58877,208123,742855,2674489,9694735,35357570,129644713,477638735,1767263206,2269153033,2991430638,1288250377,3757197244,1413958429,43422424,2072914473,2325361044,2600037558,3008195127,3276256895,4169229947,300814809,3929270464,2526730686,2527522239,645964816,1351610749,573153031,1347646066,1945953402,3824419424,480774039,2833665279,2366904092,2809807660,3295802436,3644429150,720643560,906311378,992169127,1211139059,1465960990,4269303883,3179939394,4095898594,580984841,3596758568,1063564231,3288906933
```

So now we have the second argument of `magic function`

Time to reverse the native library `magic function` using ghidra we see that it has two function `Java_com_application_darkcon_MyReceiver_magic` and `looper` function

```c
uint Java_com_application_darkcon_MyReceiver_magic
(_JNIEnv *param_1,undefined4 param_2,_jarray *param_3,_jarray *param_4)

{
char cVar1;
uint uVar2;
uint uVar3;
int iVar4;
int iVar5;
int iVar6;
uint uVar7;
uint local_2c;
byte local_15;

iVar4 = GetArrayLength(param_1,param_3);
iVar5 = GetArrayLength(param_1,param_4);
if (iVar4 == iVar5) {
iVar5 = GetByteArrayElements(param_1,(_jbyteArray *)param_3,(uchar *)0x0);
iVar6 = GetLongArrayElements(param_1,(_jlongArray *)param_4,(uchar *)0x0);
local_2c = 0;
while ((int)local_2c < iVar4) {
uVar2 = *(uint *)(iVar6 + local_2c * 8);
uVar3 = *(uint *)(iVar6 + 4 + local_2c * 8);
cVar1 = *(char *)(iVar5 + local_2c);
uVar7 = looper(local_2c);
if ((uVar2 ^ (int)cVar1 ^ uVar7 | uVar3) != 0) {
local_15 = 0;
goto LAB_0001871b;
}
local_2c = local_2c + 1;
}
local_15 = 1;
}
else {
local_15 = 0;
}
LAB_0001871b:
return (uint)local_15;
}
```

In magic function its checking for `param3` == `param4` which means the input should be the same length of `data_receiver.data` variable and then its looping over `param3` size and doing xor of `param_3` and `looper()` and checking with the `data_receiver.data`

```c
{
int iVar1;
int in_GS_OFFSET;
int aiStack64 [3];
int local_34;
int *local_30;
undefined **local_2c;
int local_28;
uint local_24;
int local_20;
int *local_1c;
int local_18;

local_1c = aiStack64;
local_2c = &__DT_PLTGOT;
local_18 = *(int *)(in_GS_OFFSET + 0x14);
local_20 = param_1 + 1;
iVar1 = -(param_1 * 4 + 0x13 & 0xfffffff0);
local_30 = (int *)((int)aiStack64 + iVar1);
*(undefined4 *)((int)aiStack64 + iVar1 + 4) = 1;
*local_30 = 1;
local_24 = 2;
while (local_24 <= param_1) {
local_30[local_24] = 0;
local_28 = 0;
while (local_28 < (int)local_24) {
local_30[local_24] =
local_30[local_28] * local_30[(local_24 - local_28) + -1] + local_30[local_24];
local_28 = local_28 + 1;
}
local_24 = local_24 + 1;
}
local_34 = local_30[param_1];
if (*(int *)(in_GS_OFFSET + 0x14) == local_18) {
return local_34;
}
*(undefined4 *)((undefined *)local_1c + -4) = 0x18961;
__stack_chk_fail();
}
```

If you try to run this code manually this will return some requence like `1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796....`
With little bit of googling we can find that the code is actually a `catalan number generator`

But the issue is that due to data types its causing int overflow on the large catalan number, so while xoring we need to solve overflow

Catlan Number generator
```c
#include <iostream>
using namespace std;

unsigned long int catalanDP(unsigned int n)
{
unsigned long int catalan[n + 1];
catalan[0] = catalan[1] = 1;
for (int i = 2; i <= n; i++) {
catalan[i] = 0;
for (int j = 0; j < i; j++)
catalan[i] += catalan[j] * catalan[i - j - 1];
}

return catalan[n];
}

int main()
{
for (int i = 0; i < 60; i++)
cout << catalanDP(i) << " ";
return 0;
}
```

```py
catalan_generate = [1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190, 6564120420, 24466267020, 91482563640, 343059613650, 1289904147324, 4861946401452, 18367353072152, 69533550916004, 263747951750360, 1002242216651368, 3814986502092304, 14544636039226909, 55534064877048198, 212336130412243110, 812944042149730764, 3116285494907301262, 11959798385860453492, 9057316177202639132, 10713166123620736856, 16342585076431942214, 2689383809735779348, 5102839245063848452, 11119451935032739784, 14452914362348096668, 14071982300670990120, 11142706294756623192, 3114992555900662896, 16682282172542456626, 11604953028367754716, 10347338891492931260, 6533841209031609592, 17577068357745673116, 11934029089710590568, 3367710287996676216, 1700012784093890096, 9253156062895436676, 11766576147974922296, 7683395182710107672, 16195324623391601584, 15200231439582411976]

enc = [101,96,112,110,77,101,202,470,1506,4758,16815,58877,208123,742855,2674489,9694735,35357570,129644713,477638735,1767263206,2269153033,2991430638,1288250377,3757197244,1413958429,43422424,2072914473,2325361044,2600037558,3008195127,3276256895,4169229947,300814809,3929270464,2526730686,2527522239,645964816,1351610749,573153031,1347646066,1945953402,3824419424,480774039,2833665279,2366904092,2809807660,3295802436,3644429150,720643560,906311378,992169127,1211139059,1465960990,4269303883,3179939394,4095898594,580984841,3596758568,1063564231,3288906933]

flag = ""
for i,c in enumerate(catalan):
flag += chr((c ^ enc[i]) & 0xFF)

print(flag)
#darkCON{th3_w31rd_c0mb1nat10n_of_fr1da_4nd_c4t4l4n_ov3rf10w}

```

```bash
adb shell am broadcast -a flag_checker -n com.application.darkcon/.MyReceiver --es flag "darkCON{th3_w31rd_c0mb1nat10n_of_fr1da_4nd_c4t4l4n_ov3rf10w}"
```

## Flag
> darkCON{th3_w31rd_c0mb1nat10n_of_fr1da_4nd_c4t4l4n_ov3rf10w}

Original writeup (https://github.com/karma9874/CTF-Writeups/blob/master/DarkCON_CTF/Fire%20in%20the%20Androiddd/Fire_in_the_Androiddd.md).