Adi Malyanker | Investigador de segurança

Principais conclusões

  • Um ataque de Consentimento de Aplicação, também conhecido como ataque de Concessão de Consentimento Ilícito, é um tipo de ataque de phishing em que um ator malicioso obtém acesso a uma aplicação e depois explora as permissões que foram concedidas a essa aplicação.
  • O pesquisador da Semperis, Adi Malyanker, descobriu que, sob certas circunstâncias, um agente mal-intencionado pode usar a permissão Diretory.ReadWrite.All ou a permissão DelegatedPermissionGrant.ReadWrite.All no Microsoft Azure para escalar privilégios, acessar recursos e dados confidenciais, lançar um ataque de negação de serviço (DoS) ou até mesmo assumir o controle de um locatário Entra ID.
  • Qualquer organização que utilize aplicações no Entra ID pode estar vulnerável a este ataque, designado por ataque de concessão de consentimento oculto.
  • Tanto quanto é do conhecimento da Semperis, não foi documentado qualquer ataque deste tipo.
  • Os investigadores da Semperis classificam esta vulnerabilidade como potencialmente SEVERA devido à possibilidade de uma aquisição de um inquilino Entra ID.

No Microsoft Azure, a permissão Diretory.ReadWrite.All tem implicações significativas. Esta permissão permite uma multiplicidade de acções, incluindo a edição pelo utilizador e o acesso a todos os dados no diretório.

Parece arriscado? Alguns argumentaram que, quando utilizada isoladamente, a permissão não apresenta nenhum risco inerente. No entanto, a minha investigação indica que um atacante pode utilizar a permissão Diretory.ReadWrite.All (ou a permissão DelegatedPermissionGrant.ReadWrite.All ) para causar danos graves num ambiente Microsoft Entra ID (anteriormente conhecido como Azure AD) ou num ambiente híbrido Microsoft Active Diretory/Entra ID.

O que são permissões de aplicação?

Na Entra ID, as permissões de aplicação concedem a uma aplicação específica a capacidade de executar determinadas acções ou aceder a determinados recursos na Entra ID ou noutros serviços Microsoft. Ao contrário das permissões delegadas, que os utilizadores concedem a aplicações autorizadas, as permissões de aplicações são normalmente concedidas por um administrador durante o processo de registo de aplicações.

A aplicação utiliza então estas permissões para executar tarefas em nome da organização ou dos seus utilizadores, sem necessitar do consentimento individual ou contínuo do utilizador(Figura 1).

Figura 1. Permissões delegadas versus permissões de aplicação

As permissões de aplicação são frequentemente utilizadas quando a aplicação necessita de aceder a recursos ou executar acções que não dependem da identidade ou das permissões de um utilizador específico. Exemplos de permissões de aplicação incluem:

  • Ler perfis de utilizador
  • Gerir grupos
  • Acesso a dados organizacionais

Gerenciar e atribuir cuidadosamente as permissões de aplicativos é essencial. As organizações devem garantir que os aplicativos tenham o nível adequado de acesso, mantendo os padrões de segurança e conformidade no ambiente Entra ID.

O que é Diretory.ReadWrite.All?

Enquanto pesquisava as permissões das aplicações, deparei-me com a permissão Diretory.ReadWrite.All. De acordo com a documentação da Microsoft:

"O Diretory.ReadWrite.All concede acesso que é amplamente equivalente a um administrador de locatário global. As aplicações às quais é concedido o Diretory.ReadWrite.All podem gerir toda a gama de recursos de diretório e podem gerir a autorização para outras aplicações e utilizadores acederem a recursos em toda a organização. Isto inclui recursos de diretório como utilizadores, grupos, aplicações e dispositivos, e recursos não diretórios no Exchange, SharePoint, Teams e outros serviços."

Dado o nome desta permissão, a associação com o Entra ID e a implicação na gestão de diretórios, suspeitei que a permissão era potencialmente perigosa.

Mas no início, depois de examinar as acções que o Diretory.ReadWrite.All permite, não consegui encontrar nenhuma ameaça digna de nota. Outros parecem ter chegado a uma conclusão semelhante. Por exemplo, um excelente artigo publicado pela SpectreOps afirmava que esta permissão, embora poderosa quando combinada com outras permissões, não era suscetível de representar um risco significativo por si só.

