Skip to content

MASTG-TEST-0071: Teste de UIActivity Sharing

Visão Geral

Análise Estática

Envio de Itens

Ao testar o compartilhamento via UIActivity, você deve prestar atenção especial a:

  • os dados (itens) sendo compartilhados,
  • as atividades personalizadas,
  • os tipos de atividade excluídos.

O compartilhamento de dados via UIActivity funciona criando um UIActivityViewController e passando a ele os itens desejados (URLs, texto, uma imagem) em init(activityItems: applicationActivities:).

Como mencionamos anteriormente, é possível excluir alguns dos mecanismos de compartilhamento por meio da propriedade excludedActivityTypes do controlador. É altamente recomendável realizar os testes usando as versões mais recentes do iOS, pois o número de tipos de atividade que podem ser excluídos pode aumentar. Os desenvolvedores devem estar cientes disso e excluir explicitamente aqueles que não são apropriados para os dados do aplicativo. Alguns tipos de atividade podem nem mesmo estar documentados, como "Create Watch Face".

Se você tiver o código-fonte, deve analisar o UIActivityViewController:

  • Inspecione as atividades passadas para o método init(activityItems:applicationActivities:).
  • Verifique se ele define atividades personalizadas (também passadas para o método anterior).
  • Confirme os excludedActivityTypes, se houver.

Se você tiver apenas o aplicativo compilado/instalado, tente pesquisar o método e a propriedade anteriores, por exemplo, usando rabin2:

$ rabin2 -zq Telegram\ X.app/Telegram\ X | grep -i activityItems
0x1000df034 45 44 initWithActivityItems:applicationActivities:

Recebimento de Itens

Ao receber itens, você deve verificar:

  • se o aplicativo declara tipos de documento personalizados analisando as UTIs Exportadas/Importadas (guia "Info" do projeto no Xcode). A lista de todas as UTIs (Identificadores de Tipo Uniforme) declaradas pelo sistema pode ser encontrada na Documentação de Desenvolvedor da Apple arquivada.
  • se o aplicativo especifica tipos de documento que pode abrir analisando os Tipos de Documento (guia "Info" do projeto no Xcode). Se presentes, eles consistem em um nome e uma ou mais UTIs que representam o tipo de dados (por exemplo, "public.png" para arquivos PNG). O iOS usa isso para determinar se o aplicativo é elegível para abrir um determinado documento (especificar UTIs Exportadas/Importadas não é suficiente).
  • se o aplicativo verifica adequadamente os dados recebidos analisando a implementação de application:openURL:options: (ou sua versão obsoleta UIApplicationDelegate application:openURL:sourceApplication:annotation:) no app delegate.

Se não tiver o código-fonte, você ainda pode analisar o arquivo Info.plist e pesquisar por:

  • UTExportedTypeDeclarations/UTImportedTypeDeclarations se o aplicativo declara tipos de documento personalizados exportados/importados.
  • CFBundleDocumentTypes para ver se o aplicativo especifica tipos de documento que pode abrir.

Uma explicação muito completa sobre o uso dessas chaves pode ser encontrada no Stackoverflow.

Vejamos um exemplo do mundo real. Tomaremos um aplicativo de Gerenciador de Arquivos e analisaremos essas chaves. Usamos objection aqui para ler o arquivo Info.plist.

objection --gadget SomeFileManager run ios plist cat Info.plist

Observe que isso é o mesmo que se recuperássemos o IPA do telefone ou acessássemos via, por exemplo, SSH e navegássemos até a pasta correspondente no IPA / sandbox do aplicativo. No entanto, com o objection estamos a apenas um comando de nosso objetivo, e isso ainda pode ser considerado análise estática.

A primeira coisa que notamos é que o aplicativo não declara nenhum tipo de documento personalizado importado, mas encontramos alguns exportados:

UTExportedTypeDeclarations =     (
            {
        UTTypeConformsTo =             (
            "public.data"
        );
        UTTypeDescription = "SomeFileManager Files";
        UTTypeIdentifier = "com.some.filemanager.custom";
        UTTypeTagSpecification =             {
            "public.filename-extension" =                 (
                ipa,
                deb,
                zip,
                rar,
                tar,
                gz,
                ...
                key,
                pem,
                p12,
                cer
            );
        };
    }
);

O aplicativo também declara os tipos de documento que abre, pois podemos encontrar a chave CFBundleDocumentTypes:

CFBundleDocumentTypes =     (
        {
        ...
        CFBundleTypeName = "SomeFileManager Files";
        LSItemContentTypes =             (
            "public.content",
            "public.data",
            "public.archive",
            "public.item",
            "public.database",
            "public.calendar-event",
            ...
        );
    }
);

