Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
OrganizerTransaction — PoC para CVE-2021-39749, permitindo iniciar Activity arbitrária no Android 12L Beta | Kitploit
Ferramentas/GitHubGitHub/michalbednarski/organizertransaction
Segurança AndroidAnálise de VulnerabilidadesExploraçãoPentesting de Apps MóveisSegurança Móvel
GitHubmichalbednarski/organizertransaction

OrganizerTransaction

PoC para CVE-2021-39749, permitindo iniciar Activity arbitrária no Android 12L Beta

Ver Repositório

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar
371157há 4 anosRevisado pelo Kitploit

Este é um PoC para CVE-2021-39749, que permite iniciar atividades de outros aplicativos no Android 12L Beta, independentemente das configurações de permission e exported

No Android 12L, o acesso ao TaskFragmentOrganizer (intencionalmente) não requer mais a permissão MANAGE_ACTIVITY_TASKS

Usar o aplicativo fornecido aqui requer desabilitar as Hidden API Checks; você pode fazer isso através de adb shell settings put global hidden_api_policy 1. Estas não são uma fronteira de segurança e existem bypasses conhecidos baseados em aplicativos

Aqui estão os commits que corrigem este bug (e alguns relacionados mencionados no relatório original):

  1. startActivityInTaskFragment não depende mais de Binder.getCallingUid()
  2. ResolverActivity agora tem relinquishTaskIdentity habilitado
  3. (Não é necessário para iniciar outras atividades, mas permite reposicioná-las pela tela e torná-las transparentes e passíveis de tap-jacking) SurfaceControl de TaskFragment não é mais fornecido
  4. (Não mostrado no código aqui, problema apenas mencionado no relatório original) A decisão de quando enviar ActivityRecord#appToken para TaskFragmentOrganizer agora é baseada em uid em vez de pid

Você pode fazer checkout de android-12.1.0_r4, reverter os primeiros 3 commits (ou os primeiros 2; o aplicativo ainda será capaz de desligar o dispositivo (iniciando ShutdownActivity), mas a caixa de seleção "Zoom and set alpha" não funcionará)

(O primeiro commit dessa lista terá conflitos de merge nos testes se você tentar revertê-lo, mas você pode ignorá-los)

Binder.getCallingUid() que sempre retorna uid do sistema

O método Binder.getCallingUid() retorna o uid do processo que enviou a transação Binder atualmente processada. Esse uid é armazenado em uma variável local de thread. O código que manipula a transação pode chamar Binder.clearCallingIdentity() para definir essa variável para o uid do próprio processo, indicando aos métodos chamados posteriormente durante o tratamento da transação que as verificações de permissão devem ser feitas contra si mesmo (código que manipula a transação) e não contra o chamador da transação Binder

Às vezes há chamadas Binder.getCallingUid() que são sempre chamadas após Binder.clearCallingIdentity(), portanto sempre retornam o uid do próprio processo. Às vezes isso acontece intencionalmente, por exemplo em ActivityTaskManagerService#startDreamActivity (embora seja uma maneira bastante complicada de fazer Process.myUid() ou Os.getuid())

Escrevi para mim mesmo uma ferramenta de análise estática (baseada em Soot) que relata tais chamadas Binder.getCallingUid() (e outras verificações de permissão) que só podem ocorrer após Binder.clearCallingIdentity(). (Tenho lógica personalizada para manipular o IR Jimple/Shimple fornecido pelo Soot, embora possa haver uma maneira melhor de fazer isso com Soot, mas é o que tenho agora)

No Android 12L Beta, essa ferramenta encontrou uma em ActivityStartController#startActivityInTaskFragment (Nota: o código-fonte não estava disponível na época, pois as versões Beta não são open source, mas Shimple é geralmente legível, então usei Soot também como decompilador Java)

Como chamar startActivityInTaskFragment

Como parte do relatório de análise estática, obtive a hierarquia de chamadas da implementação de onTransact() (onde a chamada Binder começa) até startActivityInTaskFragment:

  1. onTransact no código gerado por aidl de IWindowOrganizerController
  2. WindowOrganizerController#applyTransaction (sem o argumento CallerInfo)
  3. WindowOrganizerController#applyTransaction (com o argumento CallerInfo)
  4. WindowOrganizerController#applyHierarchyOp
  5. ActivityStartController#startActivityInTaskFragment