Mesmo assim, decidi dar uma olhadela mais atenta.

Comecei a testar o que a permissão Diretory.ReadWrite.All podia fazer a um nível elevado. O que é que um agente malicioso poderia fazer utilizando apenas esta permissão de aplicação?
A boa notícia é que não consegui utilizar a permissão para fazer o seguinte:

  • Adicionar segredos às aplicações
  • Adicionar proprietários
  • Adicionar membros ou proprietários com uma função a um grupo de segurança
  • Conceder uma função de aplicação
  • Repor a palavra-passe de um utilizador

Essas chamadas à API devolveram uma resposta como a que a Figura 2 mostra.

Figura 2. Resposta a uma chamada à API sem êxito

No entanto, consegui utilizar Diretory.ReadWrite.All para fazer o seguinte:

  • Adicionar um novo membro ou proprietário sem qualquer função atribuída a um grupo de segurança
  • Adicionar utilizadores ao diretório
  • Editar consentimentos administrativos
  • Edições limitadas a unidades administrativas

Nota: Mais de 400 chamadas de API são dedicadas ao Teams, dispositivos e OneDrive. Essas chamadas estão fora do âmbito desta investigação.

Acções como a adição de novos utilizadores e membros, que não têm qualquer função atribuída, a grupos podem ser bastante úteis para os atacantes que tentam obter acesso inicial a um diretório ou recursos. Esta possibilidade é suficientemente preocupante, mas perguntei-me se seria possível fazer mais.

Posso utilizar a permissão Diretory.ReadWrite.All para me transformar num Administrador Global?

O Diretory.ReadWrite.All pode conduzir a um potencial vetor de ataque?

Antes de passarmos à parte divertida, uma nota: o token de acesso que o ajuda a iniciar sessão no Azure contém um campo especial chamado âmbito, ou scp. Este campo regista as permissões que são concedidas ao utilizador(Figura 3).

Figura 3. O campo scp

Verifiquei este valor ao longo da minha investigação para determinar se as minhas tentativas de aumentar as permissões foram bem sucedidas.

Para testar minhas suspeitas sobre Diretory.ReadWrite.All, escolhi uma API do Microsoft Graph, oauth2PermissionGrant, que pode criar uma concessão de permissão delegada em um recurso para uma entidade escolhida(Figura 4).

Figura 4: Chamada à API oauth2PermissionGrant para conceder uma autorização delegada

Para fazer esta chamada à API, precisei de conceder a uma aplicação a permissão Diretory.ReadWrite.All(Figura 5). Decidi-me pelo postman-test(Figura 6).

Figura 5. Seleção de uma aplicação com a permissão Diretory.ReadWrite.All
Figura 6: Conceder a permissão Diretory.ReadWrite.All ao postman-test

Também criei uma conta de utilizador fraca, Evil User(Figura 7), para representar um atacante (que simula um utilizador malicioso que é membro do inquilino e um utilizador legítimo obtido por um atacante). Não atribuí a este utilizador quaisquer funções ou permissões(Figura 8).

Figura 7: Criação do utilizador Utilizador mau
Figura 8: O utilizador malvado não tem funções atribuídas

Para invocar as chamadas da API, liguei o princípio de serviço postman-test ao Postman. Vamos rever as propriedades e os requisitos da API(Figura 9):

  • clientId identifica o aplicativo (neste caso, o Graph Explorer) que está autorizado a agir em nome de um usuário conectado. O valor clientId funciona como um substituto para a identidade do usuário.
  • consentType indica se é concedida autorização para que a aplicação cliente se faça passar por todos os utilizadores ou apenas por um utilizador específico.
  • resourceId especifica o recurso (neste caso, o Microsoft Graph) a que o clientId pode aceder com as permissões concedidas.
  • O âmbito define as acções específicas (por exemplo, device.Read.All, Diretor.ReadWrite.All) que o clientId pode executar no resourceId.
Figura 9. Propriedades e requisitos da API

