Análise de malware em resposta a incidentes de segurança (1/3) – Introdução

Este é o primeiro de uma série de 3 posts sobre análise de malware e o seu papel na resposta a incidentes de segurança. Neste artigo será discutida a problemática da resposta a incidentes de segurança que envolvem malware e a motivação para a realização de análise de malware no processo de resposta a incidentes.

Os ataques com malware são hoje em dia algo que faz parte do dia a dia de qualquer organização. Os ataques podem, tal como o malware, ter diversas finalidades e podem de uma forma genérica ser separados em ataques genéricos, que afectam de forma transversal todas as organizações, ou em ataques dirigidos a uma organização em particular.

Alguns exemplos comuns de ataques genéricos com recurso a malware são:

  • Roubo de credenciais, como é o caso de trojans bancários, concebidos para roubar credenciais de acesso a sistemas de home banking;
  • Extorsão, como é o caso do conhecido malware cryptolocker, no qual os documentos de um utilizador são encriptados sendo posteriormente exigido um pagamento para que estes sejam decifrados;
  • Roubo de recursos (e.g. largura de banda, CPU), como é o caso de malware para o envio de campanhas de spam, ou malware que utiliza o CPU para minar bitcoins.

Os ataques dirigidos, embora menos comuns, existem com relativa frequência em determinados tipos de organizações e tipicamente têm como objectivos a recolha de informação privilegiada (e.g. propriedade industrial) e a manutenção de um acesso remoto persistente. Estes ataques de roubo de informação, são hoje em dia conhecidos como APT (Advanced Persistent Threat) e têm sido muito mediáticos nos últimos tempos.

Quer se trate de malware genérico ou de uma APT sofisticada, a indicação da ocorrência de um ataque pode surgir de diversas formas. Através da detecção em sistemas de monitorização de segurança (e.g. SIEM, DLP, IDS), através de colaboradores, clientes, parceiros ou fornecedores que detetem o ataque, ou mesmo através da comunicação social em casos mais mediáticos.

Uma vez detectado o ataque surgem naturalmente um grande número de questões:

  • Qual o objectivo dos atacantes?
  • De que tipo de malware se trata?
  • Quais as suas capacidades e funcionalidades?
  • Que sistemas foram afectados?
  • Que informação foi comprometida?
  • Como podemos bloquear o ataque rapidamente e de forma eficaz?

Sem resposta a estas questões, a solução passa muitas vezes pela execução sem sucesso de software antivírus, pela reinstalação mais ou menos cega de sistemas. Resultando em reinfecções consecutivas, no desconhecimento do impacto de um ataque e na incapacidade de determinar se este foi ou não bem sucedido no roubo de informação sensível.

A existência de um processo definido de resposta a incidentes que inclua a análise de malware é fundamental para que uma organização consiga responder a questões como as acima listadas e para que consiga responder de forma adequada a um ataque, bloqueando-o idealmente antes do compromisso de informação sensível.

Autor: Tiago Pereira @ SysValue

HDF5 para Dados de Performance (1/3)

Este é o 1º artigo de uma série de 3 onde apresentamos e discutimos uma componente essencial do Lighthouse, uma plataforma de Continuous Monitoring as a Service desenvolvida e comercializada pela SysValue, nomeadamente a implementação do sistema de recolha, armazenamento e disponibilização de dados de performance[1] de sistemas e/ou serviços (adiante “ativos”) recorrendo ao formato de dados HDF5.

Neste artigo discutiremos os critérios que nos levaram a escolher o HDF5 e daremos exemplos de quão simples é manipular dados neste formato. No segundo artigo discutiremos os desafios técnicos relacionadas com a implementação de uma API Web que permita a escrita e a leitura concorrentes de dados de performance através de pedidos HTTP. Por fim, no terceiro artigo daremos alguns exemplos de como manipular os dados de performance, para cálculo estatístico ou apresentação de gráficos, utilizando a biblioteca de cálculo cientifico NumPy sobre o HDF5.

