Milhões de binários depois. Como o Linux ficou mais forte

Milhões de binários depois. Como o Linux ficou mais forteTL, DR. Neste artigo, exploramos os esquemas de proteção que funcionam imediatamente em cinco distribuições Linux populares. Para cada um, pegamos a configuração padrão do kernel, carregamos todos os pacotes e analisamos os esquemas de segurança nos binários anexados. As distribuições consideradas são OpenSUSE 12.4, Debian 9, CentOS, RHEL 6.10 e 7, bem como Ubuntu 14.04, 12.04 e 18.04 LTS.

Os resultados confirmam que mesmo esquemas básicos como empilhamento de canários e código independente de posição ainda não são adotados por todos. A situação é ainda pior para os compiladores quando se trata de proteção contra vulnerabilidades como o conflito de pilha, que ganhou destaque em janeiro após a publicação informações sobre vulnerabilidades do systemd. Mas nem tudo é tão desesperador. Um número significativo de binários implementa métodos básicos de proteção e seu número cresce de versão para versão.

A análise mostrou que o maior número de métodos de proteção é implementado no Ubuntu 18.04 nos níveis de sistema operacional e de aplicativo, seguido pelo Debian 9. Por outro lado, OpenSUSE 12.4, CentOS 7 e RHEL 7 também implementam esquemas básicos de proteção e proteção contra colisão de pilha é usado ainda mais amplamente com um conjunto muito mais denso de pacotes padrão.

Introdução

É difícil garantir software de alta qualidade. Apesar do grande número de ferramentas avançadas para análise estática de código e análise dinâmica de tempo de execução, bem como do progresso significativo no desenvolvimento de compiladores e linguagens de programação, o software moderno ainda sofre de vulnerabilidades que são constantemente exploradas por invasores. A situação é ainda pior em ecossistemas que incluem código legado. Nesses casos, não apenas enfrentamos o eterno problema de encontrar possíveis erros exploráveis, mas também somos limitados por estruturas estritas de compatibilidade com versões anteriores, que muitas vezes exigem que preservemos código limitado ou, pior ainda, vulnerável ou com bugs.

É aqui que entram em ação os métodos de proteção ou fortalecimento de programas. Não podemos evitar alguns tipos de erros, mas podemos dificultar a vida do invasor e resolver parcialmente o problema, prevenindo ou prevenindo operacional esses erros. Essa proteção é usada em todos os sistemas operacionais modernos, mas os métodos diferem muito em complexidade, eficiência e desempenho: desde canários de pilha e ASLR para proteção total TPI и ROP. Neste artigo, veremos quais métodos de proteção são usados ​​nas distribuições Linux mais populares na configuração padrão e também examinaremos as propriedades dos binários distribuídos por meio dos sistemas de gerenciamento de pacotes de cada distribuição.

CVE e segurança

Todos nós já vimos artigos com títulos como “Os aplicativos mais vulneráveis ​​do ano” ou “Os sistemas operacionais mais vulneráveis”. Geralmente eles fornecem estatísticas sobre o número total de registros sobre vulnerabilidades como CVE (vulnerabilidade e exposições comuns), obtido de Banco de dados nacional de vulnerabilidades (NVD) de NIST e outras fontes. Posteriormente, esses aplicativos ou SO são classificados pelo número de CVEs. Infelizmente, embora os CVEs sejam muito úteis para rastrear problemas e informar fornecedores e usuários, eles dizem pouco sobre a segurança real do software.

Como exemplo, considere o número total de CVEs nos últimos quatro anos para o kernel Linux e as cinco distribuições de servidores mais populares, nomeadamente Ubuntu, Debian, Red Hat Enterprise Linux e OpenSUSE.

Milhões de binários depois. Como o Linux ficou mais forte
Fig. 1

O que este gráfico nos diz? Um número maior de CVEs significa que uma distribuição é mais vulnerável que outra? Nenhuma resposta. Por exemplo, neste artigo você verá que o Debian possui mecanismos de segurança mais fortes em comparação com, digamos, OpenSUSE ou RedHat Linux, e ainda assim o Debian possui mais CVEs. No entanto, não significam necessariamente segurança enfraquecida: mesmo a presença de um CVE não indica se uma vulnerabilidade é explorado. As pontuações de gravidade fornecem uma indicação de como provavelmente exploração de uma vulnerabilidade, mas, em última análise, a explorabilidade depende fortemente das proteções presentes nos sistemas afetados e dos recursos e capacidades dos atacantes. Além disso, a ausência de relatórios CVE nada diz sobre outros não registrado ou desconhecido vulnerabilidades. A diferença no CVE pode ser devida a outros fatores além da qualidade do software, incluindo os recursos alocados para testes ou o tamanho da base de usuários. No nosso exemplo, o maior número de CVEs do Debian pode simplesmente indicar que o Debian envia mais pacotes de software.

