• Perspective
Publié le 15 juin 2021

Write-up du CTF Android


Du 1er au 7 juin 2021, BSSI organisait un petit Capture The Flag (CTF) ayant pour but de faire monter en compétence (ou rappeler les bases) les auditeurs ainsi que les curieux à l’analyse statique des applications Android.

Au final, pas moins de 32 challenges se retrouvant dans les 9 catégories suivantes : AAB, APP TIERS, BACKUP, CERTIFICAT, CODE, DEEPLINKS, FIREBASE, MANIFEST ou encore RESOURCE.

Au total, une quarantaine d’équipes pour 334 validations de challenges ! Félicitations à tous !

Le write-up suivant provient de l’équipe No Rest For The Drinkers, 2e du classement, mais ayant validé l’ensemble des épreuves à disposition


Provided file: app.aab
Objective: What is the exact size of the APK standalone-xxxhdpi.apk in Bit?

Here we deal with an Android App Bundle (.aab) file which is basically a publishing format that contains all app’s compiled code and resources and defers APK generation and signing process to Google Play service. So far, we assume that standalone-xxxhdpi.apk is inside app.aab package. With the help of Google’s bundletool, we extract its content as follows:

$ java -jar ../bundletool-all-1.6.1.jar build-apks --bundle app.aab --output app.apks

Then, just unzip app.apks package we got and finally find standalones directory which contains several APKs including the one we are looking for. File’s size in bytes is indicated by ls command: 1870424.

$ unzip app.apks
$ ls -al standalones
total 12720
drwxr-xr-x 2 kali kali    4096 Jun  2 05:20 .
drwxr-xr-x 4 kali kali    4096 Jun  2 05:20 ..
-rw-r--r-- 1 kali kali 1848055 Dec 31  1969 standalone-hdpi.apk
-rw-r--r-- 1 kali kali 1840835 Dec 31  1969 standalone-ldpi.apk
-rw-r--r-- 1 kali kali 1840391 Dec 31  1969 standalone-mdpi.apk
-rw-r--r-- 1 kali kali 1883832 Dec 31  1969 standalone-tvdpi.apk
-rw-r--r-- 1 kali kali 1853407 Dec 31  1969 standalone-xhdpi.apk
-rw-r--r-- 1 kali kali 1865598 Dec 31  1969 standalone-xxhdpi.apk
-rw-r--r-- 1 kali kali 1870424 Dec 31  1969 standalone-xxxhdpi.apk


Provided file: appTiers1.apk
Objective: Which AWS tokens are accessible in the application?

The first step is to extract the apk: $ apktool d appTiers1.apk

Knowing the size of the "accessKeyId", 20 characters, and that it is composed of alpha numeric characters in uppercase surrounded by delimiters, we can perform a grep as follows:

$ grep -r -P '(?<![A-Z0-9])[A-Z0-9]{20}(?![A-Z0-9])' .