Podemos ver que este Gerenciador de Arquivos tentará abrir qualquer coisa que se conforme a qualquer uma das UTIs listadas em LSItemContentTypes e está pronto para abrir arquivos com as extensões listadas em UTTypeTagSpecification/"public.filename-extension". Por favor, tome nota disso, pois será útil se você quiser procurar por vulnerabilidades ao lidar com os diferentes tipos de arquivos ao realizar a análise dinâmica.

Análise Dinâmica

Envio de Itens

Há três coisas principais que você pode facilmente inspecionar realizando instrumentação dinâmica:

  • Os activityItems: um array dos itens sendo compartilhados. Eles podem ser de tipos diferentes, por exemplo, uma string e uma imagem para serem compartilhados via um aplicativo de mensagens.
  • Os applicationActivities: um array de objetos UIActivity representando os serviços personalizados do aplicativo.
  • Os excludedActivityTypes: um array dos Tipos de Atividade que não são suportados, por exemplo, postToFacebook.

Para alcançar isso, você pode fazer duas coisas:

Vejamos um exemplo usando o Telegram para compartilhar uma imagem e um arquivo de texto. Primeiro prepare os hooks, usaremos o REPL do Frida e escreveremos um script para isso:

Interceptor.attach(
ObjC.classes.
    UIActivityViewController['- initWithActivityItems:applicationActivities:'].implementation, {
  onEnter: function (args) {

    printHeader(args)

    this.initWithActivityItems = ObjC.Object(args[2]);
    this.applicationActivities = ObjC.Object(args[3]);

    console.log("initWithActivityItems: " + this.initWithActivityItems);
    console.log("applicationActivities: " + this.applicationActivities);

  },
  onLeave: function (retval) {
    printRet(retval);
  }
});

Interceptor.attach(
ObjC.classes.UIActivityViewController['- excludedActivityTypes'].implementation, {
  onEnter: function (args) {
    printHeader(args)
  },
  onLeave: function (retval) {
    printRet(retval);
  }
});

function printHeader(args) {
  console.log(Memory.readUtf8String(args[1]) + " @ " + args[1])
};

function printRet(retval) {
  console.log('RET @ ' + retval + ': ' );
  try {
    console.log(new ObjC.Object(retval).toString());
  } catch (e) {
    console.log(retval.toString());
  }
};

Você pode armazenar isso como um arquivo JavaScript, por exemplo, inspect_send_activity_data.js e carregá-lo assim:

frida -U Telegram -l inspect_send_activity_data.js

Agora observe a saída quando você primeiro compartilha uma imagem:

[*] initWithActivityItems:applicationActivities: @ 0x18c130c07
initWithActivityItems: (
    "<UIImage: 0x1c4aa0b40> size {571, 264} orientation 0 scale 1.000000"
)
applicationActivities: nil
RET @ 0x13cb2b800:
<UIActivityViewController: 0x13cb2b800>

[*] excludedActivityTypes @ 0x18c0f8429
RET @ 0x0:
nil

e então um arquivo de texto:

[*] initWithActivityItems:applicationActivities: @ 0x18c130c07
initWithActivityItems: (
    "<QLActivityItemProvider: 0x1c4a30140>",
    "<UIPrintInfo: 0x1c0699a50>"
)
applicationActivities: (
)
RET @ 0x13c4bdc00:
<_UIDICActivityViewController: 0x13c4bdc00>

[*] excludedActivityTypes @ 0x18c0f8429
RET @ 0x1c001b1d0:
(
    "com.apple.UIKit.activity.MarkupAsPDF"
)

Você pode ver que:

  • Para a imagem, o item de atividade é um UIImage e não há atividades excluídas.
  • Para o arquivo de texto, há dois itens de atividade diferentes e com.apple.UIKit.activity.MarkupAsPDF é excluído.

No exemplo anterior, não havia applicationActivities personalizados e apenas uma atividade excluída. No entanto, para ilustrar melhor o que você pode esperar de outros aplicativos, compartilhamos uma imagem usando outro aplicativo. Aqui você pode ver vários application activities e excluded activities (a saída foi editada para ocultar o nome do aplicativo de origem):

[*] initWithActivityItems:applicationActivities: @ 0x18c130c07
initWithActivityItems: (
    "<SomeActivityItemProvider: 0x1c04bd580>"
)
applicationActivities: (
    "<SomeActionItemActivityAdapter: 0x141de83b0>",
    "<SomeActionItemActivityAdapter: 0x147971cf0>",
    "<SomeOpenInSafariActivity: 0x1479f0030>",
    "<SomeOpenInChromeActivity: 0x1c0c8a500>"
)
RET @ 0x142138a00:
<SomeActivityViewController: 0x142138a00>

[*] excludedActivityTypes @ 0x18c0f8429
RET @ 0x14797c3e0:
(
    "com.apple.UIKit.activity.Print",
    "com.apple.UIKit.activity.AssignToContact",
    "com.apple.UIKit.activity.SaveToCameraRoll",
    "com.apple.UIKit.activity.CopyToPasteboard",
)