Obviamente, o sistema CVE fornece informações úteis que permitem criar proteções adequadas. Quanto melhor compreendermos as razões do fracasso do programa, mais fácil será identificar possíveis métodos de exploração e desenvolver mecanismos apropriados detecção e resposta. Na Fig. 2 mostra as categorias de vulnerabilidades para todas as distribuições nos últimos quatro anos (fonte). Fica imediatamente claro que a maioria dos CVEs se enquadra nas seguintes categorias: negação de serviço (DoS), execução de código, overflow, corrupção de memória, vazamento de informações (exfiltração) e escalonamento de privilégios. Embora muitos CVEs sejam contados múltiplas vezes em categorias diferentes, em geral os mesmos problemas persistem ano após ano. Na próxima parte do artigo avaliaremos a utilização de diversos esquemas de proteção para evitar a exploração dessas vulnerabilidades.

Milhões de binários depois. Como o Linux ficou mais forte
Fig. 2

Tarefas

Neste artigo pretendemos responder às seguintes questões:

  • Qual é a segurança das diferentes distribuições Linux? Quais mecanismos de proteção existem no kernel e nas aplicações do espaço do usuário?
  • Como a adoção de mecanismos de segurança mudou ao longo do tempo nas distribuições?
  • Quais são as dependências médias de pacotes e bibliotecas para cada distribuição?
  • Quais proteções são implementadas para cada binário?

Seleção de distribuições

Acontece que é difícil encontrar estatísticas precisas sobre instalações de distribuição, pois na maioria dos casos o número de downloads não indica o número de instalações reais. No entanto, as variantes Unix constituem a maioria dos sistemas de servidores (em servidores web 69,2%, por estatísticas W3techs e outras fontes), e sua participação está em constante crescimento. Assim, para nossa pesquisa focamos nas distribuições disponíveis prontas para uso na plataforma Parceria . Em particular, selecionamos o seguinte sistema operacional:

Distribuição/versão
núcleo
Construir

OpenSUSE 12.4
4.12.14-95.3-padrão
#1 SMP Quarta, 5 de dezembro 06:00:48 UTC 2018 (63a8d29)

Debian 9 (alongamento)
4.9.0-8-amd64
#1 SMP Debian 4.9.130-2 (2018/10/27)

6.10 CentOS
2.6.32-754.10.1.el6.x86_64
#1 SMP terça-feira, 15 de janeiro 17:07:28 UTC 2019

7 CentOS
3.10.0-957.5.1.el7.x86_64
#1 SMP sexta-feira, 1º de fevereiro 14:54:57 UTC 2019

Servidor Red Hat Enterprise Linux 6.10 (Santiago)
2.6.32-754.9.1.el6.x86_64
Nº 1 SMP Quarta, 21 de novembro 15:08:21 EST 2018

Servidor Red Hat Enterprise Linux 7.6 (Maipo)
3.10.0-957.1.3.el7.x86_64
#1 SMP Qui, 15 de novembro 17:36:42 UTC 2018

Ubuntu 14.04 (Trusty Tahr)
4.4.0–140-genérico

# 166 ~ 14.04.1-Ubuntu SMP Sábado, 17 de novembro 01:52:43 UTC 20…

Ubuntu 16.04 (Xenial Xerus)
4.15.0–1026-gcp
# 27 ~ 16.04.1-Ubuntu SMP Sexta-feira, 7 de dezembro 09:59:47 UTC 2018

Ubuntu 18.04 (castor biônico)
4.15.0–1026-gcp
#27-Ubuntu SMP Qui, 6 de dezembro 18:27:01 UTC 2018

Tabela 1

Análise

Vamos estudar a configuração padrão do kernel, bem como as propriedades dos pacotes disponíveis através do gerenciador de pacotes de cada distribuição pronta para uso. Assim, consideramos apenas pacotes dos espelhos padrão de cada distribuição, ignorando pacotes de repositórios instáveis ​​(como os espelhos de 'teste' do Debian) e pacotes de terceiros (como os pacotes da Nvidia dos espelhos padrão). Além disso, não consideramos compilações personalizadas de kernel ou configurações com segurança reforçada.

Análise de configuração do kernel