./assets/www/js/post/post.js:    var s3Uploader = new ContentActive.AWSHelper.S3Uploader('https://fanreactrawvideos.s3.amazonaws.com/', 'eyJleHBpcmF0aW9uIjoiMjAyMC0xMi0zMVQxMjowMDowMC4wMDBaIiwiY29uZGl0aW9ucyI6W3siYnVja2V0IjoiZmFucmVhY3RyYXd2aWRlb3MifSxbInN0YXJ0cy13aXRoIiwiJGtleSIsInVwbG9hZHMvIl0seyJhY2wiOiJwdWJsaWMtcmVhZCJ9LFsic3RhcnRzLXdpdGgiLCIkQ29udGVudC1UeXBlIiwiIl0sWyJjb250ZW50LWxlbmd0aC1yYW5nZSIsMCw1MjQyODgwMDBdXX0=', '11kl5cpholWbZbMVd5oWvwhtjAM=', 'AKIAITHQUI7AUOG7PZJA');
./assets/www/js/recordVideo/recordVideo.js:AWS.config.update({accessKeyId: 'AKIAI7XI6PMABTZQO47A', secretAccessKey: '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'});
./assets/www/js/recordVideo/recordVideo.js:    var s3Uploader = new ContentActive.AWSHelper.S3Uploader('https://fanreactrawvideos.s3.amazonaws.com/', 'eyJleHBpcmF0aW9uIjoiMjAyMC0xMi0zMVQxMjowMDowMC4wMDBaIiwiY29uZGl0aW9ucyI6W3siYnVja2V0IjoiZmFucmVhY3RyYXd2aWRlb3MifSxbInN0YXJ0cy13aXRoIiwiJGtleSIsInVwbG9hZHMvIl0seyJhY2wiOiJwdWJsaWMtcmVhZCJ9LFsic3RhcnRzLXdpdGgiLCIkQ29udGVudC1UeXBlIiwiIl0sWyJjb250ZW50LWxlbmd0aC1yYW5nZSIsMCw1MjQyODgwMDBdXX0=', '11kl5cpholWbZbMVd5oWvwhtjAM=', 'AKIAITHQUI7AUOG7PZJA');
./smali/org/apache/cordova/globalization/Globalization.smali:.field public static final GETPREFERREDLANGUAGE:Ljava/lang/String; = "getPreferredLanguage"

We therefore find the answer: AKIAI7XI6PMABTZQO47A:lrcJ9D9C6vTsyknp1FDtwzJt5xaLQTGJPd+9zvsY


Provided file: appTiers2.apk
Objective: In which file is the source code of the application (file name only)?

The first step is to extract the apk: $ apktool d appTiers2.apk

A quick look at the manifest tells us about the technology used:

$ cat appTiers2/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8" standalone="no ?><manifest xmlns_android="http://schemas.android.com/apk/res/android" package="com.react_native_examples" platformBuildVersionCode="23" platformBuildVersionName="6.0-2704002">
    <uses-permission android_name="android.permission.INTERNET"/>
    <uses-permission android_name="android.permission.SYSTEM_ALERT_WINDOW"/>
    <uses-permission android_name="com.google.android.c2dm.permission.RECEIVE"/>
    <android:uses-permission android_name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <android:uses-permission android_name="android.permission.READ_PHONE_STATE"/>
    <android:uses-permission android_name="android.permission.READ_EXTERNAL_STORAGE"/>
    <application android_allowBackup="false" android_icon="@mipmap/ic_launcher" android_label="@string/app_name" android_name="com.react_native_examples.MainApplication" android_theme="@style/AppTheme">
        <activity android_configChanges="keyboard|keyboardHidden|orientation|screenSize" android_label="@string/app_name" android_name="com.react_native_examples.MainActivity" android_windowSoftInputMode="adjustResize">
                <action android_name="android.intent.action.MAIN"/>
                <category android_name="android.intent.category.LAUNCHER"/>
        <activity android_name="com.facebook.react.devsupport.DevSettingsActivity"/>
        <activity android_exported="false" android_name="com.google.android.gms.common.api.GoogleApiActivity" android_theme="@android:style/Theme.Translucent.NoTitleBar"/>
        <meta-data android_name="com.google.android.gms.version" android_value="@integer/google_play_services_version"/>
        <meta-data android_name="com.android.vending.derived.apk.id" android_value="1"/>

