description | cover | coverY |
---|---|---|
Diving into the code... |
117 |
This is a method of examining source code without actually executing the program. The main objective is to gain a further understanding of what the application is doing, how it is doing those things, as well as a deep dive into the design/implementation. Ultimately, this is done to pinpoint vulnerabilities within the code in order to create an exploit to fulfil the attacker's objectives.
{% embed url="https://asrp.darkwolf.io/ASRP-Plays/static" %}
APKiD will allow us to gain a quick look at the APK to discover what technologies were used for the compilation of the binary itself ranging from detecting compilers, packers, obfuscators, and security techniques.
apkid -r --scan-depth 4 FridaLab.apk
[+] APKiD 2.1.5 :: from RedNaga :: rednaga.io
[*] FridaLab.apk!classes.dex
|-> compiler : r8 without marker (suspicious)
We can see from this output that the default Android compiler, R8 was used to compile Java bytecode into Dalvik (dex) byte code for this APK. We can also see that there is a classes.dex
file, which is written in Java and then compiled into a class file, in charge of storing all of the application's logic. All APK's will contain this file.
APKTool will allow you to decompress the APK and be able to obtain the Java/Dex source code for the application.
apktool d -s FridaLab.apk
This will create a directory named FridaLab
, where we can quickly load into our Integrated Development Environment (IDE) of choice such as Android Studio or JADX-GUI. Alternatively, you can just utilize utilities such as cat
or batcat
to view the AndroidManifest.xml
file.
Contents of /FridaLab
Contents of the AndroidManifest.xml
file for the FridaLab APK using Android Studio
Using batcat
to quickly analyze our manifest file
This AndroidManifest.xml
file should serve as the primary starting point for the static analysis portion of your research with each new target.
This is because each and every single APK to ever exist requires this file and it is responsible for containing essential metadata about the application itself.
This includes the package name (uk.rossmarks.fridalab
), activity names, the main activity (entry point for the application), hardware requirements, and exported activities (activities can be launched by other application components -- if true or the activity can be only launched by components of the same application -- if false).
We will now be utilizing JADX to decompile our binary while taking advantage of its tools to de-obfuscate and decompile its classes. Then, we will save it as a Gradle project and load it into Android Studio for examining the APK's filae structure.
Be sure to follow the steps embedded in the ASRP or from JADX's GitHub to install.
OpenFridaLab.apk
in jadx-gui
.
Then, follow the next few steps:
- Select the option to Open up a File
- Go to File->Preferences, and make sure under Decompilation that Code Comments Level is set to DEBUG
- Go to Tools->Decompile All Classes
- Once the APK is decompiled, save this project onto your system as a Gradle project folder by going to File->Save As Gradle Project -> I often rename this file to be
<application_name>-gradle
.
We can now open up this Gradle project in Android Studio.
Select "Open..." and select the FridaLab-gradle
file to import it into Android Studio.
Analyzing the APK file structure
You can now see that we have access to all of the files pertaining to the source code for the FridaLab APK!
There are not any library files (.so
) files, so we do not have to worry about loading these into Ghidra to analyze these shared object binary blobs for C-based native Android implementations.
Once you have reached this point, we can begin analyzing the files for anything that we may be able to leverage in an attempt to exploit this application.
Inside of the Gradle project file, we can begin diving into the code base to further our understanding of the application in search of vulnerabilities that we can exploit to obtain our goal.
We will be focusing on all application logic files, no external resources. So, anything in /app/src/main/java/uk/rossmarks/fridalab
.
Let's start at the MainActivity.java
file. MainActivity is the first screen that appears when the user launches the app. Other activities can also exist that are used to perform different actions. Each activity can start other activities.
MainActivity.java
{% embed url="https://developer.android.com/guide/components/activities/intro-activities#oncreate" %}
MainActivity.onCreate()
{% endembed %}
package uk.rossmarks.fridalab;
import android.os.Bundle;
import android.support.v4.internal.view.SupportMenu;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
/* loaded from: classes.dex */
public class MainActivity extends AppCompatActivity {
public int[] completeArr = {0, 0, 0, 0, 0, 0, 0, 0};
public boolean chall03() {
return false;
}
/* JADX INFO: Access modifiers changed from: protected */
@Override // android.support.v7.app.AppCompatActivity, android.support.v4.app.FragmentActivity, android.support.v4.app.SupportActivity, android.app.Activity
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.activity_main);
((Button) findViewById(R.id.check)).setOnClickListener(new View.OnClickListener() { // from class: uk.rossmarks.fridalab.MainActivity.1
@Override // android.view.View.OnClickListener
public void onClick(View view) {
if (challenge_01.getChall01Int() == 1) {
MainActivity.this.completeArr[0] = 1;
}
if (MainActivity.this.chall03()) {
MainActivity.this.completeArr[2] = 1;
}
MainActivity.this.chall05("notfrida!");
if (MainActivity.this.chall08()) {
MainActivity.this.completeArr[7] = 1;
}
MainActivity.this.changeColors();
}
});
challenge_06.startTime();
challenge_06.addChall06(new Random().nextInt(50) + 1);
new Timer().scheduleAtFixedRate(new TimerTask() { // from class: uk.rossmarks.fridalab.MainActivity.2
@Override // java.util.TimerTask, java.lang.Runnable
public void run() {
int nextInt = new Random().nextInt(50) + 1;
challenge_06.addChall06(nextInt);
Integer.toString(nextInt);
}
}, 0L, 1000L);
challenge_07.setChall07();
}
/* JADX INFO: Access modifiers changed from: private */
public void changeColors() {
TextView textView = (TextView) findViewById(R.id.chall01txt);
TextView textView2 = (TextView) findViewById(R.id.chall02txt);
TextView textView3 = (TextView) findViewById(R.id.chall03txt);
TextView textView4 = (TextView) findViewById(R.id.chall04txt);
TextView textView5 = (TextView) findViewById(R.id.chall05txt);
TextView textView6 = (TextView) findViewById(R.id.chall06txt);
TextView textView7 = (TextView) findViewById(R.id.chall07txt);
TextView textView8 = (TextView) findViewById(R.id.chall08txt);
if (this.completeArr[0] == 1) {
textView.setTextColor(-16711936);
} else {
textView.setTextColor(SupportMenu.CATEGORY_MASK);
}
if (this.completeArr[1] == 1) {
textView2.setTextColor(-16711936);
} else {
textView2.setTextColor(SupportMenu.CATEGORY_MASK);
}
if (this.completeArr[2] == 1) {
textView3.setTextColor(-16711936);
} else {
textView3.setTextColor(SupportMenu.CATEGORY_MASK);
}
if (this.completeArr[3] == 1) {
textView4.setTextColor(-16711936);
} else {
textView4.setTextColor(SupportMenu.CATEGORY_MASK);
}
if (this.completeArr[4] == 1) {
textView5.setTextColor(-16711936);
} else {
textView5.setTextColor(SupportMenu.CATEGORY_MASK);
}
if (this.completeArr[5] == 1) {
textView6.setTextColor(-16711936);
} else {
textView6.setTextColor(SupportMenu.CATEGORY_MASK);
}
if (this.completeArr[6] == 1) {
textView7.setTextColor(-16711936);
} else {
textView7.setTextColor(SupportMenu.CATEGORY_MASK);
}
if (this.completeArr[7] == 1) {
textView8.setTextColor(-16711936);
} else {
textView8.setTextColor(SupportMenu.CATEGORY_MASK);
}
}
private void chall02() {
this.completeArr[1] = 1;
}
public void chall04(String str) {
if (str.equals("frida")) {
this.completeArr[3] = 1;
}
}
public void chall05(String str) {
if (str.equals("frida")) {
this.completeArr[4] = 1;
} else {
this.completeArr[4] = 0;
}
}
public void chall06(int i) {
if (challenge_06.confirmChall06(i)) {
this.completeArr[5] = 1;
}
}
public void chall07(String str) {
if (challenge_07.check07Pin(str)) {
this.completeArr[6] = 1;
} else {
this.completeArr[6] = 0;
}
}
public boolean chall08() {
return ((String) ((Button) findViewById(R.id.check)).getText()).equals("Confirm");
}
}
We can see that this MainActivity
is the primary UI when the application is in use. Also judging from the code, it appears that the UI will change based on the completion of the challenges.
package uk.rossmarks.fridalab;
/* loaded from: classes.dex */
public class challenge_01 {
static int chall01;
public static int getChall01Int() {
return chall01;
}
}
This Java file defines a Java class named challenge_01
.
chall01
is a integer variable- There is a method used to return the value of the variable
chall01
,getChall01Int()
package uk.rossmarks.fridalab;
/* loaded from: classes.dex */
public class challenge_06 {
static int chall06;
static long timeStart;
public static void startTime() {
timeStart = System.currentTimeMillis();
}
public static boolean confirmChall06(int i) {
return i == chall06 && System.currentTimeMillis() > timeStart + 10000;
}
public static void addChall06(int i) {
chall06 += i;
if (chall06 > 9000) {
chall06 = i;
}
}
}
Another Java class file, named challenge_06
.
startTime()
is a method used to set thetimeStart
variable to the current time in milliseconds usingSystem.currentTimeMillis()
-- assuming this starts the timer for the beginning of the challengeconfirmChall06(int i)
is a method that takesi
as an input and check if it matches the value of the static variable,chall06
. If the current time (milliseconds) is greater than thetimeStart
+ 10,000 milliseconds. Ultimately, if both of these conditions are met, it will return true, allowing us to pass the challenge.addChall06(int i)
is a method that checks if the value of the variablechall06
exceeds 9000, it will resetchall06
to the value ofi
package uk.rossmarks.fridalab;
/* loaded from: classes.dex */
public class challenge_07 {
static String chall07;
public static void setChall07() {
chall07 = BuildConfig.FLAVOR + (((int) (Math.random() * 9000.0d)) + 1000);
}
public static boolean check07Pin(String str) {
return str.equals(chall07);
}
}
Yet another class file, challenge_07
is in charge of managing the 7th challenge in the CTF.
We will need to input a PIN that is generated randomly and if it matches the value, we will pass the challenge.
setChall07()
is a method that generates a random PIN by usingBuildConfig.FLAVOR
. The way this works is by concatenating the build flavor with a random integer between 1000-9999check07Pin(String str)
is a method that takes astring
(str
) as input and compares it with the generated pin number stored in thechall07
variable. If it returns 'true', the challenge will be completed successfully.
We can now use this logic going forward and utilize Frida for dynamic instrumentation and manipulate the application's logic at runtime.
{% content-ref url="using-frida.md" %} using-frida.md {% endcontent-ref %}