Malware e forense
Hashes de arquivos em DFIR e a ética de enviar arquivos ao VirusTotal
Como hashes identificam arquivos, por que o SHA-256 substituiu o MD5, como o NSRL filtra softwares conhecidos e o risco de enviar arquivos ao VirusTotal.
Escreva uma primeira regra YARA que case com um arquivo inofensivo e não com outro, e aprenda por que toda regra precisa de testes positivos e negativos.
YARA é uma ferramenta de correspondência de padrões. Você escreve uma regra que descreve como um arquivo é, e o YARA lista os arquivos que se encaixam na descrição. Analistas de malware são quem mais usa. Eles escrevem regras para strings, estruturas de arquivo e sequências de bytes, e depois varrem pastas, imagens de disco e dumps de memória. O mesmo mecanismo funciona em qualquer arquivo que você controla, por exemplo um modelo que nunca pode carregar um marcador interno, ou um arquivo de configuração que nunca pode conter um token.
O YARA não sabe o que é perigoso. Ele compara o que a regra diz. Uma regra é uma afirmação sobre bytes, e ela só vale o que vale essa afirmação.
O projeto fica no GitHub, na organização da VirusTotal. O guia oficial de escrita, hospedado no Read the Docs, é a referência de sintaxe. A VirusTotal também mantém o YARA-X, uma reescrita em Rust que mantém a maior parte da linguagem de regras. Os dois se sobrepõem, mas não são idênticos, então leia as notas de compatibilidade antes de levar uma regra de um para o outro.
Um hash de arquivo responde a uma pergunta estreita: este arquivo exato já é conhecido? Mude um byte e o hash muda. O artigo sobre hashes de arquivo explica isso com detalhes. Uma regra YARA faz uma pergunta mais ampla: este arquivo contém as partes que esperamos? Uma regra pode casar com muitos arquivos que compartilham uma string, uma estrutura ou um padrão de bytes, mesmo que nenhum deles tenha o mesmo hash.
É justamente aí que mora o problema. Um hash quase nunca casa com um arquivo que não deveria. Uma regra descuidada pode casar com metade do seu servidor de arquivos. Por isso, o que você precisa aprender não é tanto a sintaxe. É escrever uma regra que case com o que você quer, e somente com isso, e depois provar que ela faz isso com arquivos que você controla.
Toda regra tem um nome e, dentro de chaves, até três seções:
rule Exemplo_De_Regra {
meta:
description = "Para que serve esta regra"
strings:
$texto = "algum texto"
condition:
$texto
}Nomes de regra usam letras, números e sublinhados, e não podem começar com número. O nome aparece em todo relatório de correspondência, então escolha um que diga ao leitor do que a regra trata.
A seção meta guarda pares chave-valor para as pessoas: uma descrição, um autor, uma data, um link para a pesquisa que originou a regra. O YARA nunca usa a meta para decidir uma correspondência. Mantenha-a correta mesmo assim, porque a próxima pessoa a abrir o arquivo vai confiar nela.
Cada string recebe um identificador que começa com $. A condição se refere às strings por esse identificador. Há três tipos.
Strings de texto ficam entre aspas duplas:
$marcador = "LAB-TRAINING-MARKER"Modificadores mudam a forma como o texto é comparado. nocase ignora maiúsculas e minúsculas. wide casa com texto UTF-16LE, em que cada caractere ASCII é seguido de um byte zero, algo comum em binários do Windows. fullword exige que a ocorrência apareça sozinha, e não dentro de uma palavra maior.
Strings hexadecimais ficam entre chaves e casam com bytes crus:
$mz = { 4D 5A }Esses dois bytes são a assinatura MZ no início de muitos executáveis do Windows. ?? casa com qualquer byte, [2-4] aceita um intervalo de dois a quatro bytes desconhecidos, e ( 90 | 91 ) casa com um ou com outro.
Expressões regulares ficam entre barras:
$versao = /LAB-[A-Z]+ v[0-9]+/Regex é o tipo mais flexível e também o mais fácil de deixar amplo demais. Use uma string de texto ou hexadecimal sempre que ela der conta do padrão.
A condição é uma expressão booleana. Ela combina strings e propriedades do arquivo em uma resposta só: casa ou não casa.
$marcador é verdadeiro quando a string aparece pelo menos uma vez.any of them e all of them valem para todas as strings da regra. 2 of ($a, $b, $c) exige duas de três.$marcador at 0 exige a string no primeiro byte. $marcador in (0..512) exige que ela esteja nos primeiros 512 bytes.#marcador > 3 conta as ocorrências.filesize < 2KB verifica o tamanho do arquivo.uint16(0) == 0x5A4D lê os dois primeiros bytes como um inteiro little-endian e confere a assinatura MZ.Toda regra precisa de uma condição. Uma regra com strings e sem condição sensata está quebrada ou não serve para nada.
-s, o YARA também imprime cada string encontrada, com o deslocamento em bytes onde ela apareceu.As strings vêm primeiro e a condição depois. Uma condição como $a and $b é estrita, porque as duas strings precisam ser encontradas antes de a regra casar.
A saída esperada abaixo é descrita, não colada, porque o formato exato pode variar entre versões do YARA. Compare o que aparecer no seu terminal com essas descrições.
Trabalhe em uma pasta vazia. No Windows, rode os comandos no Git Bash. O heredoc do passo 3 não funciona da mesma forma no PowerShell.
No Debian e no Ubuntu:
sudo apt install yaraNo macOS, com Homebrew:
brew install yaraNo Windows, baixe o zip da release em https://github.com/VirusTotal/yara/releases, extraia e coloque o executável no seu PATH. O arquivo pode se chamar yara64.exe. Renomeie para yara.exe, ou copie com esse nome, para que os comandos abaixo funcionem como estão.
Confira a instalação:
yara --versionVocê deve ver um número de versão. Este artigo não fixa uma versão, então use a release mais recente que o seu método de instalação entregar.
mkdir yara-lab && cd yara-lab
printf 'LAB-TRAINING-MARKER v1\n' > match.txt
printf 'arquivo de texto comum\n' > nomatch.txtOs dois arquivos são texto puro que você escreveu. O primeiro deve casar com a regra. O segundo não deve.
As aspas em volta de EOF importam. Sem elas, o shell troca $m por uma variável vazia antes de o YARA ler o arquivo, e a regra deixa de compilar.
cat > lab.yar <<'EOF'
rule Lab_Training_Marker {
meta: description = "Casa com o marcador do laboratório"
strings: $m = "LAB-TRAINING-MARKER"
condition: $m
}
EOFLeia em três partes. A meta diz para que serve a regra. A string diz que o arquivo precisa conter o texto do marcador. A condição diz que o arquivo casa quando essa string é encontrada pelo menos uma vez.
yara -s lab.yar match.txtO YARA deve imprimir o nome da regra e o caminho do arquivo. Por causa do -s, ele também imprime uma linha para a string encontrada, com o deslocamento em bytes e o texto achado. O marcador começa no primeiro byte de match.txt, então o deslocamento deve ser 0x0.
yara lab.yar nomatch.txtSaída esperada: nada. O YARA imprime apenas as correspondências, então o silêncio significa que o arquivo não casou.
O silêncio só diz alguma coisa se o teste positivo funcionou. Rode o passo 4 primeiro. Se o passo 4 não imprimiu nada, a regra ou a instalação está errada, e o silêncio do passo 5 não prova nada.
Um quase-casamento é o arquivo negativo mais útil, porque ele falha por um motivo específico e estreito.
printf 'LAB-TRAINING\n' > near.txt
yara lab.yar near.txtSaída esperada: nada. O arquivo tem o começo do marcador, mas não a string completa, então a string não é encontrada e a condição é falsa.
yara -r lab.yar .A flag -r varre a pasta recursivamente. Espere ver match.txt na saída. Provavelmente você também verá lab.yar, o próprio arquivo de regra. Ele contém o texto do marcador, então a regra casa com o próprio código-fonte. Esse é um erro comum, e a próxima seção trata dele. Mantenha as regras em uma pasta diferente dos dados que você varre.
A regra do laboratório serve para um marcador de treino. Regras reais costumam precisar de uma restrição que a string sozinha não dá. Suponha que você queira apenas arquivos pequenos que carreguem o marcador:
rule Lab_Training_Marker_Pequeno {
strings: $m = "LAB-TRAINING-MARKER"
condition: $m and filesize < 1KB
}Teste do mesmo jeito. match.txt deve casar. nomatch.txt e near.txt não devem. Toda mudança nas strings ou na condição exige rodar os dois arquivos de teste de novo.
Esta regra parece razoável:
rule Muito_Amplo {
strings: $a = "texto"
condition: $a
}Ela vai casar com nomatch.txt, porque a frase arquivo de texto comum contém a palavra texto. Você pode confirmar com o arquivo negativo que já criou. Uma regra que casa com ele falhou no próprio teste.
Padrões que casam demais:
texto, erro, copyright ou este programa.{ 00 } ou { 90 }.nocase em uma palavra comum.any of them sobre uma lista de strings que aparecem em arquivos comuns.filesize < 10KB, que casa com a maioria dos arquivos pequenos./.+/.Formas de apertar a regra:
and, ou com 2 of (...), para que nenhuma string comum decida sozinha.at ou in quando souber onde a string deve aparecer.uint16(0) == 0x5A4D.$m antes de o YARA ler o arquivo. Veja o passo 3.Esta é a regra que mais importa neste artigo. Toda regra YARA precisa dos dois tipos de arquivo de teste antes de alguém implantá-la.
match.txt. No trabalho real, vêm de amostras que você está autorizado a guardar, seguindo as regras de manuseio da sua organização.near.txt.Uma regra passa quando casa com todos os arquivos positivos e com nenhum negativo. Guarde os dois conjuntos ao lado da regra, no controle de versão se sua equipe usar um, e rode de novo a cada edição. Uma regra que passou no teste positivo, mas nunca foi rodada contra negativos, não foi testada.
pe, que permite a uma regra testar cabeçalhos e seções de um executável do Windows em vez de bytes crus.Malware e forense
Como hashes identificam arquivos, por que o SHA-256 substituiu o MD5, como o NSRL filtra softwares conhecidos e o risco de enviar arquivos ao VirusTotal.