Home Internals Mobile TCP1P Writeup
Post
Cancel

Internals Mobile TCP1P Writeup

Mobile Internals Challenge TCP1P Capture The Flag Writeup by azka

Author: aimardcr

Background

Seperti yang sudah ada dideskripsi challenge nya, untuk menyelesaikan challenge ini kita perlu untuk membuat malicious/exploit aplikasi sendiri yang dimana nanti akan diinstall di Virtual Android Device dari TCP1P.

Sebelum kita membuat malicious code atau exploitnya, penting bagi kita untuk memahami aplikasi internals ini secara mendalam. Langkah pertama yang krusial adalah menganalisis aplikasi tersebut dengan menggunakan tools decompiler. Decompiler ini memungkinkan kita untuk membuka aplikasi “challenge” menjadi source code yang dapat kita baca dan pahami. Saya pribadi menggunakan tools decompiler dari jadx untuk melakukan ini.

Cara menggunakan jadx:

  1. Buka exe dari jadx yang sudah didownload, dan akan menampilkan seperti gambar berikut ini.

  2. Kita klik tombol Open file dan kemudian cari .apk challenge yang sudah didownload oleh teman teman.
  3. Setelah itu jadx akan menalisis nya dan mendecompile apk tersebut agar dapat kita baca, beriku ini hasil decompile dari jadx.

  4. Cari nama package dari aplikasi challenge terlebih dahulu, untuk mengetahui nama package dari aplikasi ini adalah dengan melihat code dari file AndroidManifest.xml terlebih dahulu, yang didapatkan dari isi dari Resources -> AndroidManifest.xml.

  5. Buka file AndroidManifest.xml nya dan cari kata MainActivity, karena biasanya developer android menggunakan MainActivity sebagai class utama dari aplikasi mereka.

  6. Dalam contoh kasus ini nama package dari aplikasi ini adalah com.kuro.internals.
  7. Setelah mendapatkan nama package dari aplikasi nya, kita bisa melihat source code dari aplikasinya lewat jadx yang ada pada Source code -> [nama package dari aplikasi] contohnya Source Code -> com.kuro.internals.

Internals

Link aplikasi: challenge.apk

Diberikan challenge dengan nama Internals dengan deskripsi sebagai berikut:

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

Static Analysis

Dari deskripsi yang diberikan terlihat kita dichallenge oleh pembuat soal terkait knowledge kita tentang andoir internals, dan pembuat soal juga melarang kita untuk menggunakan library external untuk melakukan exploit nya, kalaupun kita menggunakan library external kita akan mendapatkan pengurangan point.

Sekarang coba kita install dan buka terlebih dahulu aplikasi challenge ini dan melihat bagaimana tampilan nya.

Terlihat aplikasi ini meminta url yang akan mendownload payload.dex dan akan meload dex nya.

Oke setelah membaca deskripsi dan isi aplikasi dari soal kita perlu untuk melihat terlebih dahulu source code dari aplikasi internals ini, dan didpatkan satu activity yaitu MainActivity saja:

Dengan isi dari activitynya sebagai berikut:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
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();
        }
    }
}

Bisa dilihat bahwa aplikasi menggunakan layout activity_main yang bisa didapatkan di jadx didalam folder Resources/res/layout/activity_main.xml, aplikasi ini juga mempunyai 1 button dan 1 EditText.

Jadi simplenya ketika user sudah memasukan url kedalam box dan menekan button nya maka aplikasi akan mengambil url tersebut dan melemparkannya ke fungsi downloadDex, jika user tidak memasukan url kedalam box maka akan memunculkan popup URL cannot be empty!.

downloadDeX

Pada fungsi downloadDex bisa dilihat bahwa aplikasi akan memunculkan dialog Downloading... dan akan melemparkannya ke fungsi AnonymousClass2.

AnonymousClass2

berikut ini isi dari fungsi AnonymousClass2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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();
            }
        }
    }

loadDex

Setelah membaca source code dari fungsi AnonymousClass2 bisa dilihat bahwa dia mencoba untuk mendownload file dex dari url yang kita berikan dan menyimpannya dengan nama file payload.dex yang kemudian akan dialihkan ke fungsi loadDex().

Dan disinilah bagian menariknya muncul dan bagaimana kita akan membuat malicious dex nya.

Yang dimana dalam fungsi ini dia mengecheck terlebih dahulu apakah ada file payload.dex didalam folder files aplikasi kita, perlu dinote setiap file yang diload oleh android studio itu ada pada folder /data/data/[nama package apk]/files, jadi dalam kode ini aplikasi challenge akan mengecheck terlebih dahulu apakah file payload.dex itu ada atau tidak didalam folder files kita.