Recebimento de Itens

Após realizar a análise estática, você saberá os tipos de documento que o aplicativo pode abrir e se ele declara algum tipo de documento personalizado e (parte dos) métodos envolvidos. Você pode usar isso agora para testar a parte de recebimento:

  • Compartilhe um arquivo com o aplicativo a partir de outro aplicativo ou envie-o via AirDrop ou e-mail. Escolha o arquivo para que ele dispare o diálogo "Abrir com..." (ou seja, não há um aplicativo padrão que abrirá o arquivo, um PDF, por exemplo).
  • Enganche application:openURL:options: e quaisquer outros métodos identificados em uma análise estática anterior.
  • Observe o comportamento do aplicativo.
  • Além disso, você pode enviar arquivos malformados específicos e/ou usar uma técnica de fuzzing.

Para ilustrar isso com um exemplo, escolhemos o mesmo aplicativo de gerenciador de arquivos do mundo real da seção de análise estática e seguimos estas etapas:

  1. Envie um arquivo PDF de outro dispositivo Apple (por exemplo, um MacBook) via AirDrop.
  2. Aguarde o pop-up do AirDrop aparecer e clique em Aceitar.
  3. Como não há um aplicativo padrão que abrirá o arquivo, ele muda para o pop-up Abrir com.... Lá, podemos selecionar o aplicativo que abrirá nosso arquivo. A próxima captura de tela mostra isso (modificamos o nome de exibição usando Frida para ocultar o nome real do aplicativo):

  4. Após selecionar SomeFileManager, podemos ver o seguinte:

    (0x1c4077000)  -[AppDelegate application:openURL:options:]
    application: <UIApplication: 0x101c00950>
    openURL: file:///var/mobile/Library/Application%20Support
                        /Containers/com.some.filemanager/Documents/Inbox/OWASP_MASVS.pdf
    options: {
        UIApplicationOpenURLOptionsAnnotationKey =     {
            LSMoveDocumentOnOpen = 1;
        };
        UIApplicationOpenURLOptionsOpenInPlaceKey = 0;
        UIApplicationOpenURLOptionsSourceApplicationKey = "com.apple.sharingd";
        "_UIApplicationOpenURLOptionsSourceProcessHandleKey" = "<FBSProcessHandle: 0x1c3a63140;
                                                                    sharingd:605; valid: YES>";
    }
    0x18c7930d8 UIKit!__58-[UIApplication _applicationOpenURLAction:payload:origin:]_block_invoke
    ...
    0x1857cdc34 FrontBoardServices!-[FBSSerialQueue _performNextFromRunLoopSource]
    RET: 0x1
    

Como você pode ver, o aplicativo de envio é com.apple.sharingd e o esquema da URL é file://. Observe que, uma vez que selecionamos o aplicativo que deve abrir o arquivo, o sistema já moveu o arquivo para o destino correspondente, ou seja, para a Inbox do aplicativo. Os aplicativos são então responsáveis por excluir os arquivos dentro de suas Inboxes. Este aplicativo, por exemplo, move o arquivo para /var/mobile/Documents/ e o remove da Inbox.

(0x1c002c760)  -[XXFileManager moveItemAtPath:toPath:error:]
moveItemAtPath: /var/mobile/Library/Application Support/Containers
                            /com.some.filemanager/Documents/Inbox/OWASP_MASVS.pdf
toPath: /var/mobile/Documents/OWASP_MASVS (1).pdf
error: 0x16f095bf8
0x100f24e90 SomeFileManager!-[AppDelegate __handleOpenURL:]
0x100f25198 SomeFileManager!-[AppDelegate application:openURL:options:]
0x18c7930d8 UIKit!__58-[UIApplication _applicationOpenURLAction:payload:origin:]_block_invoke
...
0x1857cd9f4 FrontBoardServices!__FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__
RET: 0x1

Se você observar o stack trace, pode ver como application:openURL:options: chamou __handleOpenURL:, que chamou moveItemAtPath:toPath:error:. Note que agora temos essas informações sem ter o código-fonte do aplicativo alvo. A primeira coisa que tivemos que fazer estava clara: enganchar application:openURL:options:. Quanto ao resto, tivemos que pensar um pouco e criar métodos que poderíamos começar a rastrear e que estão relacionados ao gerenciador de arquivos, por exemplo, todos os métodos contendo as strings "copy", "move", "remove", etc., até encontrarmos o que estava sendo chamado: moveItemAtPath:toPath:error:.

Uma última coisa que vale a pena notar aqui é que essa maneira de lidar com arquivos recebidos é a mesma para esquemas de URL personalizados. Consulte Teste de Custom URL Schemes para obter mais informações.