Em sistemas de monitorização contínua, tal como o Lighthouse, a recolha e armazenamento de dados de performance dos ativos monitorizados é um requisito indispensável. A posterior análise dos dados de monitorização permite uma compreensão mais detalhada do comportamento desses ativos e é uma ajuda valiosa para o diagnóstico de potenciais problemas. Os dados de performance contêm uma grande variedade de informação dependente dos ativos que os geram, e podem tanto ser dimensionais (representados em diversos sistemas de unidades), como adimensionais (sem unidades). Alguns exemplos de métricas que constituem dados de performance usualmente recolhidos por um sistema de monitorização podem ser: tempo de resposta a pedidos, espaço disponível em disco ou memória, número de transações executadas por minuto num SGBD ou a percentagem de recursos disponíveis, entre outros.

Dois desafios chave se apresentam no momento de recolher, armazenar e utilizar dados de performance. Em primeiro lugar a quantidade de dados a armazenar. Mesmo com uma taxa de aquisição aparentemente modesta, i.e. uma medição por minuto, o número de medições gerado por um sistema que monitorize milhares de ativos atinge rapidamente uma ordem de grandeza dos milhares de milhões de registos. Em segundo lugar a capacidade e disponibilizar toda essa informação em tempo útil.

Estes desafios colocaram-se à nossa equipa quando começámos a avaliar as alternativas para gerir os dados de performance recolhidos pelo Lighthouse. Os requisitos funcionais do problema a resolver são simples de descrever:

  1. Cada métrica de performance corresponde a um único número real. O mesmo sistema pode gerar medições relativas a várias métricas, mas cada métrica será tratada separadamente;
  2. O conjunto de medições ao longo do tempo constitui séries temporais, i.e.,  todas as medições têm associado um tempo de recolha (ou timestamp);
  3. Queremos guardar todas as medições efetuadas, ou seja, não queremos apenas sumarizar os dados mais antigos. Desta forma a quantidade de dados a guardar pode chegar a meio milhão de entradas por ano/por ativo/por métrica;
  4. Queremos apresentar as séries temporais em forma de gráficos, em intervalos de tempo pré determinados e com uma resolução variável, i.e., o número de pontos a apresentar deve ser pré determinado. Se o intervalo de dados contiver mais medições do que o número de pontos definido estas medições devem ser sumarizadas de alguma forma (e.g. uma média local à volta de posições pré-definidas).

A adicionar aos requisitos funcionais tínhamos também alguns pressupostos não funcionais que gostaríamos de ver cumpridos:

  1. Custos de infraestrutura reduzidos;
  2. Recurso a soluções open source;
  3. Possibilidade de utilização do Python como linguagem de acesso aos dados.

Para dar resposta a todos estes requisitos – por vezes contraditórios entre si – equacionámos três possíveis abordagens:

  • Bases de dados Round Robin;
  • Base de dados SQL;
  • HDF5