Selanjutnya ketika file payload.dex itu ada didalam folder files, payload.dex akan diload dengan DexClassLoader yang nantinya akan dicheck dengan menggunakan Reflection untuk meload class didalam file dex nya, yang dimana dikode ini aplikasi challenge berusaha untuk meload class dari package com.kuro.payload dengan nama class Main dan juga dia mengambil method dari execute untuk dijalankan diaplikasi challenge, setelah berhasil loadClass dari payload.dex dia akan mengecheck nama package dari aplikasi challenge yang dimana saat ini masihlah com.kuro.internals sudah berubah ke nama package baru atau tidak yaitu l33t_h4x0r, ketika kondisi ini terpenuhi aplikasi akan menampilkan flag nya.

Creating a malicious dex file

Setelah berhasil memahami bagaimana alur dari aplikasi ini berjalan, penulis sudah mempunya gambaran tentang bagaimana membuat malicious dex nya agar ketika dex yang penulis buat nanti bisa merubah packageName dari aplikasi challenge dari com.kuro.internals ke l33t_h4x0r.

Dari hint yang diberikan oleh pembuat soal, pembuat soal meminta kita untuk melakukan osint digithub akunnya dia.

Akhirnya penulis menemukan akun githubnya dan menemukan repository yang menarik didalamnya yaitu APKKiller, didalam repository ini pembuat soal memberitahukan bahwa dengan menggunakan Reflection kita bisa membaca dan memodifikasi internal classes dan fields nya.

Setelah membaca dan melakukan trial and error tentang Reflection penulis menemukan cara bahwa kita bisa menggunakan class dari ActivityThread untuk mengubah packageName ke packageName yang kita mau.

Cobalah untuk membuat projek baru di Android Studio dengan setup sebagai berikut:

  1. Pilih Empty Views Activity lalu Next.

  2. Buatlah nama terserah kalian, asalkan nama package nya itu com.kuro.payload karena pada aplikasi challenge package ini yang akan di load, lalu pilihlah Java sebagai bahasa pemrogramannya, karena Reflection bisa kita pakai di java.

  3. Setelah itu klik Finish dan tunggu sampai android studio menyiapkan setupnya.
  4. Setelah semuanya sudah selesai, kita buat class baru dengan nama Main karena aplikasi challenge akan meload class dari package kita dengan nama Main dengan klik kanan pada package com.kuro.payload lalu klik New -> Java Class.

  5. Masukan nama Main dan enter.

  6. Kita akan memakai activity MainActivity terlebih dahulu untuk debugging.

Jadi step pertama adalah cari terlebih dahulu field dari packageName dan kemudian baru kita set ke value yang baru

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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();
        }
    }
}

Coba gunakan kodingan diatas dan jalankan, lalu coba untuk melihat logcat nya karena saya memasukan Log.e disitu untuk debugging dan melihat isi field dari ActivityThread.

Dan didapatlah isi field dari class ActivityThread.

Seperti yang diberikan hint oleh pembuat soal bahwa getPackageName itu terdapat pada mPackageInfo.

Setelah menghabiskan banyak waktu disini, akhirnya saya menemukan bahwa didalam fields mBoundApplication terdapat field info

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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();
        }
    }
}

Dan akhirnya didapatilah field mPackageName

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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();
        }
    }
}

Setelah mendapatkan field yang benar, sekarang tinggal merubah value dari mPackageName ini ke l33t_h4x0r dengan kode berikut ini:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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();
        }
    }
}

Setelah dijalankan dan dilihat dilogcat package name sudah berhasil dirubah ke l33t_h4x0r.

Setelah itu tinggal copy code nya dan masukan kedalam class Main dan didalam fungsi execute.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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();
        }

    }
}

Setelah itu set gradle nya agar ketika dibuild classes.dex nya itu hanya satu dan tidak multiple dengan menambahkan config sebagai berikut:

Setelah itu kita build projek ini.

Akan muncul popup di pojok kanan bawah seperti berikut ini.

Klik locate dan akan diarahkan ke folder apk yang sudah dibuild:

Masuk kedalam folder debug.

Dan app-debug.apk ini adalah hasil compile kita tadi, setelahnya kita buka apk ini di jadx untuk kita ambil classes.dex nya dan kita pakai di aplikasi challenge.

folder apk hasil compile ada di <Nama Folder Projek>\app\build\outputs\apk\debug.

Setelah dibuka dengan jadx, kita save all hasil decompile ini dan masukan kedalam sebuah folder, saya sendiri memasukannya ke folder kelasss.

Setelah itu kita copy classes.dex nya yang terdapat di folder kelasss/resources/classes.dex ke folder yang lain untuk bisa kita transfer ke aplikasi challenge.

Saya memasukannya di folder internals saja dan saya rubah namanya ke payload.dex dan menjalankan http.server dan ngrok, setelah itu tinggal masukan url nya ke box dan exploit kita berhasil untuk mendapatkan flagnya.

Tinggal dijalankan di Virtual Android Device dan didapatkan flagnya:

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
This post is licensed under CC BY 4.0 by the author.
Contents
Trending Tags