Aplicamos um roteiro de análise baseado em verificador kconfig gratuito. Vamos dar uma olhada nos parâmetros de proteção prontos para uso das distribuições nomeadas e compará-los com a lista de Projeto Central de Autodefesa (KSPP). Para cada opção de configuração, a Tabela 2 descreve a configuração desejada: a caixa de seleção é para distribuições que atendem às recomendações do KSSP (veja a seguir uma explicação dos termos). aqui; Em artigos futuros explicaremos quantos desses métodos de segurança surgiram e como hackear um sistema na sua ausência).

Milhões de binários depois. Como o Linux ficou mais forte

Milhões de binários depois. Como o Linux ficou mais forte

Em geral, os novos kernels têm configurações mais rígidas prontas para uso. Por exemplo, CentOS 6.10 e RHEL 6.10 no kernel 2.6.32 não possuem a maioria dos recursos críticos implementados em kernels mais recentes, como FOTO, permissões RWX estritas, randomização de endereço ou proteção copy2usr. Deve-se notar que muitas das opções de configuração na tabela não estão disponíveis em versões mais antigas do kernel e não são aplicáveis ​​na realidade - isso ainda é indicado na tabela como falta de proteção adequada. Da mesma forma, se uma opção de configuração não estiver presente em uma determinada versão e a segurança exigir que essa opção seja desativada, esta será considerada uma configuração razoável.

Outro ponto a considerar na interpretação dos resultados: algumas configurações de kernel que aumentam a superfície de ataque também podem ser utilizadas para segurança. Esses exemplos incluem uprobes e kprobes, módulos de kernel e BPF/eBPF. A nossa recomendação é utilizar os mecanismos acima para fornecer proteção real, uma vez que a sua utilização não é trivial e a sua exploração pressupõe que os atores maliciosos já estabeleceram uma posição no sistema. Mas se essas opções estiverem habilitadas, o administrador do sistema deverá monitorar ativamente os abusos.

Olhando mais detalhadamente as entradas na Tabela 2, vemos que os kernels modernos oferecem diversas opções para proteção contra a exploração de vulnerabilidades, como vazamento de informações e estouros de pilha/heap. No entanto, notamos que mesmo as distribuições populares mais recentes ainda não implementaram proteções mais complexas (por exemplo, com patches segurança) ou proteção moderna contra ataques de reutilização de código (por exemplo, combinação de randomização com esquemas como R^X para código). Para piorar a situação, mesmo estas defesas mais avançadas não protegem contra toda a gama de ataques. Portanto, é fundamental que os administradores de sistema complementem as configurações inteligentes com soluções que ofereçam detecção e prevenção de exploits em tempo de execução.

Análise de Aplicação

Não é de surpreender que distribuições diferentes tenham características de pacotes, opções de compilação, dependências de biblioteca diferentes, etc. relacionado distribuições e pacotes com um pequeno número de dependências (por exemplo, coreutils no Ubuntu ou Debian). Para avaliar as diferenças, baixamos todos os pacotes disponíveis, extraímos seu conteúdo e analisamos os binários e dependências. Para cada pacote, rastreamos os outros pacotes dos quais ele depende e, para cada binário, rastreamos suas dependências. Nesta seção resumimos brevemente as conclusões.

Distribuições

No total, baixamos 361 pacotes para todas as distribuições, extraindo apenas pacotes de espelhos padrão. Ignoramos pacotes sem executáveis ​​ELF, como fontes, fontes, etc. Após a filtragem, restaram 556 pacotes, contendo um total de 129 binários. A distribuição de pacotes e arquivos entre distribuições é mostrada na Fig. 569.

Milhões de binários depois. Como o Linux ficou mais forte
Fig. 3

Você pode notar que quanto mais moderna a distribuição, mais pacotes e binários ela contém, o que é lógico. No entanto, os pacotes Ubuntu e Debian incluem muito mais binários (tanto executáveis ​​quanto módulos e bibliotecas dinâmicas) do que CentOS, SUSE e RHEL, o que potencialmente afeta a superfície de ataque do Ubuntu e Debian (deve-se notar que os números refletem todos os binários de todas as versões pacote, ou seja, alguns arquivos são analisados ​​diversas vezes). Isto é especialmente importante quando você considera dependências entre pacotes. Assim, uma vulnerabilidade em um único pacote binário pode afetar muitas partes do ecossistema, assim como uma biblioteca vulnerável pode afetar todos os binários que a importam. Como ponto de partida, vejamos a distribuição do número de dependências entre pacotes em diferentes sistemas operacionais:

Milhões de binários depois. Como o Linux ficou mais forte
Fig. 4

