October 16, 2023

Demo: How apps can hide clipboard data copying on Android 14

Demo: How Malicious Apps Can Easily Hide Copying Clipboard Data on Android 14

Have you ever copied sensitive information to your clipboard, such as passwords, credit card numbers, messages, or personally identifying information? If so, this data can remain in the clipboard on your device for various amounts of time. Do you trust the clipboard and the apps that access its data? This article will explore the Android Clipboard Manager and demonstrate why it needs better protection for the data you copy.

How Accessing Clipboard Data Works

The last item you copied on Android gets stored in a primary clip. Every application may store some text information using the code below:

val clipboard: ClipboardManager? =
            ContextCompat.getSystemService(this, ClipboardManager::class.java)
val clip = ClipData.newPlainText("", text)
clipboard?.setPrimaryClip(clip)

Here is how any application may read it:

val clipboard: ClipboardManager? =
            ContextCompat.getSystemService(this, ClipboardManager::class.java)
clipboard.primaryClip?.getItemAt(0)?.text.toString()

It's a global variable with get and set methods, nothing complicated. When something is copied to the clipboard, it remains until a new value overrides it or a device reset occurs. Are there any restrictions on this process?

Before Android 12

For a long time, the answer was "No." Before Android 12, apps could access your clipboard data without your knowledge and even read it in the background, allowing advertising SDKs to gather information about your interests and activity across multiple apps, especially if you shared a link with your friends or spouse.

After Android 12

The problem was applicable not only for Android but for iOS as well. Apple first proposed, if not a complete solution, at least something that makes users aware of the problem. In iOS 14, a small view indicates that a particular application is reading your clipboard data.

iOS clipboard notification

In Android 12, Google followed the same approach and introduced a similar mechanism one year later: a small view is displayed at the bottom of the screen:

Android clipboard notification

Take a look at the code, and you can see a simple toast (which should be recognizable to all Android developers) is being drawn:

Binder.withCleanCallingIdentity(() -> {
            try {
                CharSequence callingAppLabel = mPm.getApplicationLabel(
                        mPm.getApplicationInfoAsUser(callingPackage, 0, userId));
                String message =
                        getContext().getString(R.string.pasted_from_clipboard, callingAppLabel);
                Slog.i(TAG, message);
                Toast toastToShow;
                if (SafetyProtectionUtils.shouldShowSafetyProtectionResources(getContext())) {
                    Drawable safetyProtectionIcon = getContext()
                            .getDrawable(R.drawable.ic_safety_protection);
                    toastToShow = Toast.makeCustomToastWithIcon(getContext(),
                            UiThread.get().getLooper(), message,
                            Toast.LENGTH_SHORT, safetyProtectionIcon);
                } else {
                    toastToShow = Toast.makeText(
                            getContext(), UiThread.get().getLooper(), message,
                            Toast.LENGTH_SHORT);
                }
                toastToShow.show();
            } catch (PackageManager.NameNotFoundException e) {
                // do nothing
            }
        });

Also, here in the code is a list of exceptions for showing the notification:

if (clipboard.primaryClip == null) {
            return;
        }
        if (Settings.Secure.getInt(getContext().getContentResolver(),
                Settings.Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS,
                (mShowAccessNotifications ? 1 : 0)) == 0) {
            return;
        }
        // Don't notify if the app accessing the clipboard is the same as the current owner.
        if (UserHandle.isSameApp(uid, clipboard.primaryClipUid)) {
            return;
        }
        // Exclude special cases: IME, ContentCapture, Autofill.
        if (isDefaultIme(userId, callingPackage)) {
            return;
        }
        if (mContentCaptureInternal != null
                && mContentCaptureInternal.isContentCaptureServiceForUser(uid, userId)) {
            return;
        }
        if (mAutofillInternal != null
                && mAutofillInternal.isAugmentedAutofillServiceForUser(uid, userId)) {
            return;
        }
        if (mPm.checkPermission(Manifest.permission.SUPPRESS_CLIPBOARD_ACCESS_NOTIFICATION,
                callingPackage) == PackageManager.PERMISSION_GRANTED) {
            return;
        }
        // Don't notify if already notified for this uid and clip.
        if (clipboard.mNotifiedUids.get(uid)) {
            return;
        }

Only system apps have access to the Manifest.permission.SUPPRESS_CLIPBOARD_ACCESS_NOTIFICATION permission. Same story with changing Settings.Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS. However, system apps on Android devices include preinstalled third-party apps. Hence, these apps may easily bypass the measure.

However, the measure worked on Android and iOS for all the other apps. Right after the update to Android 12, I noticed that many of my apps were collecting the clipboard value, but they stopped doing that a short time after.

Is there a way of bypassing the mechanism? On the surface, everything looks secure: A service manages the creation of a toast notification and communicates with other apps through an inter-process communication (IPC) mechanism called Binder. There is no way of canceling or changing a toast drawn by another application. But can something be drawn on top of that?

Thanks to the Android security system, the answer is still “No.” All the views an app draws will be under system views by default. Unless the application has one permission.

The SYSTEM_ALERT_WINDOW Permission

