How to crack the challenges of DIVA

DIVA is a vulnerable Android app that is a good start to teach students how to check for vulnerabilities or design flaws in Android apps. According to the developers, “DIVA (Damn insecure and vulnerable App) is an App intentionally designed to be insecure. The aim of the App is to teach developers/QA/security professionals, flaws that are generally present in the Apps due poor or insecure coding practices.”

Within this Blogpost, I want to help you cracking the 13 challenges of DIVA.

0. Install the App

First of all we need to install the app to our test-device or emulator. This can be done by executing the following command:

adb install diva.apk

As we will need for some of the following challenges also a good static analysis tool, we will now import the app into Codeinspect (but you can also use any other Android static analysis tool – e.g., JadX).


1. Insecure Logging

Lets get started with the first challenge. In this case we need to find out what the app is logging, where the log is located and also which lines of code are responsible for this kind of data leakage.

As we know from Android in general, logcat is used for logging of the system itself as well as for nearly all apps installed on a device. So lets try to have a look at the output of logcat.

To start logcat, just enter the following command into your local shell:

adb logcat

If we now enter any value into the textbox and hit “check out” we will receive an error message by the app.

But at the same time we will see a line like the following one within our logcat output:

E/diva-log( 6067): Error while processing transaction with credit card: 4711

This line shows exactly the value that we entered within the app. Thus, we have found the data leak 😉

When we now look into the decompiled app within Codeinspect, we can find a class called LogActivity which contains the code snippet responsible for the output:

Log.e((String)"diva-log", (String)new StringBuilder().append("Error while processing transaction with credit card:").append($param0.getText().toString()).toString());

If you are using log messages for debugging, please be sure that you do not log sensitive user data or secret values of your app!


2. Hardcoding Issues

The 2nd challenge is again not really hard, but you now have to jump to the decompiled source code directly. The aim of this challenge is to find hardcoded secrets that are used to gain you access within restricted areas of the app.

Taking a look into the class HardcodeActivity we see the following code and also the hardcoded secret “vendorsecretkey” that brings us to the “other side” 😉

if (((EditText)this.findViewById(2131492987)).getText().toString().equals("vendorsecretkey")) {
    Toast.makeText((Context)this, (CharSequence)"Access granted! See you on the other side :)", (int)0).show();
    return;
}

3. Insecure Data Storage

Within the next 4 challenges we have to find the location where the app is storing the credentials a user is entering.

These 4 challenges describe the different ways developer can store data entered by a user or stored by the app itself:

  • shared preferences,
  • local database,
  • tmp file and
  • local file on the SD-card.

In any of those 4 cases, the app should never store confidential data – like credentials – in plain text and should use cryptographic hashes or encryption instead.

But now let’s get back to the challenges:

For this example, will we add “secret_user” as the 3rd party service username and “secret” as the 3rd party service password.

When looking at the source code, we see that the app is storing the data within the shared preferences folder:

public void saveCredentials(View $param0) {
    $param0 = PreferenceManager.getDefaultSharedPreferences((Context)this).edit();
    EditText $EditText_1 = (EditText)this.findViewById(2131493001);
    $param0.putString("user", ((EditText)this.findViewById(2131493000)).getText().toString());
    $param0.putString("password", $EditText_1.getText().toString());
    $param0.commit();
    Toast.makeText((Context)this, (CharSequence)"3rd party credentials saved successfully!", (int)0).show();
}

To see if this is really true, we connect via adb shell to the test device and navigate into the shared_prefs folder of the DIVA app:

adb shell
cd /data/data
cd jakhar.aseem.diva/shared_prefs

There we find a file called jakhar.aseem.diva_preferences.xml which contains the following data:

< ?xml version='1.0' encoding='utf-8' standalone='yes' ?>

    secret_user
    secret

And here we find the entered credentials in plain text 😉


4. Insecure Data Storage

The next challenge looks identically from the UI, but when looking at the code, we see the difference:

this.mDB = this.openOrCreateDatabase("ids2", 0, null);
this.mDB.execSQL("CREATE TABLE IF NOT EXISTS myuser(user VARCHAR, password VARCHAR);");
...
SQLiteDatabase $SQLiteDatabase = this.mDB;
$param0 = new Object();
$SQLiteDatabase.execSQL($param0.append("INSERT INTO myuser VALUES ('").append($EditText.getText().toString()).append("', '").append($EditText_1.getText().toString()).append("');").toString());
this.mDB.close();

As we can see in this code snippet, the inserted credentials are stored within a sqlite database called “ids” in the table “myuser”. When dumping the database from the device by entering the following command and then opening the database with the DB Browser for SQLite we see our entered credentials:

adb pull /data/data/jakhar.aseem.diva/databases/ids .


5. Insecure Data Storage

The next challenge looks identically from the UI, but when looking at the code, we see the difference:

public void saveCredentials(View $param0) {
    $param0 = (EditText)string.findViewById(2131493006);
    EditText $EditText_1 = (EditText)string.findViewById(2131493007);
    Serializable $File = new Serializable(string.getApplicationInfo().dataDir);
    try {
        $File = File.createTempFile("uinfo", "tmp", $File);
        $File.setReadable(true);
        $File.setWritable(true);
    }
    ...
    $FileWriter.write($File.append($param0.getText().toString()).append(":").append($EditText_1.getText().toString()).append("\n").toString());
    $FileWriter.close();
    Toast.makeText((Context)string, (CharSequence)"3rd party credentials saved successfully!", (int)0).show();
}

As we can see in this code snippet, the inserted credentials are stored within tmp file called “uinfo-838734269tmp”:

secret_user:secret

6. Insecure Data Storage

The next challenge again looks identically from the UI, but when looking at the code, we see the difference – this time the credentials are stored to the SD-card:

Object $File_Method_getExternalStorageDirectory = Environment.getExternalStorageDirectory();
try {
    $param0 = new Object();
    $File = new File($param0.append($File_Method_getExternalStorageDirectory.getAbsolutePath()).append("/.uinfo.txt").toString());
    $File.setReadable(true);
    $File.setWritable(true);
}
...
$File_Method_getExternalStorageDirectory = new Object($File);
$param0 = new Object();
$File_Method_getExternalStorageDirectory.write($param0.append($EditText.getText().toString()).append(":").append($EditText_1.getText().toString()).append("\n").toString());
$File_Method_getExternalStorageDirectory.close();
Toast.makeText((Context)string, (CharSequence)"3rd party credentials saved successfully!", (int)0).show();

As we can see in this code snippet, the inserted credentials are stored within a file called “.uinfo.txt” located on the virtual SD-card of the device (as the file name starts with a “.” it is not shown to you if you try to list the content of the SD-card with “ls“, therefore use always “ls -la“):

secret_user:secret

7. Input Validation Issues

As the app tells us already, there are 3 users stored in the database that you have to guess. But instead of brute-forcing, we are only allowed to use one command. Thus, we need to find a vulnerability in the provided search field. A good start is always to test for SQLinjections. A first try is to just enter a single quote and look what logcat is telling you:

D/Diva-sqli( 6067): Error occurred while searching in database: unrecognized token: "'''" (code 1): , while compiling: SELECT * FROM sqliuser WHERE user = '''

This is a good start, we see that it is executing the SQL query with our input without escaping it. So now we test if we can manipulate the predefined query in a way that returns always true:

1' or '1' != '2

And here we see all the stored accounts in plain text 😉


8. Input Validation Issues

The challenge tells us, that we should use the search field to access any sensitive information of the app by just entering an URL in the search field. Remembering one of the challenges above, we could try to access the data stored on the SD-card. Therefore, we have to enter the following URL:

file:///sdcard/.uinfo.txt

And as we can see below, it is working 😉


9. Access Control Issues

Within this challenge we need to gain access to a specific activity of the DIVA app without navigating through the UI.

A good way to know if we can directly access activities is by using Drozer with its automated vulnerability detection and support in exploiting those weaknesses.

Thus, we will start Drozer and search for exported activities in the DIVA app:

            ..                    ..:.
           ..o..                  .r..
            ..a..  . ....... .  ..nd
              ro..idsnemesisand..pr
              .otectorandroidsneme.
           .,sisandprotectorandroids+.
         ..nemesisandprotectorandroidsn:.
        .emesisandprotectorandroidsnemes..
      ..isandp,..,rotectorandro,..,idsnem.
      .isisandp..rotectorandroid..snemisis.
      ,andprotectorandroidsnemisisandprotec.
     .torandroidsnemesisandprotectorandroid.
     .snemisisandprotectorandroidsnemesisan:
     .dprotectorandroidsnemesisandprotector.

drozer Console (v2.4.4)
dz> run app.package.attacksurface jakhar.aseem.diva
Attack Surface:
  3 activities exported
  0 broadcast receivers exported
  1 content providers exported
  0 services exported
    is debuggable

As we can see above, there are 3 of those activities. Now, let’s see which activities we can access:

dz> run app.activity.info -a jakhar.aseem.diva
Package: jakhar.aseem.diva
  jakhar.aseem.diva.MainActivity
    Permission: null
  jakhar.aseem.diva.APICredsActivity
    Permission: null
  jakhar.aseem.diva.APICreds2Activity
    Permission: null

For us, the most important one is “jakhar.aseem.diva.APICredsActivity“.

The same answer we can also get by looking at the Android Manifest of the app:


    
        
        
    

The last step that we need to perform now, is sending an intent that is opening the activity:

run app.activity.start --component jakhar.aseem.diva jakhar.aseem.diva.APICredsActivity

10. Access Control Issues

This challenge looks pretty similar tho the one before, so let’s try the same approach with the “jakhar.aseem.diva.APICreds2Activity” activity:

run app.activity.start --component jakhar.aseem.diva jakhar.aseem.diva.APICreds2Activity

We can see, that is now opening an activity, but it seems that we need additional input passed with the intent to gain access. Thus, look into the source code if we can get the needed information there:

public class APICreds2Activity
extends AppCompatActivity {
    protected void onCreate(Bundle $param0) {
        super.onCreate($param0);
        this.setContentView(2130968606);
        $param0 = (TextView)this.findViewById(2131492983);
        EditText $EditText = (EditText)this.findViewById(2131492984);
        Button $Button = (Button)this.findViewById(2131492985);
        if (!this.getIntent().getBooleanExtra(this.getString(2131099686), true)) {
            $param0.setText((CharSequence)"TVEETER API Key: secrettveeterapikey\nAPI User name: diva2\nAPI Password: p@ssword2");
            return;
        }
        $param0.setText((CharSequence)"Register yourself at http://payatu.com to get your PIN and then login with that PIN!");
        $EditText.setVisibility(0);
        $Button.setVisibility(0);
    }

    public void viewCreds(View $param0) {
        Toast.makeText((Context)this, (CharSequence)"Invalid PIN. Please try again", (int)0).show();
    }
}

If we look at line 9, we can see that the app is checking if the string provided together with the Intent is identical to the string with the id 2131099686 from the apps “strings.xml” file. When looking at this file, we can see the needed string:

check_pin 

With this information, we now can modify our Intent:

run app.activity.start --component jakhar.aseem.diva jakhar.aseem.diva.APICreds2Activity --extra boolean check_pin false

And………yes, it is working again and we got access to the credentials 😉


11. Access Control Issues

This challenge deals with accessing a PIN protected notes storage without knowing the PIN.

A good way to know if we can bypass PIN-based authorization screens is by using Drozer with its automated vulnerability detection and support in exploiting those weaknesses.

Thus, we will start Drozer and search for vulnerabilities in the DIVA app:

            ..                    ..:.
           ..o..                  .r..
            ..a..  . ....... .  ..nd
              ro..idsnemesisand..pr
              .otectorandroidsneme.
           .,sisandprotectorandroids+.
         ..nemesisandprotectorandroidsn:.
        .emesisandprotectorandroidsnemes..
      ..isandp,..,rotectorandro,..,idsnem.
      .isisandp..rotectorandroid..snemisis.
      ,andprotectorandroidsnemisisandprotec.
     .torandroidsnemesisandprotectorandroid.
     .snemisisandprotectorandroidsnemesisan:
     .dprotectorandroidsnemesisandprotector.

drozer Console (v2.4.4)
dz> run app.package.attacksurface jakhar.aseem.diva
Attack Surface:
  3 activities exported
  0 broadcast receivers exported
  1 content providers exported
  0 services exported
    is debuggable

This results shows us already, that the app has 3 exported activities (activities that we could access directly from outside the app) and one exported content provider.

For this challenge, the exported content provider seems to be very interesting as it allows access to a database – now let’s hope that it is the right one. To find out which database the app is exporting through this content provider, we use again Drozer:

dz> run scanner.provider.finduris -a jakhar.aseem.diva
Scanning jakhar.aseem.diva...
Able to Query    content://jakhar.aseem.diva.provider.notesprovider/notes/
Unable to Query  content://jakhar.aseem.diva.provider.notesprovider
Unable to Query  content://jakhar.aseem.diva.provider.notesprovider/
Able to Query    content://jakhar.aseem.diva.provider.notesprovider/notes

Accessible content URIs:
  content://jakhar.aseem.diva.provider.notesprovider/notes/
  content://jakhar.aseem.diva.provider.notesprovider/notes

As we can see here, the exported content provider is the right one, it allows access to the secret notes. To gain access to it, we will query it through the URI and Drozer:

dz> run app.provider.query content://jakhar.aseem.diva.provider.notesprovider/notes/ --projection "* FROM notes;--"
| _id | title    | note                                 |
| 1   | office   | 10 Meetings. 5 Calls. Lunch with CEO |
| 2   | home     | Buy toys for baby, Order dinner      |
| 3   | holiday  | Either Goa or Amsterdam              |
| 4   | Expense  | Spent too much on home theater       |
| 5   | Exercise | Alternate days running               |
| 6   | Weekend  | b333333333333r                       |

And here we see all the secret notes in plain text 😉


12. Hardcoding Issues

This challenge again seems to be connected to hardcoded secrets. So, let’s have a look at the source code to find some hints:

public class Hardcode2Activity
extends AppCompatActivity {
    private DivaJni djni;

    public void access(View $param0) {
        $param0 = this.djni;
        String $String_Method_toString = ((EditText)this.findViewById(2131492990)).getText().toString();
        if ($param0.access($String_Method_toString) != 0) {
            Toast.makeText((Context)this, (CharSequence)"Access granted! See you on the other side :)", (int)0).show();
            return;
        }
        Toast.makeText((Context)this, (CharSequence)"Access denied! See you in hell :D", (int)0).show();
    }

    protected void onCreate(Bundle $param0) {
        super.onCreate($param0);
        this.setContentView(2130968608);
        this.djni = new DivaJni();
    }
}

As we can see here, it is calling an Object from the DivaJni class. So let’s have a look at this class:

staticinvoke System.loadLibrary("divajni");

The only interesting line is the one above. Here we see, that it is loading a native library. As we are searching for a hardcoded credential, there is a good chance that we find it within the native library. A first try in such cases is always to execute strings command against the library and search for strings that could be used as passwords. By doing this, we find the following string that looks suspicious “olsdfgad;lh“.

And……Bingo 😉


13. Input Validation Issues

This time we do not need to find out what is the correct value to launch the missile – this time we need to crash the app by sending an input that cannot be handled by the app.

A good first try is always to send a very large string to the app – just because we have seen in several challenges before that the author is not validating the user input. So we try it with 30 times “A”:

And……Bingo, the app crashed and no missiles will be launched 😉

One Reply to “How to crack the challenges of DIVA”

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.