Para resumir as relações entre estas propriedades: Um ou mais utilizadores concedem ao clientId um conjunto aprovado de permissões, conforme definido pelo âmbito. O clientId pode então utilizar essas permissões para aceder ao resourceId especificado.

Agora, de volta à exploração da minha chamada à API. Primeiro, eu precisava do ID do objeto do Graph Explorer. Eu poderia navegar no portal de aplicativos no Azure ou usar a API do Graph(Figura 10) para obter essa ID.

Figura 10: Obter o ID do objeto

Para começar, utilizei o Graph Explorer como clientId e resourceId.

Como Utilizador Maléfico, tentei ler todos os grupos do diretório. Como se pode ver na resposta 403 na Figura 11, esta ação não foi permitida devido a privilégios insuficientes.

Figura 11. Tentativa (e falha) de ler todos os grupos no diretório

Em seguida, tentei adicionar uma permissão delegada - GroupMember.Read.All - aoGraph Explorer para todo o diretório(Figura 12). Se eu conseguisse, todos os usuários do diretório (incluindo o Evil User) deveriam receber essa permissão.

Figura 12. Adicionar uma permissão delegada

Verifiquei que a adição dessa permissão foi bem-sucedida. Observe que, no aplicativo Graph Explorer, a permissão foi mostrada como concedida para todo o diretório(Figura 13).

Figura 13. Verificação da adição de privilégios

Agora, devo poder utilizar a chamada à API para obter todos os grupos(Figura 14).

Figura 14. Tentativa de recuperação de grupos

Estranho... O Graph Explorer tinha a permissão GroupMember.Read.All com o consentimento do administrador, mas continuo a receber uma resposta 403. Porquê?

Lembra-se da definição de permissões delegadas? Como o Graph Explorer usava as permissões do usuário conectado, ele podia ler os membros somente se o usuário conectado tivesse a função ou o nível de privilégio correto para usar essa permissão, o que o Usuário Maligno não tinha. Minha tentativa foi um beco sem saída.

Mas espera. Conhece o ataque do subsídio de consentimento ilícito?

O que é um ataque de concessão de consentimento ilícito?

Em termos simples, um ataque de Concessão de Consentimento Ilícito, também conhecido como um ataque de Consentimento de Aplicação, ocorre quando alguém é enganado para conceder a uma aplicação de terceiros (ou seja, externa) direitos excessivos aos seus dados ou à configuração das suas aplicações do Office 365. A Figura 15 ilustra como este ataque funciona.

Figura 15. Concessão de consentimento ilícito

Em suma, um atacante cria uma aplicação registada no Azure. Depois:

  1. A aplicação solicita o acesso a dados sensíveis, como informações de contacto, e-mails ou documentos.
  2. O utilizador final concede o consentimento à aplicação. O consentimento pode ser obtido através de ataques de phishing ou da injeção de código ilícito num sítio Web de confiança.
  3. A aplicação do atacante recebe a aprovação do consentimento e um token de acesso.
  4. A aplicação maliciosa - e o atacante - têm agora acesso aos dados do utilizador ao nível da conta.

O que aconteceria se um atacante lançasse um ataque de Concessão de Consentimento Ilícito, utilizando uma aplicação maliciosa à qual tivesse sido concedida a permissão Diretory.ReadWrite.All? Era altura de voltar a tentar a minha experiência.

Conheça o ataque da Subvenção de Consentimento Oculto

Desta vez, actuei como um atacante que tentava comprometer um utilizador no diretório alvo através de uma aplicação com a permissão Diretory.ReadWrite.All. A aplicação começaria com privilégios baixos e depois aumentaria as suas permissões após receber o consentimento do administrador(Figura 16).

Figura 16. Fluxo de ataque