Em quase todas as distribuições, 60% dos pacotes possuem pelo menos 10 dependências. Além disso, alguns pacotes possuem um número significativamente maior de dependências (mais de 100). O mesmo se aplica às dependências reversas de pacotes: como esperado, alguns pacotes são usados ​​por muitos outros pacotes na distribuição, portanto, vulnerabilidades nesses poucos pacotes selecionados são de alto risco. Como exemplo, a tabela a seguir lista os 20 pacotes com o número máximo de dependências reversas no SLES, Centos 7, Debian 9 e Ubuntu 18.04 (cada célula indica o pacote e o número de dependências reversas).

Milhões de binários depois. Como o Linux ficou mais forte
Tabela 3

Fato interessante. Embora todos os sistemas operacionais analisados ​​sejam construídos para a arquitetura x86_64, e a maioria dos pacotes tenha a arquitetura definida como x86_64 e x86, os pacotes geralmente contêm binários para outras arquiteturas, conforme mostrado na Figura 5. XNUMX.

Milhões de binários depois. Como o Linux ficou mais forte
Fig. 5

Na próxima seção, nos aprofundaremos nas características dos binários analisados.

Estatísticas de proteção de arquivos binários

No mínimo, você precisa explorar um conjunto básico de opções de segurança para seus binários existentes. Várias distribuições Linux vêm com scripts que realizam essas verificações. Por exemplo, o Debian/Ubuntu possui esse script. Aqui está um exemplo de seu trabalho:

$ hardening-check $(which docker)
/usr/bin/docker:
 Position Independent Executable: yes
 Stack protected: yes
 Fortify Source functions: no, only unprotected functions found!
 Read-only relocations: yes
 Immediate binding: yes

O script verifica cinco funções de proteção:

  • Executável Independente de Posição (PIE): Indica se a seção de texto de um programa pode ser movida na memória para obter randomização se o ASLR estiver habilitado no kernel.
  • Stack Protected: Se os canários de pilha estão habilitados para proteção contra ataques de colisão de pilha.
  • Fortify Source: se as funções inseguras (por exemplo, strcpy) são substituídas por suas contrapartes mais seguras e as chamadas verificadas em tempo de execução são substituídas por suas contrapartes não verificadas (por exemplo, memcpy em vez de __memcpy_chk).
  • Relocações somente leitura (RELRO): Se as entradas da tabela de relocação serão marcadas como somente leitura se forem acionadas antes do início da execução.
  • Ligação imediata: se o vinculador de tempo de execução permite todas as movimentações antes do início da execução do programa (isso equivale a um RELRO completo).

Os mecanismos acima são suficientes? Infelizmente não. Existem maneiras conhecidas de contornar todas as defesas acima, mas quanto mais resistente for a defesa, maior será a barreira para o atacante. Por exemplo, Métodos de desvio RELRO mais difícil de aplicar se o PIE e a vinculação imediata estiverem em vigor. Da mesma forma, o ASLR completo requer trabalho adicional para criar uma exploração funcional. No entanto, atacantes sofisticados já estão preparados para enfrentar tais proteções: a sua ausência irá essencialmente acelerar o hack. É, portanto, essencial que estas medidas sejam consideradas necessárias mínimo.

Queríamos estudar quantos arquivos binários nas distribuições em questão são protegidos por estes e três outros métodos:

  • Bit inexecutável (NX) impede a execução em qualquer região que não deveria ser executável, como o heap da pilha, etc.
  • RPATH/RUNPATH denota o caminho de execução usado pelo carregador dinâmico para encontrar bibliotecas correspondentes. O primeiro é obrigatório para qualquer sistema moderno: sua ausência permite que invasores gravem arbitrariamente a carga na memória e a executem como está. Para o segundo, configurações incorretas do caminho de execução ajudam na introdução de código não confiável que pode levar a vários problemas (por exemplo, escalonamento de privilégiosE outros problemas).
  • A proteção contra colisão de pilha fornece proteção contra ataques que fazem com que a pilha se sobreponha a outras áreas da memória (como o heap). Dadas as explorações recentes que abusam vulnerabilidades de colisão de heap do systemd, achamos apropriado incluir esse mecanismo em nosso conjunto de dados.

Então, sem mais delongas, vamos aos números. As Tabelas 4 e 5 contêm um resumo da análise de arquivos executáveis ​​e bibliotecas de diversas distribuições, respectivamente.

  • Como você pode ver, a proteção do NX é implementada em todos os lugares, com raras exceções. Em particular, pode-se notar seu uso ligeiramente menor nas distribuições Ubuntu e Debian em comparação com CentOS, RHEL e OpenSUSE.
  • Canários de pilha estão faltando em muitos lugares, especialmente em distribuições com kernels mais antigos. Algum progresso está sendo visto nas distribuições mais recentes do Centos, RHEL, Debian e Ubuntu.
  • Com exceção do Debian e do Ubuntu 18.04, a maioria das distribuições tem suporte ruim para PIE.
  • A proteção contra colisão de pilha é fraca no OpenSUSE, Centos 7 e RHEL 7, e praticamente inexistente em outros.
  • Todas as distribuições com kernels modernos têm algum suporte para RELRO, com o Ubuntu 18.04 liderando e o Debian vindo em segundo.