Descobri que chamadas Binder para applyTransaction estão presentes na classe TaskFragmentOrganizer e decidi usá-la como um wrapper mais conveniente do que fazer todas as chamadas Binder diretamente (nenhuma é API pública, então tive que usar reflexão de qualquer maneira)

Primeiro de tudo, o método "2." chama enforceTaskPermission, que no Android 12.0 verificava a permissão MANAGE_ACTIVITY_TASKS somente-signature, que não podíamos obter; no entanto, no Android 12L as regras foram relaxadas para que certas transações pudessem ser feitas sem permissões. Descobriu-se que nenhuma das operações necessárias para fazer startActivityInTaskFragment exigia permissão (se a transação tivesse um TaskFragmentOrganizer associado)

Então queremos executar HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT. Para fazer isso, devemos ter nosso TaskFragment registrado em mLaunchTaskFragments; caso contrário, a exceção "Not allowed to operate with invalid fragment token" será relatada

Podemos registrar tal TaskFragment através de HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT, que chama createTaskFragment()

(No código do PoC, essas transações são enviadas em SecondActivity: HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT é enviado por initOrganizerAndFragment() e HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT é enviado por startActivityInOrganizer)

Então isso nos permite chamar startActivityInTaskFragment, e os Intents das Atividades iniciadas aqui são considerados como vindos do uid do sistema, mas acontece que isso por si só não nos permite fazer nada: atividades iniciadas pelo sistema não podem fazer URI grants e, se tentarmos lançar uma atividade de outro aplicativo, seremos impedidos pela verificação canEmbedActivity

Contornando canEmbedActivity

Vamos dar outra olhada em canEmbedActivity: o embedding é permitido se taskFragment.getTask().effectiveUid for o uid do sistema ou corresponder ao uid do aplicativo lançado. Precisaremos estar em uma task cujo effectiveUid seja o do sistema

Também recuando para createTaskFragment(): a criação de TaskFragment só era permitida se rootActivity.getUid() != ownerActivity.getUid(). Isso significa que nossa atividade precisará estar na base da back-stack da Task em que está

