Este artigo está disponível em Inglês na PenTest Magazine e também no Pulse (LinkedIn).
Introdução
A Análise Forense é uma das áreas que mais crescem na Segurança da Informação. Juntamente com o Teste de Penetração (ou Pentesting), conhecimentos em forense são também valiosos e é fácil de se perceber isso, uma vez que são frequentes notícias na TV, jornais e Internet a respeito de incidentes relacionados a invasão de servidores, ransomware, vazamento de dados, e assim por diante. Portanto, ser capaz de coletar e analisar corretamente evidências são qualidades essenciais para um profissional ou futuro candidato a analista forense.
Alguns profissionais de TI pensam e falam sobre análise forense como sendo restrita apenas à verificação de armazenamento persistente: disco rígido, pen drives, flash drives… Todavia, nunca se deve esquecer a memória RAM! De fato, dados preciosos podem ser encontrados lá, como rastros de malware. Há alguns repositórios públicos com arquivos interessantes contendo malware real para propósitos de estudo. Você, como profissional de segurança, deve usá-los em suas práticas.
Neste artigo, gostaria de apresentar o Volatility Framework, uma iniciativa de código aberto para realizar análise forense através da investigação de memória RAM. Toda a documentação, FAQs, contribuições e downloads podem ser encontrados no site oficial do projeto em http://www.volatilityfoundation.org/. A Fundação por trás do projeto também possui um repositório no Github que podem ser encontrado em https://github.com/volatilityfoundation/. Lá, você pode encontrar amostras de memória, perfis e coisas importantes para a comunidade. Recomendo fortemente a visita.
Antes de mais nada, é preciso entender o que o Volatility é, o que não é e o que pode fazer. Além de ser um framework (no sentido de que é possível usá-lo como bibliotecas para escrever seus próprios scripts em Python), o Volatility é apresentado como um executável binário, disponível para sistemas operacionais de desktop famosos (Linux, Windows e macOS). Ele checa e analisa dumps de memória, procurando padrões, como listas de processos, rastros de malware, conexões IP, payloads. O Volatility vem com um conjunto de perfis de sistemas operacionais que podem ser usados para ajudar a melhorar o desempenho durante as buscas às entranhas da memória.
Para os propósitos de escrita deste artigo, escolhi fazer todos os testes e laboratórios no meu ambiente Ubuntu, uma vez que é uma distribuição amplamente usada e conhecida.
Contato Inicial e Instalação
É óbvio (uma vez que estamos tratando de programação em Python), que o Volatility precisa do Python para funcionar. Ele roda perfeitamente com a versão 2.7. Para mostrar a você meu ambiente, seguem alguns detalhes do meu sistema:
$ more /etc/issue Ubuntu 14.04.5 LTS \n \l $ python -V Python 2.7.6 $ uname -a Linux pc-ubuntu 3.19.0-65-generic #73~14.04.1-Ubuntu SMP Wed Jun 29 21:05:22 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux $ free total used free shared buffers cached Mem: 2032356 1736088 296268 16204 104948 773900 -/+ buffers/cache: 857240 1175116 Swap: 1046524 0 1046524
Vamos começar! Como estamos lidando com programação, não faz sentido baixar a versão binária do produto. Portanto, vamos com o código fonte:
$ ls -lph total 3.2M -rw-rw-r-- 1 mauricio mauricio 3.2M Aug 16 10:57 volatility-2.5.zip drwxrwxr-x 7 mauricio mauricio 4.0K Oct 21 2015 volatility-master/
A instalação do código fonte é necessária se você precisa usar o framework como biblioteca (nosso caso). Entretanto, você pode ter problemas com algumas tarefas manuais se for preciso atualizar o software no futuro. A instalação é realizada pela linha de comando abaixo (boa parte da saída foi omitida para evitar desperdício de espaço):
$ cd volatility-master $ sudo python setup.py install ... Installing vol.py script to /usr/local/bin Installed /usr/local/lib/python2.7/dist-packages/ volatility-2.5-py2.7.egg Processing dependencies for volatility==2.5 Finished processing dependencies for volatility==2.5
E uma possível atualização futura precisa ser precedida por uma remoção manual da versão existente:
$ sudo rm -rf /usr/local/lib/python2.6/dist-packages/volatility $ sudo rm `which vol.py` $ sudo rm -rf /usr/local/contrib/plugins
Quando você executa o vol.py (script principal), pode ser que apareçam as mensagens abaixo:
*** Failed to import volatility.plugins.ssdt (NameError: name 'distorm3' is not defined) *** Failed to import volatility.plugins.mac.apihooks_kernel (ImportError: No module named distorm3) *** Failed to import volatility.plugins.malware.threads (NameError: name 'distorm3' is not defined) *** Failed to import volatility.plugins.malware.apihooks (NameError: name 'distorm3' is not defined) *** Failed to import volatility.plugins.mac.check_syscall_shadow (ImportError: No module named distorm3) *** Failed to import volatility.plugins.mac.apihooks (ImportError: No module named distorm3)
Isto acontece porque o Volatility precisa dos módulos distorm3 e pycrypto. Eles podem ser rapidamente instalados via pip. Se você não possui o pip no seu sistema, precisa instalá-lo primeiro. Dependendo da sua distribuição Linux, o pacote pode se chamar “pip” ou “python-pip”.
$ sudo pip install pycrypto $ sudo pip install distorm3
Alguns plugins podem necessitar de outros módulos, como Yara e PII. Isso dependerá da sua necessidade. Use o pip sempre que for preciso.
O Código
Vamos começar nosso código. Como mencionado antes, este é um cenário de laboratório, mas qualquer aspecto apresentado pode ser completamente reproduzido em um ambiente real. Todo o foco aqui é escrever código e usar o poder do framework para nossos propósitos.
Citei anteriormente que o Volatility usa perfis de sistemas operacionais. Isto é necessário, pois o programa precisa saber o sistema de onde veio o dump de memória, para que ele possa carregar os interpretadores de símbolo corretos. Se você não especificar nenhum perfil, o default será “WinXPSP2x86”. Há perfis para versões de Windows, Linux e macOS.
Fiz o download de algumas amostras de memória. Aqui, estão três delas:
- win7sp1.vmem: Uma máquina Windows 7 SP1 x64 com cerca de 1 GB de memória;
- winxpsp3.vmem: Uma máquina Windows XP SP3 x86 com o malware e aproximadamente 265 MB de RAM;
- victoria-v8.memdump.img: Uma máquina usada num desafio com informação oculta para descobrir (cerca de 256 MB de tamanho).
Depois de testar todas, decidi escolher apenas mostrar a ferramenta. Para permitir facilmente mostrar os resultados encontrados, usarei o segundo arquivo porque ele possui um malware embutido. Dividirei o código em seções para facilitar o entendimento.
Importando módulos necessários:
#!/usr/bin/python import volatility.conf as conf import volatility.registry as registry import volatility.commands as commands import volatility.addrspace as addrspace import volatility.plugins.taskmods as taskmods from cStringIO import StringIO import sys
Configurando parâmetros e registrando plugins:
registry.PluginImporter() config = conf.ConfObject() registry.register_global_options(config, commands.Command) registry.register_global_options(config, addrspace.BaseAddressSpace) config.parse_options() config.PROFILE="WinXPSP3x86" config.LOCATION = "file:///winxpsp3.vmem"
Manipulando a saída padrão para evitar o comportamento indesejado do método execute() (excesso de exibição de saída):
class Capturing(list): def __enter__(self): self._stdout = sys.stdout sys.stdout = self._stringio = StringIO() return self def __exit__(self, *args): self.extend(self._stringio.getvalue().splitlines()) sys.stdout = self._stdout # Iniciando a variável de saída with Capturing() as big_output: print ''
O código de análise começa aqui:
""" Adquirindo a lista de processos (com cabeçalhos). O método execute() mostra a lista completa de processos obtidos a partir da imagem de memória. """ # A saída do execute() será colocada dentro da variável big_output. with Capturing(big_output) as big_output: p.execute() # Removendo os cabeçalhos de big_output. small_output = big_output[3:] # Adquirindo PIDs e PPIDs pid = list() ppid = list() for line in small_output: pid.append(line.split()[2]) # Obtendo PID ppid.append(line.split()[3]) # Obtendo PPID """ Esta amostra de memória contém o malware Shylock. Além de outras características, ele cria um processo fantasma que inicia outros processos, como o explorer.exe. Assim, precisamos procurar por PIDs que não possuam PPIDs correspondentes. Os processos 'System' serão uma exceção, uma vez que constituem nos primeiros a serem iniciados, como o 'init' em sistemas Unix/Linux. """ # Procurando um PPID "órfão" dentro da lista de PIDs. suspicious = list() for process in ppid: try: found = pid.index(process) except: # Precisamos alimentar os PIDs de processos suspeitos. # A exceção é o "System" (PID 0). if process != "0": suspicious.append(process) # Apresentando eventuais PIDs suspeitos. if len(suspicious) > 0: print "You should investigate the following processes:" for line in suspicious: print "PID: ", line
Portanto, o código final para checar (e prevenir) process IDs suspeitos é:
#!/usr/bin/python import volatility.conf as conf import volatility.registry as registry import volatility.commands as commands import volatility.addrspace as addrspace import volatility.plugins.taskmods as taskmods from cStringIO import StringIO import sys registry.PluginImporter() config = conf.ConfObject() registry.register_global_options(config, commands.Command) registry.register_global_options(config, addrspace.BaseAddressSpace) config.parse_options() config.PROFILE="WinXPSP3x86" config.LOCATION = file:///winxpsp3.vmem class Capturing(list): def __enter__(self): self._stdout = sys.stdout sys.stdout = self._stringio = StringIO() return self def __exit__(self, *args): self.extend(self._stringio.getvalue().splitlines()) sys.stdout = self._stdout # Iniciando a variável de saída with Capturing() as big_output: print '' """ Adquirindo a lista de processos (com cabeçalhos). O método execute() mostra a lista completa de processos obtidos a partir da imagem de memória. """ # A saída do execute() será colocada dentro da variável big_output. with Capturing(big_output) as big_output: p.execute() # Removendo os cabeçalhos de big_output. small_output = big_output[3:] # Adquirindo PIDs e PPIDs pid = list() ppid = list() for line in small_output: pid.append(line.split()[2]) # Obtendo PID ppid.append(line.split()[3]) # Obtendo PPID """ Esta amostra de memória contém o malware Shylock. Além de outras características, ele cria um processo fantasma que inicia outros processos, como o explorer.exe. Assim, precisamos procurar por PIDs que não possuam PPIDs correspondentes. Os processos 'System' serão uma exceção, uma vez que constituem nos primeiros a serem iniciados, como o 'init' em sistemas Unix/Linux. """ # Procurando um PPID "órfão" dentro da lista de PIDs. suspicious = list() for process in ppid: try: found = pid.index(process) except: # Precisamos alimentar os PIDs de processos suspeitos. # A exceção é o "System" (PID 0). if process != "0": suspicious.append(process) # Apresentando eventuais PIDs suspeitos. if len(suspicious) > 0: print "You should investigate the following processes:" for line in suspicious: print "PID: ", line
Conclusão
O Volatility framework fornece uma imensa quantidade de plugins e comandos para ajudar os profissionais de análise forense a vasculhar amostras de memória e encontrar atividades potencialmente suspeitas. Analistas com conhecimento em Python irão adorá-lo e explorar o máximo dele. A quantidade de verificações e testes é considerável e a capacidade de checar não apenas memória de Linux, mas também ambientes com Windows ou macOS, confere ainda mais poder a esta ferramenta. E não podemos esquecer um “pequeno detalhe”: custo zero!
Entretanto, a documentação ainda é um pouco espalhada e não muito detalhada. Seria ótimo se pudesse ser melhorada, especialmente a parte que trata de plugins e da API. Considere isto como um convite a você, entusiasta de segurança, para colaborar e melhorar este incrível projeto.
0 comentário