Conforme já mencionado, as métricas nesta tabela são a média para todas as versões do arquivo binário. Se você observar apenas as versões mais recentes dos arquivos, os números serão diferentes (por exemplo, consulte Progresso do Debian com implementação PIE). Além disso, a maioria das distribuições normalmente testa apenas a segurança de algumas funções no binário ao calcular estatísticas, mas nossa análise mostra a verdadeira porcentagem de funções que são reforçadas. Portanto, se 5 das 50 funções estiverem protegidas em um binário, daremos nota 0,1, o que corresponde a 10% das funções sendo fortalecidas.

Milhões de binários depois. Como o Linux ficou mais forte
Tabela 4. Características de segurança dos arquivos executáveis ​​mostrados na Fig. 3 (implementação de funções relevantes como porcentagem do número total de arquivos executáveis)

Milhões de binários depois. Como o Linux ficou mais forte
Tabela 5. Características de segurança para as bibliotecas mostradas na Fig. 3 (implementação de funções relevantes em percentagem do número total de bibliotecas)

Então há progresso? Definitivamente existe: isso pode ser visto nas estatísticas de distribuições individuais (por exemplo, Debian), bem como nas tabelas acima. Como exemplo na Fig. A Figura 6 mostra a implementação de mecanismos de proteção em três distribuições sucessivas do Ubuntu LTS 5 (omitimos as estatísticas de proteção contra colisão de pilha). Notamos que, de versão para versão, mais e mais arquivos suportam canários de pilha e também mais e mais binários são enviados com proteção RELRO total.

Milhões de binários depois. Como o Linux ficou mais forte
Fig. 6

Infelizmente, vários arquivos executáveis ​​em diferentes distribuições ainda não possuem nenhuma das proteções acima. Por exemplo, olhando para o Ubuntu 18.04, você notará o binário ngetty (um substituto do getty), bem como os shells mksh e lksh, o interpretador picolisp, os pacotes nvidia-cuda-toolkit (um pacote popular para aplicativos acelerados por GPU como estruturas de aprendizado de máquina) e klibc -utils. Da mesma forma, o binário mandos-client (uma ferramenta administrativa que permite reiniciar automaticamente máquinas com sistemas de arquivos criptografados), bem como o rsh-redone-client (uma reimplementação de rsh e rlogin) são fornecidos sem proteção NX, embora tenham direitos SUID: (. Além disso, vários binários suid não possuem proteção básica, como canários de pilha (por exemplo, o binário Xorg.wrap do pacote Xorg).

Resumo e observações finais

Neste artigo, destacamos vários recursos de segurança das distribuições Linux modernas. A análise mostrou que a distribuição Ubuntu LTS mais recente (18.04) implementa, em média, a proteção mais forte em nível de sistema operacional e de aplicativo entre distribuições com kernels relativamente novos, como Ubuntu 14.04, 12.04 e Debian 9. No entanto, as distribuições examinadas CentOS, RHEL e OpenSUSE em nosso conjunto por padrão eles produzem um conjunto mais denso de pacotes, e nas versões mais recentes (CentOS e RHEL) eles têm uma porcentagem maior de proteção contra colisão de pilha em comparação com concorrentes baseados em Debian (Debian e Ubuntu). Comparando as versões CentOS e RedHat, notamos grandes melhorias na implementação de stack canaries e RELRO das versões 6 a 7, mas em média o CentOS tem mais recursos implementados que o RHEL. Em geral, todas as distribuições devem prestar atenção especial à proteção PIE, que, com exceção do Debian 9 e Ubuntu 18.04, é implementada em menos de 10% dos binários do nosso conjunto de dados.

Finalmente, deve-se notar que embora tenhamos conduzido a pesquisa manualmente, existem muitas ferramentas de segurança disponíveis (por exemplo, Lynis, tigre, Hubble), que realizam análises e ajudam a evitar configurações inseguras. Infelizmente, mesmo uma proteção forte em configurações razoáveis ​​não garante a ausência de explorações. É por isso que acreditamos firmemente que é vital garantir monitoramento confiável e prevenção de ataques em tempo real, concentrando-se nos padrões de exploração e prevenindo-os.

Fonte: habr.com

Adicionar um comentário