Precisaremos lançar uma nova Task (através de Intent.FLAG_ACTIVITY_NEW_TASK) que terá uma Atividade pertencente ao uid do sistema (então Task#effectiveUid será definido como AID_SYSTEM) e então essa Atividade iniciará nossa Atividade (dentro da mesma Task) e finish() a si mesma (então nossa Atividade se tornará a raiz dessa task, permitindo-nos usar createTaskFragment())

Uma dessas Atividades é ChooserActivity. O Chooser é geralmente usado para escolher para qual aplicativo o usuário deseja ir após selecionar a opção "compartilhar". ChooserActivity, no entanto, tem android:relinquishTaskIdentity="true" definido em AndroidManifest.xml, o que significa que quando ela lança outra Atividade, ela sobrescreve Task#effectiveUid com o uid do aplicativo recém-lançado

(relinquishTaskIdentity só funciona quando usado pelo primeiro aplicativo na Task e apenas para aplicativos do sistema, então não podemos usar relinquishTaskIdentity nós mesmos e lançar um aplicativo do sistema para sobrescrever Task#effectiveUid da nossa Task)

Outra dessas Atividades (que pode iniciar nossa Atividade e finish() a si mesma) é ResolverActivity. Ela é usada ao iniciar um Intent implícito que resolve para múltiplas Atividades. O Resolver (diferente do Chooser) oferece a opção de lembrar a escolha, que é como você (como usuário do telefone) pode distinguir os dois. ResolverActivity não tinha relinquishTaskIdentity definido; no entanto, o Resolver usa seu próprio Intent para descobrir quais opções estão disponíveis (enquanto o Chooser recebe o Intent fornecido em Extras). Isso acaba sendo um problema para a exploração porque os flags de Intent usados pelo Resolver ao lançar a Atividade selecionada serão os mesmos usados para lançar o Resolver e:

  • Se não definirmos Intent.FLAG_ACTIVITY_NEW_TASK, o Resolver será lançado dentro da nossa Task, que já tem effectiveUid permanentemente definido
  • Se definirmos Intent.FLAG_ACTIVITY_NEW_TASK, o Resolver lançará a seleção em outra Task, que então terá effectiveUid definido para um pertencente ao aplicativo lançado

A solução para esses problemas é usar ambos:

  1. Primeiro lançamos ChooserActivity: Fornecemos ao seu Intent:
    • Intent.FLAG_ACTIVITY_NEW_TASK, para que o Chooser seja lançado em uma nova Task (que terá effectiveUid do sistema, mas apenas até o próximo lançamento de Atividade)
    • Intent.EXTRA_INTENT definido para um Intent que não corresponde a nenhuma Atividade, e as únicas opções restantes no Chooser virão de Intent.EXTRA_INITIAL_INTENTS
    • Intent.EXTRA_INITIAL_INTENTS contendo um array com um elemento: O Intent que queremos que o Chooser lance (quando há apenas uma opção, tanto o Chooser quanto o Resolver pulam o prompt e imediatamente lançam a única opção e finish() a si mesmos)
  2. Então ResolverActivity é lançada por ChooserActivity:
    • O Resolver não tinha definido, então agora está definido para o sistema e permanecerá assim independentemente das próximas Atividades lançadas nesta Task

(No aplicativo PoC, a preparação dessas etapas é realizada em FirstActivity)

Outros truques com TaskFragmentOrganizer

O TaskFragmentOrganizer recebeu um SurfaceControl através do callback onTaskFragmentAppeared, e usando esse SurfaceControl é possível escalar a Atividade lançada e torná-la transparente, enquanto ela ainda receberá eventos de toque e não será considerada obscurecida (então elementos protegidos contra tap-jacking ainda podem ser tocados)

Você pode ver isso marcando a caixa de seleção "Zoom and set alpha" no aplicativo PoC

Isso é corrigido pelo commit "3." da lista de correções no topo


Outra coisa é que os ActivityRecord#appToken-s das Atividades executando dentro de TaskFragment são passados para os callbacks do TaskFragmentOrganizer. Essa lista era filtrada para incluir apenas tokens de Atividades dentro do mesmo processo; no entanto, a verificação era feita comparando o pid do TaskFragmentOrganizer com o pid da Atividade cujo appToken podíamos obter. Na verdade, não verifiquei, mas acho que um aplicativo poderia criar um TaskFragmentOrganizer, sair do processo usado para criá-lo inicialmente e ter seu pid reutilizado como pid de uma Atividade de outro aplicativo para obter seu appToken. Aqui está o commit ("4." na lista de correções acima) que muda a verificação de baseada em pid para baseada em uid (parece que este commit foi feito independentemente do meu relatório, embora depois dele)

Uma vez que o atacante obtém o appToken de uma Atividade, ele pode injetar chamadas onActivityResult() (mesmo que o aplicativo alvo não tenha chamado startActivityForResult() ele mesmo) e possivelmente adulterar savedInstanceState (chamando activityStopped(), assumindo que o atacante pode vencer a corrida com o aplicativo alvo chamando esse método e que a chamada adicional não fará com que o estado seja perdido devido a um crash)

Não verifiquei se isso pode ser feito neste caso; no entanto, anteriormente, com CVE-2020-0001 (Sim, consegui um número chique), fui capaz de usar adulteração de savedInstanceState e injeção de onActivityResult() para enganar o aplicativo de configurações do sistema a habilitar meu AccessibilityService sem interação do usuário, mas essa é uma história para outra hora

Baixar ferramenta
relinquishTaskIdentity
Task#effectiveUid
  • O Intent não tem Intent.FLAG_ACTIVITY_NEW_TASK, então a próxima Atividade é lançada dentro da mesma Task
  • A ação do Intent é definida para uma não padrão, correspondendo apenas ao <intent-filter> que declaramos nós mesmos em nosso aplicativo, então o Resolver prossegue imediatamente para lançar nossa Atividade
  • ResolverActivity lança nossa Atividade
    • Agora estamos em uma Task cujo effectiveUid é AID_SYSTEM, então canEmbedActivity() permite qualquer coisa
    • Tanto o Chooser quanto o Resolver terminaram a si mesmos, então somos a Atividade raiz na Task e podemos usar createTaskFragment()