Este fluxo de ataque seria possível utilizando uma aplicação, mas decidi utilizar duas aplicações para aumentar a discrição (separando entre a aplicação com a permissão Diretory.ReadWrite.All e a aplicação cujas permissões serão actualizadas). Para esta experiência:

  1. Criei uma conta para um utilizador com privilégios limitados: Harmless Attacker (harmless_attacker). Esta experiência pressupõe que um atacante já obteve acesso a esta conta através de um phishing anterior, de outro ataque ou da adesão a um inquilino.
  2. Criei uma conta de utilizador privilegiada, Privileged User (privileged_user), para representar um administrador global.
  3. Utilizei o Harmless Attacker para criar uma aplicação com a permissão Diretory.ReadWrite.All (neste caso, postman-test, que utilizei para conceder permissões delegadas ao 365stealer).
  4. Usei o Harmless Attacker para criar outra aplicação, chamada 365stealer. Esta aplicação tem as definições mostradas na Figura 17. Note-se que o URI de redireccionamento da aplicação conduz a um endpoint controlado pelo atacante.
Figura 17. Definições da aplicação 365stealer

O Harmless Attacker não conseguiu adicionar permissões privilegiadas à aplicação 365stealer durante a criação, por isso, no início, as permissões da aplicação pareciam as mostradas na Figura 18.

Figura 18. Permissões iniciais do 365stealer

Em seguida, usei um endpoint controlado pelo atacante para configurar um servidor HTTPS, escutando na porta 443(Figura 19).

Figura 19. Configuração do servidor HTTPS

Depois, utilizei o Harmless Attacker para construir uma página de phishing(Figura 20). Num cenário real, o Harmless Attacker tentaria enganar o Privileged User para que este acedesse a esta página através de um e-mail ou de um ficheiro malicioso.

Figura 20. Página de phishing

A página de phishing continha uma hiperligação para uma página de login da Microsoft redireccionada, onde o utilizador privilegiado seria encorajado a iniciar sessão na aplicação maliciosa(365stealer). De acordo com a parte redirect_uri, os tokens serão enviados para o servidor HTTPS que o atacante implementou(Figura 21).

Figura 21. Redireccionamento do utilizador para o servidor HTTPS malicioso

Após um início de sessão bem sucedido, será apresentado ao utilizador um desafio de consentimento(Figura 22).

Figura 22. Desafio de consentimento

Nada nestas permissões ou no desafio de consentimento parece prejudicial (não são necessárias permissões sensíveis), pelo que o utilizador aceitará o consentimento. No nosso caso, ser-lhe-á então apresentada uma página como a mostrada na Figura 23, fechará o browser e continuará com o seu dia.

Figura 23. Não há nada para ver aqui!

Nota: Apesar de a página estar marcada como insegura, existem formas de a contornar. No entanto, isso está para além do âmbito deste artigo. Também pode ver a nota não verificada no consentimento. Abordarei maneiras de ocultar isso mais adiante neste artigo.

Entretanto, sem o conhecimento do utilizador, o atacante recebe o token de acesso e utiliza-o para chamar a API /me(Figura 24).

Figura 24. Ficha de acesso

Este token de acesso contém as permissões que o utilizador aceitou no desafio de consentimento. Um olhar sobre o campo scp do token confirma este facto(Figura 25).

Figura 25. Token de acesso descodificado