The SYSTEM_ALERT_WINDOW permission allows apps to get drawn on top of other apps. Here are some examples:

  • Caller applications: A call comes in, and a view of the caller app is drawn on top of the other apps, so you are notified about the call and may answer.
  • Facebook chat heads: A small view pops up when a new message is in a chat. It’s drawn on top of everything you have on the screen.
  • Telegram video messages: A circle gets drawn on top of the app screens, and it doesn’t go away when you collapse the application; the video will continue playing.

You need to grant permission from a separate section of the App Info screen.

Granting app permissions

The explanation is pretty terrifying if you think about it.

Display over apps permission warning

Some malware was (and maybe still is) using this permission to overdraw their screens while a banking app was being launched and then steal credentials that the user typed into fake views.

Trying to overdraw a little toast using SYSTEM_ALERT_WINDOW is like using a sledgehammer to crack nuts. But still, many people use the pop-over features of messaging apps like Facebook and Telegram, as well as video apps like YouTube. Let’s check what happens when an app has this permission and tries to draw something on top of the original toast.

Obscuring the Toast Notification

If you have permission, the general idea is simple: use a LayoutInflater to “inflate” (create an object within the code) any view. After that, the “inflated” view gets passed to the WindowManager (responsible for drawing the windows). The following code completes the job: It will draw a translucent view with a layout defined in the dummy_view.xml file.

val windowManager: WindowManager =
            applicationContext.getSystemService(WINDOW_SERVICE) as WindowManager

val params = WindowManager.LayoutParams()
params.format = PixelFormat.TRANSLUCENT
params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
		params.isFitInsetsIgnoringVisibility = true
}
params.flags = (WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)

val view = LayoutInflater.from(applicationContext).inflate(R.layout.dummy_view, null)
windowManager.addView(view, params)

Let’s draw a small white rectangle on the toast.

Drawing a transparent white retangle over notification

Despite the appearance of a translucent white rectangle, the underlying message remains visible. The almost transparent appearance happens because a required parameter for the WindowManager, type, should at least be TYPE_APPLICATION_OVERLAY. That's the minimal type for drawing on top; others are for system or caller apps. The documentation explains, “The system may change the position, size, or visibility of these windows at anytime to reduce visual clutter to the user and also manage resources.”

Once again, Android has outsmarted us by making the view semi-transparent. However, let’s take another look at the issue at hand: We have a semi-transparent window. How can we make it less transparent? The obvious and straightforward solution is to draw additional windows on top of it. Let’s add two more white rectangles on top of the existing window:

Completely opaque white retangle over notification

It worked. 😈 Three layers make the initial toast barely visible, but we can add more if needed. With minor adjustments, we can make it look like a real toast. Thanks to Android and its  open-source code – we may reproduce the behavior quickly:

Opaque black notification covering original notification

As a result, it’s possible to overdraw a toast either with a different toast or with any other view. Completely hiding the original toast can prevent the user from being notified of clipboard actions. Any application with the SYSTEM_ALERT_WINDOW permission can read clipboard data without notifying the user. We have verified this on the latest version of Android 14.

It is worth mentioning that some vendors have already addressed this issue and do not allow overdrawing system toasts. For example, this specific method does not work on the latest Samsung devices with the latest versions of One UI. You can check if you are vulnerable by launching the demo we have prepared for you below.

How to Prevent Apps from Stealing Clipboard Data on Your Android Device

  1. Remove the SYSTEM_ALERT_WINDOW permission from as many applications as you can. Despite our humorous demonstration, granting this permission can be dangerous, so only give it to apps you trust.
  2. To protect your clipboard data, avoid using devices with Android 11 and older versions. Update to the latest Android version if possible. If not, consider using AOSP-based ROMs on Android 12+ (e.g., Lineage OS), which are commonly used to extend the lifespan of devices.
  3. Whenever possible, refrain from copying sensitive information.
  4. Be aware that giants of the advertising market may still have access to your Android clipboard data without your knowledge.

Demo Application

We’ve prepared a simple application that allows you to see this method in action. You can use it to check if your Android device is vulnerable to the attack. The demo applies to devices running Android 12 and newer. The source code is open and available for you to review.

Obscured notification demo animation

Conclusion

With information security (like any other security), finding the right balance between safety and convenience is crucial. Setting the measures too tight may push users away from your product, and the same result can occur without any measures. The clipboard tool is no exception. From the privacy and security point of view, a better mechanism on Android would have at least a similar mechanism as in browsers: JavaScript code that launched inside of browsers (not extensions for them) can have access to the clipboard data if, and only if, a user-triggered action has happened.

Google implemented a simple measure in Android to keep users from moving towards iOS. They have limited access to silently reading clipboard values for all other apps but have preserved it for themselves. Their access to your clipboard allows them to have a valuable source of information for targeted advertisements and to maintain their market position.

To stay updated with the latest in Android device fingerprinting, you can star, follow, or fork our production-grade library on GitHub. If you have any questions, feel free to email us at oss@fingerprint.com. If you're interested in joining our team, you can find more information on our careers page.

All article tags

Share this post