Round Robin Database: As Bases de Dados Round Robin (DBRR) são excelentes ferramentas para armazenar longas séries temporais. A implementação RRDTool (http://oss.oetiker.ch/rrdtool/), em particular, era praticamente tudo o que pretendíamos: robusta, com bom desempenho, simples de implementar e fácil de utilizar. Já a utilizámos no passado e iremos com certeza voltar a fazê-lo. Infelizmente no nosso caso pretendemos guardar todos os dados e não apenas uma estatística dos dados passados (especialidade das DBRRs). Este requisito, portanto, torna esta abordagem pouco interessante para o nosso problema.

SQL: A abordagem seguinte na nossa lista seria a utilização de uma Base de dados SQL. SQL é a abordagem natural para quem procura robustez, desempenho e simplicidade. No entanto e dadas as restrições em termos de equipamento e custo pareceu-nos que as necessidades de computação e memória para dar resposta à quantidade de dados esperada iriam ultrapassar os limites que nos auto impusemos (ver “benchmark rápido” no fim do artigo).

HDF5: Enquanto procurávamos alternativas de solução, uma pergunta surgia de forma recorrente: “qual o suporte para dados de séries temporais utilizados pelas instituições que geram enormes quantidades de dados?”. Uma resposta recorrente a essa pergunta era o HDF5 (Hierarchical Data Format 5). O HDF é um formato desenhado para guardar dados científicos e tem uma impressionante lista de utilizadores: http://www.hdfgroup.org/users.html. As credenciais desta tecnologia e alguns benchmarks consultados convenceram-nos a tentar esta abordagem.

O HDF5 é um formato poderoso e a sua estrutura hierárquica é altamente adaptável a qualquer tipo de estrutura de dados.  Estas características tornam-o relativamente complexo. Felizmente há um módulo Python que torna a utilização do HDF5 trivial: PyTables (http://www.pytables.org/moin). Com o PyTables criar um novo ficheiro para receber os dados de métricas (e.g. a percentagem de utilização de RAM e SWAP num dado momento de tempo) é tão simples quanto isto:

 
import tables
 
h5f = tables.openFile( “myfile.h5”, “w” )
 
descr = {
    ‘timestamp’: tables.Float64Col(pos=1),
    ‘ram’:       tables.Float64Col(pos=2),
    ‘swap’:      tables.Float64Col(pos=2)
}
 
tbl = h5f.createTable( ‘/’, ‘node’, descr )
 
h5f.close()

O registo de duas novas medições resume-se a:

 
import tables
import time
 
h5f = tables.openFile( “myfile.h5”, “a” )
 
tbl = h5f.getNode( “/”, “node” )
 
tbl.append( [(time.time(), 0.8, 0.1)] )
time.sleep( 1.5 )
tbl.append( [(time.time(), 0.9, 0.15)] )
 
h5f.close()

E finalmente, a leitura de todos os dados da série temporal referentes aos últimos 60 minutos e tão simples quanto:

import tables
import time
 
h5f = tables.openFile( “myfile.h5”, “r” )
 
tbl = h5f.getNode( “/”, “node” )
 
tf = time.time()
ti = tf-3600
 
ram = [(rec[‘timestamp’], rec[‘ram’], rec[‘swap’])
             for rec in tbl.where( “timestamp > ti and timestamp < tf” )]
 
print ram
 
h5f.close()
 

O formato HDF5 foi originalmente concebido para uma utilização WORM (Write Once Read Many). Deste modo o desempenho em escrita deixa um pouco a desejar: 60s para inserir 500000 medidas, i.e., o equivalente a um ano de medições (num Intel core i7 com 8GB de RAM e disco SSD). No nosso caso este tempo de escrita não é critico uma vez que não afeta a experiência do utilizador. No entanto o desempenho de leitura é precisamente o que desejamos: 40ms para ler, de uma vez, as 500000 medidas. Esta velocidade de leitura permite-nos disponibilizar gráficos de dados de performance personalizados a partir de milhares de milhões de pontos, muito rapidamente e com um custo muito baixo.

O próximo desafio será o de disponibilizar uma interface (API) web simples para registo das medições e leitura dos dados. Mas isso fica para o próximo artigo.

Benchmark rápido

Para uma comparação rápida da leitura de uma grande quantidade de dados entre PyTables e o MySQL utilizámos uma tabela/ficheiro HDF5 com 500.000 medições e efetuámos sobre essa tabela uma query que retorna em média 125000 registos.  Ambos os testes foram efetuados utilizando um Intel core i7 com 8GB de RAM e disco SSD.

Após 100 ciclos de teste os tempos de leitura dos 125000 registos foram:

                PyTables                 30.2 ms +/-  1.1 ms

                MySQLdb            240.9 ms +/-  8.5 ms

i.e., o PyTables revelou-se cerca de 8 vezes mais rápido que o MySQL no mesmo hardware.


[1] O termo “performance” é utilizado neste artigo de forma bastante abrangente dado que, na realidade, a prática descrita serve para processar métricas que podem ser relativas a outros indicadores, desde que numéricos.

Autor: José Cruz @ SysValue

logotipo_POR_Lisboa_100pxlogotipo_QREN_100pxlogotipo_UE_Feder_100px