Assim, agora o atacante tem um token de acesso (e um token de atualização) de um utilizador privilegiado. Mas este token só pode ser utilizado com a aplicação e as permissões delegadas listadas no âmbito. O atacante ainda não tem a permissão RoleManagement.ReadWrite.Diretory, pelo que qualquer tentativa de chamar a API directoryRoles (através de um pedido para https://graph.microsoft.com/v1.0/directoryRoles) devolverá uma resposta Access Denied(Figura 26).

Figura 26. Acesso negado

Mais uma vez, parece que o potencial do atacante para causar danos é muito limitado. Como é que um atacante pode aumentar os privilégios e chamar outras APIs interessantes?

É aqui que entra o Diretory.ReadWrite.All.

Posso usar a API oauth2PermissionGrant da minha experiência original, que requer apenas a permissão Diretory.ReadWrite.All, e usá-la para conceder a permissão delegada RoleManagement.ReadWrite.Diretory à aplicação 365stealer ?

Para descobrir, primeiro utilizo a API oauth2PermissionGrant para enumerar o clientId e o resourceId utilizados para o 365stealer(Figura 27).

Figura 27. Enumerar 365stealer clientId e resourceId

De seguida, defino o consentType para "AllPrincipals"(Figura 28). Isto elimina o requisito de consentimento do administrador ao conceder as permissões.

Figura 28. Definição de consentType como "AllPrincipals" (Todos os diretores)

Aguarde um momento. A permissão RoleManagement.ReadWrite.Diretory foi concedida mas não está incluída na lista de permissões configuradas(Figura 29).

Figura 29. Permissões configuradas

O Inofensivo é o dono da aplicação 365stealer . Tudo o que preciso de fazer é clicar com o botão direito do rato em RoleManagement.ReadWrite.Diretory e selecionar Add to configured permissions (Figura 30). Mesmo sem este passo, a permissão deve estar disponível para mim.

Figura 30. Adicionar RoleManagement.ReadWrite.Diretory às permissões configuradas

A permissão está agora configurada(Figura 31).

Figura 31. Verificação da configuração

Agora, basta chamar o ponto final /refresh no servidor HTTP para atualizar o token de acesso da conta de utilizador privilegiado e atualizar as suas permissões(Figura 32).

Figura 32. Atualização do token de acesso do utilizador

Um olhar sobre o âmbito do token verifica a adição da permissão RoleManagement.ReadWrite.Diretory(Figura 33).

Figura 33. Verificar a adição da permissão RoleManagement.ReadWrite.Diretory

Com esta permissão no token de acesso, posso agora chamar GET https://graph.microsoft.com/v1.0/directoryRoles(Figura 34).

Figura 34. Enumerar funções de diretório

E agora posso chamar POST https://graph.microsoft.com/v1.0/roleManagement/diretory/roleAssignments para atribuir ao Harmless Attacker o papel de Global Admin(Figura 35).

Figura 35. Atribuição da função de Administrador global ao Atacante inofensivo

Já está! Um olhar sobre as atribuições activas da conta do atacante verifica o aumento de privilégios(Figura 36). A minha tentativa de lançar aquilo a que chamei uma concessão de consentimento oculto foi bem sucedida.

Figura 36. Verificação do aumento de privilégios

Concessão de consentimento oculto: Agora com mais furtividade!

Lembra-se do desafio de consentimento que era apresentado ao utilizador, nas fases iniciais do ataque Hidden Consent Grant? Perguntei-me se poderia esconder esse desafio para tornar o ataque ainda mais furtivo. Ao fazê-lo, criaria um fluxo de ataque semelhante sem exigir a ação do utilizador na forma de a vítima passar qualquer desafio de consentimento, mesmo quando a aplicação solicita permissões elevadas(Figura 37).

Figura 37. Fluxo de ataque sem desafios de consentimento

Comecei com as mesmas permissões que usei na experiência anterior. No entanto, concedi autorização administrativa à aplicação 365stealer (Figura 38). Lembre-se que Diretory.ReadWrite.All permite que o atacante o faça sem qualquer interação do utilizador.

Figura 38. Permissões da API do 365stealer

Agora, para executar o fluxo de phishing e determinar se o Utilizador Privilegiado recebe um desafio de consentimento quando clica na ligação da página de phishing. (Alerta de spoiler: não é.)

Nesta altura, já provei dois pontos:

  • A vítima de phishing não será confrontada com qualquer desafio de consentimento se os privilégios requeridos já tiverem sido aprovados pelo administrador.
  • Mesmo depois de o administrador aprovar o desafio de consentimento para adicionar as permissões da aplicação, o atacante pode adicionar mais permissões editando manualmente as permissões, sem que a vítima saiba.

Difundir a bolsa de consentimento oculto

Perguntei-me se poderia transformar todas as aplicações do diretório em ligações de phishing. Eu sabia que podia transformar todas as aplicações que possuíamos numa aplicação de phishing, alterando o seu URL de redireccionamento. Mas e as aplicações que não cumprem este requisito?

Nos raros casos em que uma aplicação também tem a permissão Application.ReadWrite.All, o atacante pode editar ou adicionar outro URL de redireccionamento - o servidor de escuta do atacante - à aplicação. Desta forma, cada aplicação no diretório é transformada numa entidade maliciosa(Figura 39).

Figura 39. Permissões necessárias para espalhar o ataque no locatário

A chamada à API Graph apresentada na Figura 40 pode ajudar a alterar o URI de redireccionamento da aplicação(Figura 41).

Figura 40. Chamada à API do Graph para alterar o URI de redireccionamento
Figura 41. URIs de redireccionamento

Para este vetor de ataque, o atacante ainda precisa de saber o segredo da aplicação. Felizmente para o atacante, Application.ReadWrite.All permite-lhe adicionar um novo segredo à aplicação(Figura 42).

Figura 42. Adicionar um novo segredo

Nota: Parece haver uma discrepância entre a documentação da Microsoft e as minhas próprias observações. Com base nos meus testes, embora Diretory.ReadWrite.All seja mencionado, poderão ser necessárias permissões adicionais.

Para esta prova de conceito, a Semperis criou e publicou uma nova ferramenta, HiddenConsentGrant, disponível aqui. Esta ferramenta pode ser utilizada para criar um servidor de escuta, à espera de respostas de token de acesso.

Deteção e atenuação

Como se pode detetar um ataque de concessão de consentimento oculto?

  1. Os utilizadores do Semperis Directory Services Protector ( DSP) ou Purple Knight poderão utilizar o indicador de segurança Entra tenant is susceptible to Hidden Consent Grant Attack para verificar e comunicar a possibilidade de um ataque de concessão de consentimento oculto ao inquilino.
  2. Procure por diretores de serviço suspeitos com privilégios elevados, tais como Gestão de funções.LerEscrever.Diretório, Aplicação.ReadWrite.Todose AppRoleAssignment.ReadWrite.All. É possível utilizar a seguinte chamada à API para enumerar estas permissões (Figura 43):
    GET graph.microsoft.com/v1.0/oauth2PermissionGrants?$filter=consentType eq %27AllPrincipals%27
    Figura 43. Enumerar privilégios
  3. Determinar se um administrador concedeu aprovação para as permissões.
  4. Verificar se as permissões são ativamente utilizadas e necessárias.
  5. Remover privilégios desnecessários.
  6. Nos registos de auditoria, procure o Adicionar concessão de autorização delegada entrada com o ator iniciado Aplicação (Figura 44).
    Figura 44. Revisão da entrada de registo Adicionar concessão de permissão delegada

    O Tipo valor deve ser Aplicação e o Tipo de atividade deve ser Adicionar concessão de autorização delegada. Pode utilizar a API Graph para obter todos os registos de auditoria que tenham o tipo de atividade Adicionar concessão de autorização delegada:
    https://graph.microsoft.com/v1.0/auditLogs/directoryAudits?$filter=activityDisplayName eq 'Add delegated permission grant'

    Pode então procurar por "user": null, o que significa que uma aplicação invocou a concessão de permissão delegada(Figura 45).

    Figura 45. Pesquisa de concessões de permissões delegadas invocadas por aplicações
  7. Monitorizar e revogar qualquer OAuth concessões de consentimento. A seguinte chamada à API pode ajudar:
    GET https://graph.microsoft.com/v1.0/oauth2PermissionGrants/
    Esta chamada devolve todas as permissões delegadas concedidas no locatário, o tipo de consentimento e a entidade (Figura 46).
    Figura 46. Descobrir concessões de consentimento OAuth não autorizadas
  8. Por último, evite dar às aplicações as permissões Diretory.ReadWrite.All ou DelegatedPermissionGrant.ReadWrite.All como permissões de aplicação. (A permissão DelegatedPermissionGrant.ReadWrite.All permite que um atacante lance os mesmos ataques que descrevo neste artigo). Sempre que possível, configure permissões mais específicas para as suas aplicações.

Iluminar as subvenções de consentimento ocultas

Este artigo ilustra a prova de conceito de vários fluxos de ataque que abusam da função Diretory.ReadWrite.All para obter potencialmente todas as permissões que um atacante possa querer. Estes fluxos de ataque podem ser mortais nas mãos de um atacante experiente. Reveja e reforce as permissões da sua aplicação agora e feche esta potencial abertura antes que os agentes de ameaças possam tirar partido dela.