In a react native application, in production, the JavaScript source code is stored in /index/*.bundle.

The challenge is no exception, and the source code can be found there: appTiers2/assets/index.android.bundle


Provided file: appTiers3.apk
Objective: Which JavaScript engine optimizes the loading time of the application, making the index.android.bundle file unreadable (lowercase)?

The first step is to extract the apk: $ apktool d appTiers3.apk

By going to the official documentation of react native, a description of an engine corresponding to our search is proposed.

Hermes is an open-source JavaScript engine optimized for React Native. For many apps, enabling Hermes will result in improved start-up time, decreased memory usage, and smaller app size”

Looking at the manifest, we get confirmation: hermes is therefore the solution


Provided file: appTiers3.apk
Objective: When disassembled, how many registers are used for the Function<global>0 function?

The first step is to disassemble the source code, so we search for a tool to do this easily and found the hbctool tool. Then, we disassemble the bytecode :

$ hbctool disasm index.android.bundle bundle

[*] Disassemble 'index.android.bundle' to 'bundle' path
[*] Hermes Bytecode [ Source Hash: d0310a88a868dfb1ee21d12e9011725b1f716875, HBC Version: 74 ]
[*] Done

Then we look at the extracted code:

$ head bundle/instruction.hasm

Function<global>0(1 params, 19 registers, 0 symbols):
	DeclareGlobalVar    	UInt32:2116
	; Oper[0]: String(2116) '__BUNDLE_START_TIME__'

	DeclareGlobalVar    	UInt32:2120
	; Oper[0]: String(2120) '__DEV__'

	DeclareGlobalVar    	UInt32:150
	; Oper[0]: String(150) 'process'

We have our answer, there are 19 registers in the Function<global>0 function.


Provided file: appTiers4.apk
Objective: What is the value of XAMARIN_INSIGHTS_HOST in Tink.Portable.dll/Tink.Client?

After extracting the apk, we start looking for the dll location:

$ find . -name "Tink.Portable.dll"

We then search for the string " XAMARIN_INSIGHTS_HOST ".

$ grep -l ./unknown/assemblies/* -e "XAMARIN_INSIGHTS_HOST"

We are looking for some information about the binary to determine which tool can retrieve the source code:

$ file Tink.MonoDroid.dll
Tink.MonoDroid.dll: PE32 executable (DLL) (console) Intel 80386 Mono/.Net assembly, for MS Windows

As this is a .net library, we will use the dotPeek tool:

The tool allows us to do a string search and thus find the expected value: “xaapi.xamarin.com”.



Provided file: backup.ab
Objective: What is the password associated with the user ‘root’?

Like other operating system, Android has backup capabilities which, in case of misconfiguration, could save some personal information. The provided file has been created by “adb backup” command which by default, includes most of files and directories used by the application. First, we can check if the backup is encrypted:

$ file backup.ab
backup.ab: Android Backup, version 3, Compressed, Not-Encrypted

Before we delve deeper, as we know that .ab file is like TAR archive with modified headers, the first step is to transform it:

$ dd if=backup.ab bs=1 skip=24 | python -c "import zlib,sys;sys.stdout.write(zlib.decompress(sys.stdin.read()))" > backup.tar

Then just extract the TAR file and use grep to find “root” strings:

$ grep -ri root

The root user’s password is: R00tPassw0rd. To handle this issue, Manifest can be configured to exclude sensitive files by using fullBackupContent parameter.


Provided file: diva.apk
Objective: Who is the owner of the certificate (Format: “CN=XX, O=XX, C=XX”)?

When we talk about certificate, we immediately think to application signing certificate. Every application that runs on Android platform must be signed otherwise all installation attempt will be rejected. We can get information about this certificate with tools like apksigner or keytool.

$ apksigner verify --print-certs -v diva.apk
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
Verified using v1 scheme (JAR signing): true
Verified using v2 scheme (APK Signature Scheme v2): false
Verified using v3 scheme (APK Signature Scheme v3): false
Number of signers: 1
Signer #1 certificate DN: CN=Android Debug, O=Android, C=US
Signer #1 certificate SHA-256 digest: 35d7f7ad35dfb826b70fa4b73187ed478540e32c8b8c5653b86568029fcd5840
Signer #1 certificate SHA-1 digest: ae4ead5aeaba4e9e4fc928e7c7f7fd459f008031
Signer #1 certificate MD5 digest: d620162ac34ee974d7fd3a1862e7e4df
Signer #1 key algorithm: RSA
Signer #1 key size (bits): 2048
Signer #1 public key SHA-256 digest: 12aa87c885a8ca151f9e45b2f910aecccaef86efbadfb4227fe6813f650eb5eb
Signer #1 public key SHA-1 digest: 866628414b1cd1d243c501ea91d3d120be059dae
Signer #1 public key MD5 digest: 6632f4b7d6156dc19f8fba7732c8047b

The certificate owner is CN=Android Debug, O=Android, C=US.


Provided file: diva.apk
Objective: What is the MD5 fingerprint of the certificate ?

By using previous command result, it is trivial to get MD5 hash: d620162ac34ee974d7fd3a1862e7e4df


Provided file: diva.apk
Objective: What type of certificate is it? (v1, v2 or v3)?

Android supports three application signing schemes:

  • v1 scheme, based on JAR signing
  • v2 scheme, an APK signature introduced in Android 7.0
  • v3 scheme, an APK signature introduced in Android 9.0

All three have their specificity which will not be discussed here but more information could be found on android developer website. Based on previous command, we can obviously say that diva APK only uses v1 JAR signing application certificate. In fact, it is a debug certificate created by Android Studio IDE when running or debugging application.


Provided file: diva.apk
Objective: What vulnerability affects the v1 version of certificates?

In 2017, a vulnerability, now registered and known as CVE-2017-13156 also called Janus, has been found on v1 signing process.

The loophole lies in an incomplete verification of some APK parts which could authorize a third-party attacker to modify the code without affecting the overall signature.

More information regarding this vulnerability is available here: https://www.trendmicro.com/en_us/research/17/l/janus-android-app-signature-bypass-allows-attackers-modify-legitimate-apps.html


Provided file: diva.apk
Objective: Is the code obfuscated?

If it is not obfuscated, we can revert the compiled source code by using tools like dex2jar and jd-gui.

$ sh d2j-dex2jar.sh -f CODE-01/diva.apk
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
dex2jar CODE-01/diva.apk -> ./diva-dex2jar.jar

$ java -jar jd-gui-1.6.6.jar
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true

Now we just get the jar file from the classes.dex, we can open this file with jd-gui and find an entirely readable source code:

As we can see, the source code is not obfuscated.


Provided file: badEncryption.java

Objective: What is the password to validate the test?

Below is the provided code:

package p006sg.p007vp.owasp_mobile.OMTG_Android;

import android.os.Bundle;
import android.support.p003v7.app.AppCompatActivity;
import android.support.p003v7.widget.Toolbar;
import android.util.Base64;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.google.common.base.Ascii;
import p006sg.p007vp.owasp_mobile.omtg_android.R;


    /* access modifiers changed from: private */
    /* access modifiers changed from: public */
    private void result(Boolean result) {
        if (result.booleanValue()) {
            Toast.makeText(this, "Congratulations, this is the correct password", 1).show();
        } else {
            Toast.makeText(this, "Try again!", 1).show();

    /* access modifiers changed from: private */
    public static boolean verify(String str) {
        byte[] encryptedDecoded = Base64.decode("vJqfip28ioydips=", 0);
        byte[] userPass = encrypt(str);
        if (userPass.length != encryptedDecoded.length) {
            return false;
        for (int i = 0; i < userPass.length; i++) {
            if (userPass[i] != encryptedDecoded[i]) {
                return false;
        return true;

    private static byte[] encrypt(String str) {
        byte[] bytes = str.getBytes();
        for (int i = 0; i < bytes.length; i++) {
            bytes[i] = (byte) (bytes[i] ^ Ascii.DLE);
            bytes[i] = (byte) ((bytes[i] ^ -1) & 255);
        return bytes;

The algorithm used is composed of reciprocal XOR functions. It is therefore sufficient to repeat the operations in reverse order to decipher the message:

import base64

def decrypt(b64_encoded):
    encrypted = base64.b64decode(b64_encoded)
    passwd = ""
    for byte in encrypted:
        passwd += chr((((byte - 256)) ^ -1 ) ^ 16)

if __name__ == "__main__ :

The deciphered text is “SuperSecret”.


Provided file: androidRealMalware.apk

Objective: What is the password to decrypt the ransomware?

As we were asked to find a password, we immediately think that it might have been hardcoded in the source code. It is a common mistake made by developers and an important check to be done while analysing a mobile application.

We can use the same technique as above, with dex2jar and jd-gui to retrieve the source code. After some research, we find a class named BlockedAppActivity in which there is a cleartext password: 4865083501.

Tool such as jadx could also be used


Provided file: diva.apk

Objective: What is the ‘http://’ URL within the APK?

Decompiling the APK and executing the following commands can highlight the URL:  http://schemas.android.com/apk/res/android.

$ apktool d diva.apk
$ find diva -type f -name “*” -print0 | xargs -0 grep -H –-color “http://”


For a long time, there was no way to redirect users to the right application when they clicked on a link. So, if you shared a link to a specific content in a mobile application with your friends or other contacts, it was opened in your mobile's web browser, even if the application was installed on the target system. That behaviour was anything but convenient. Today, deep links handle this problem and takes you directly to a specific destination within an app (if it installed) or ask to choose you between different app (if several can answer).

However, this feature also introduces security holes if not properly implemented. Incomplete controls or even a lack of validation can allow unauthorized access to certain data.

It is also good to know that when there are several deep links within the same intent-filter, each schema can be associated with each host (see example below).

Provided file: periscope.apk
Objective: How many deep links are there to the tv.periscope.android.AppRouterActivity?

$ cat AndroidManifest.xml
        <activity android_launchMode="singleTask" android_name="tv.periscope.android.AppRouterActivity">
            <intent-filter android_autoVerify="true">
                <action android_name="android.intent.action.VIEW"/>
                <category android_name="android.intent.category.DEFAULT"/>
                <category android_name="android.intent.category.BROWSABLE"/>
                <data android_scheme="http"/>
                <data android_scheme="https"/>
                <data android_host="www.periscope.tv"/>
                <data android_host="periscope.tv"/>
                <data android_host="www.pscp.tv"/>
                <data android_host="pscp.tv"/>
                <action android_name="android.intent.action.VIEW"/>
                <category android_name="android.intent.category.DEFAULT"/>
                <category android_name="android.intent.category.BROWSABLE"/>
                <data android_host="www.periscope.tv" android_pathPrefix="/" android_scheme="https"/>
                <data android_host="periscope.tv" android_pathPrefix="/" android_scheme="https"/>
                <data android_host="www.pscp.tv" android_pathPrefix="/" android_scheme="https"/>
                <data android_host="pscp.tv" android_pathPrefix="/" android_scheme="https"/>
                <data android_host="www.periscope.tv" android_pathPrefix="/" android_scheme="http"/>
                <data android_host="periscope.tv" android_pathPrefix="/" android_scheme="http"/>
                <data android_host="www.pscp.tv" android_pathPrefix="/" android_scheme="http"/>
                <data android_host="pscp.tv" android_pathPrefix="/" android_scheme="http"/>
                <data android_host="user" android_pathPrefix="/" android_scheme="pscp"/>
                <data android_host="user" android_pathPrefix="/" android_scheme="pscpd"/>
                <data android_host="broadcast" android_pathPrefix="/" android_scheme="pscp"/>
                <data android_host="broadcast" android_pathPrefix="/" android_scheme="pscpd"/>
                <data android_host="channel" android_pathPrefix="/" android_scheme="pscp"/>
                <data android_host="channel" android_pathPrefix="/" android_scheme="pscpd"/>
                <data android_host="discover" android_pathPrefix="/" android_scheme="pscp"/>
             <data android_host="discover" android_pathPrefix="/" android_scheme="pscpd"/>
            <intent-filter android_autoVerify="true">
                <action android_name="android.intent.action.VIEW"/>
                <category android_name="android.intent.category.DEFAULT"/>
                <category android_name="android.intent.category.BROWSABLE"/>
                <data android_host="b.pscp.live" android_pathPrefix="/g97c" android_scheme="https"/>
                <data android_host="b.pscp.live" android_pathPrefix="/g97c" android_scheme="http"/>

Therefore, previous activity can be accessed by 42 deep links (pscpd://discover, pscpd://channel, pscpd://user, pscpd://www.pscp.tv etc.).

Python script such as get_schemas also allows to identify deeplinks.


Firebase is a development platform provided and maintained by Google. Android applications often use it to save and synchronize data in real time NoSQL databases. In 2018, a vulnerability, caused by a misconfiguration in access restriction, has been discovered on thousands of Firebase databases which was therefore readable or writeable without any authentication.

Provided file: Injured.apk
Objective: Which Firebase URL gets the flags?

Usually, Firebase data can be accessed following URL: https://<project_name>.firebaseio.com/.json

To help us find it in our APK, just use FireBaseScanner tool.

$ python FirebaseMisconfig.py -p ../Injured.apk
Checking if the APK file path is valid.

APK File Found.

Initiating APK Decompilation Process.

The same APK is already decompiled. Skipping decompilation and proceeding with scanning application.

Firebase Instance(s) Found

Scanning Firebase Instance(s)

Secure Firbase Instance Found: injuredandroid
Thank You For Using FireBase Scanner

Now we find it, try accessing it by browsing the URL: https://injuredandroid.firebaseio.com/.json

$ curl https://injuredandroid.firebaseio.com/.json 
"error" : "Permission denied"

It did not work because the Firebase instance is not readable. But, according to Google documentation, we can also use child filtering to access and retrieve data. As we are looking for a flag, we may find something in the source code. By using the same technique as above, here is what we get:

With a closer look, we find a FlagNineFirebaseActivity which contains a base64 encoded string :

$ echo -n “ZmxhZ3Mv” | base64 -d

We are in! Get the flag by accessing: https://injuredandroid.firebaseio.com/flags.json


Provided file: Injured.apk
Objective: Is the Firebase URL writeable?

In other words, can we add arbitrary data in the database without any authentication? Multiple tools are available on Github, but we use Insecure Firebase Exploit whose operation is quite simple : try to insert an arbitrary file in the database :

$ python Firebase_Exploit.py
[>] Input Data for exploit

[+] Enter firebase Database Name : injuredandroid
[+] Enter filename    : test
[+] Enter name        : s1ren
[+] Enter email       : test
[+] Enter Website     : test
[+] Enter A Message   : Pwned by s1ren :)


[*] Not Exploited
No File Created


If you get a response 'Permission Denied' with 'Successfully
Exploited' This shows Exploit is written but can't be read.
Verify by visiting the URL
[>] Response

  "error" : "Permission denied"


[x] Not Exploitable 
[!] Reason: All Permissions Denied

Firebase instance is not writeable because required permission was not granted.

firebaseWritableCheck could allow to automate the previous tests.

Vet the Manifest

AndroidManifest.xml gathers all information about the application’s structure and must be thoroughly analysed. Here is what you can find there.

Provided file : diva.apk (will be used for MANIFEST challenges until MANIFEST-10)

$ cat AndroidManifest.xml
<?xml version="1.0" encoding="utf-8" standalone="no ?><manifest xmlns_android="http://schemas.android.com/apk/res/android" package="jakhar.aseem.diva" platformBuildVersionCode="23" platformBuildVersionName="6.0-2166767">
    <uses-permission android_name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android_name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android_name="android.permission.INTERNET"/>
    <application android_allowBackup="true" android_debuggable="true" android_icon="@mipmap/ic_launcher" android_label="@string/app_name" android_supportsRtl="true" android_theme="@style/AppTheme">
        <activity android_label="@string/app_name" android_name="jakhar.aseem.diva.MainActivity" android_theme="@style/AppTheme.NoActionBar">
                <action android_name="android.intent.action.MAIN"/>
                <category android_name="android.intent.category.LAUNCHER"/>
        <activity android_label="@string/d1" android_name="jakhar.aseem.diva.LogActivity"/>
        <activity android_label="@string/d2" android_name="jakhar.aseem.diva.HardcodeActivity"/>
        <activity android_label="@string/d3" android_name="jakhar.aseem.diva.InsecureDataStorage1Activity"/>
        <activity android_label="@string/d4" android_name="jakhar.aseem.diva.InsecureDataStorage2Activity"/>
        <activity android_label="@string/d5" android_name="jakhar.aseem.diva.InsecureDataStorage3Activity"/>
        <activity android_label="@string/d6" android_name="jakhar.aseem.diva.InsecureDataStorage4Activity"/>
        <activity android_label="@string/d7" android_name="jakhar.aseem.diva.SQLInjectionActivity"/>
        <activity android_label="@string/d8" android_name="jakhar.aseem.diva.InputValidation2URISchemeActivity"/>
        <activity android_label="@string/d9" android_name="jakhar.aseem.diva.AccessControl1Activity"/>
        <activity android_label="@string/apic_label" android_name="jakhar.aseem.diva.APICredsActivity">
                <action android_name="jakhar.aseem.diva.action.VIEW_CREDS"/>
                <category android_name="android.intent.category.DEFAULT"/>
        <activity android_label="@string/d10" android_name="jakhar.aseem.diva.AccessControl2Activity"/>
        <activity android_label="@string/apic2_label" android_name="jakhar.aseem.diva.APICreds2Activity">
                <action android_name="jakhar.aseem.diva.action.VIEW_CREDS2"/>
                <category android_name="android.intent.category.DEFAULT"/>
        <provider android_authorities="jakhar.aseem.diva.provider.notesprovider" android_enabled="true" android_exported="true" android_name="jakhar.aseem.diva.NotesProvider"/>
        <activity android_label="@string/d11" android_name="jakhar.aseem.diva.AccessControl3Activity"/>
        <activity android_label="@string/d12" android_name="jakhar.aseem.diva.Hardcode2Activity"/>
        <activity android_label="@string/pnotes" android_name="jakhar.aseem.diva.AccessControl3NotesActivity"/>
        <activity android_label="@string/d13" android_name="jakhar.aseem.diva.InputValidation3Activity"/>


Objective: What is the package name?

Just look inside manifest tag attributes and find the package name: jakhar.aseem.diva



Objective: Is the application debuggable?

This parameter indicates whether the application can be debugged (should normally be deactivated in production environment). Looking at application tag attributes, we find it is debuggable.



Objective: Is the application backupable?

This parameter indicates whether to allow the application to participate in the backup infrastructure. If set to false, no backup will ever be performed even via adb. Using the same method as in previous challenge, we find the application data can be saved in an external backup.



Objective: How many permissions are granted to the application?

Simply count how many uses-permissions tag we have, there are three different permissions.

<uses-permission android_name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android_name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android_name="android.permission.INTERNET"/>


Objective: How many dangerous permissions are there in the application?

From Android 6.0 and above, dangerous permissions (ones which could potentially affect the user privacy) concept has been introduced (asking users before authorize application to access some of them). Of the three permissions above, two are considered dangerous (WRITE_EXTERNAL_STORAGE and READ_EXTERNAL_STORAGE).


Objective: What permissions is needed to use NFC?

BIND_NFC_SERVICE permission is needed to use NFC.



Objective: To which group do these dangerous permissions belong?

These permissions belong to the STORAGE group.



Objective: How many activities are there?

Activities can be assimilated to the screens of the Android app’s user interface. All activities used by an application are listed in the activity Manifest’s tag.Diva APK has 17 declared activities.



Objective: What activity is launched when the application starts?

By default, when they start, Android applications launch MainActivity whose name here is: jakhar.aseem.diva.MainActivity.


Objective: How many elements (Activity / Broadcast Receiver, Service / Content Provider) are exported ?

Activities can communicate with each other via Intent process, broken into 2 different types :

  • Explicit Intents which specify the recipient
  • Implicit Intents whose recipient is not precisely known (some information must be provided to let Android determine it). They are built via startActivity method for which an action is specified (generally a constant starting with “ACTION_”)

Then, to determine the recipient of a given Intent, the operating system performs a comparison with “filters” that indicate the application's components can handle certain intents. Represented by “intent-filter” nodes they are declared in the activity tag of the AndroidManifest.xml file.

As far as they are used to establish a communication with external activities, application’s internal activities must be exported :

  • via the android:exported attribute (false by default)
  • with an implicit Intent associated with one or more filters

Only those that meet one of these criteria can be called at any time by any program. Other can only have interaction within the application.

This behaviour is totally valid for Broadcast Receiver, Service and Content Provider too.

As we know the presence of intent filters automatically sets the value of the android:exported attribute to true (even if it is not declared) we can count 4 exported elements.


Provided file: clipwatch.apk
Objective: Which option prevents the installation of the application via google Play (format XX:XX) ?

$ cat clipwatch/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8" standalone="no ?><manifest xmlns_android="http://schemas.android.com/apk/res/android" android_compileSdkVersion="29" android_compileSdkVersionCodename="10" package="com.prodaft.clipwatch" platformBuildVersionCode="29" platformBuildVersionName="10">
    <application android_allowBackup="true" android_appComponentFactory="androidx.core.app.CoreComponentFactory" android_debuggable="true" android_icon="@mipmap/ic_launcher" android_label="@string/app_name" android_roundIcon="@mipmap/ic_launcher_round" android_supportsRtl="true" android_testOnly="true" android_theme="@style/AppTheme">
        <activity android_label="@string/app_name" android_name="com.prodaft.clipwatch.MainActivity" android_theme="@style/AppTheme.NoActionBar">
                <action android_name="android.intent.action.MAIN"/>
                <category android_name="android.intent.category.LAUNCHER"/>

android:testOnly manifest attribute indicates whether the application is only for testing purposes.


Provided file: androidRealMalware.apk
Objective: Which permission allows the device data to be encrypted?

BIND_DEVICE_ADMIN permission allows the device to be encrypted.


Provided files: diva.apk
Objective: How many PNG files are there in the application?

As we saw above, decompiled APK contains some files, including res directory in which the application’s resources are saved. Using the following commands, we can simply count how many PNG files the application uses:

$ grep -Eri « *.png »
drawable-ldrtl-xxxhdpi-v4/abc_spinner_mtrl_am_alpha.9.png: binary file matches
grep: drawable-ldrtl-xxxhdpi-v4/abc_ic_ab_back_mtrl_am_alpha.png: binary file matches
grep: drawable-mdpi-v4/abc_popup_background_mtrl_mult.9.png: binary file matches
grep: drawable-mdpi-v4/abc_ic_menu_share_mtrl_alpha.png: binary file matches

We find a total of 218 results.


Provided files: Injured.apk
Objective: What is the password expected by the application?

if (post.equals(getString(R.string.cmVzb3VyY2VzX3lv)))
// Good password

Android offers the possibility to define generic strings (saved in res/values/strings.xml) that we can use in all our code by simply calling it by their name: R.string.<name>. Here, the string’s name is : cmVzb3VyY2VzX3lv. Just use the following command to retrieve its value:

$ grep -i cmVzb3VyY2VzX3lv strings.xml 
<string name="cmVzb3VyY2VzX3lv">F1ag_thr33</string>

The expected password is the flag : F1ag_thr33

Article rédigé par
Régis Senet