Este artigo está disponível em Inglês na PenTest Magazine e também no Pulse (LinkedIn).

Motivação

Software, software, software… À medida que o tempo passa, a impressão que se tem é que o software irá governar quase cada aspecto dos negócios.  Tem sido uma parte fundamental em muitas indústrias, empresas de varejo e de serviços por muitos anos.  No entanto, hoje em dia (e esta é uma tendência inevitável), muitos negócios iniciam do zero baseados unicamente em software e nada mais.  Sem dúvida, estamos vivendo uma era definida por software.

Projetos de hardware como o Arduino e o Raspberry Pi, para citar alguns exemplos, nos trouxeram novas perspectivas e pontos de vida sobre como as coisas podem ajudar a humanidade a atingir suas metas, automatizar tarefas repetitivas e tomar controle da vida de uma forma ampla e agradável.  Por “coisas” aqui, estou me referindo a dispositivos reduzidos com capacidade de executar o software que você precisar.  E de novo, vemos o software ao nosso redor.  Incrível, não?

Portanto, com o software estando virtualmente em todo lugar, um aspecto inerente dele deve sempre ser levado em consideração:  bugs!  Com os mesmos, o código pode apresentar falhas, comportamento inesperado (como escalação de privilégio) e talvez exposição indesejada de alguma informação.

Tentei encontrar uma estatística consolidada relacionada ao uso crescente de software, mas tenho a impressão de que não existe tal coisa.  Em seu lugar, é fácil encontrar estatísticas separadas relacionadas ao uso crescente de CRM, produtos de Ciência de Dados, IoT, e assim por diante.

 

Introdução

Vamos iniciar mais um artigo.  Como sempre, estou feliz por compartilhar conhecimento com você.  Desta vez, estamos tratando de bugs.  Eles podem nos apresentar algumas oportunidades para explorar software que escolhemos como nosso alvo.  A exploração de software através de bugs é possível através de uma boa quantidade de formas, mas existe uma em particular que é bem curiosa (e efetiva):  fuzzing.

Através dos anos, o teste de software ganhou importância como uma metodologia para eliminar, ou pelo menos reduzir, a quantidade de falhas no código.  Está melhorando sua abordagem para ajudar os desenvolvedores e times de DevOps a alcançar com sucesso os critérios de testes.  Um bom exemplo das novas plataformas é o MAVEN, especialmente focado me projetos Java.

Um bom teste de penetração usa muitas técnicas e ferramentas para tentar uma resposta efetiva em uma ou mais delas.

De qualquer forma, não estamos aqui para falar de testes. 🙂 Estamos aqui para explorar software.  E uma forma efetiva de alcançar isso, como já mencionei, é através de fuzzing.  Esta técnica não é muito nova e consiste em gerar entrada aleatória para um programa.  Depois disso, o software testado é monitorado para detectar possíveis falhas ou má interpretação da entrada.  Esses erros eventuais podem gerar comportamentos imprevistos.  O sucesso é baseado não apenas na geração de qualquer entrada, mas entrada que seja válida o suficiente para ser aceita pelo código.  Quanto mais restrita a entrada (em termos de privilégio), mais atrativa será para o propósito de fuzzing.  Por exemplo, algum campo de texto que pode ser preenchido por qualquer usuário apresenta um cenário de teste melhor do que uma função interna que só é executada sob privilégios de super-usuário.

Neste artigo, introduzirei o AFL (American Fuzzy Loop), outra ferramenta de código aberto que pode nos ajudar em nossas atividades diárias de pentest.  O AFL é maduro o suficiente para atacar códigos que foram escritos em algumas linguagens de programação importantes e emprega muitas técnicas efetivas.  Vamos ver isso, com exemplos.

 

AFL e suas Aplicações

Um bom teste de penetração usa muitas técnicas e ferramentas para tentar uma resposta efetiva em uma ou mais delas.  Fuzzing é, com certeza, uma que deve ser considerada e você deveria testar o AFL.  Este produto apresenta muitos recursos interessantes, como velocidade, simplicidade de uso, conceitos e metodologias inteligentes.  A página do produto diz que ele foi o responsável por descobrir muitos bugs e falhas de código em um vasto conjunto de programas.

Ele é acompanhado de um programa drop-in do gcc.  Este código (afl-gcc) pode ser usado para inserir código proposital dentro do seu próprio código para tornar o procedimento de fuzzing mais fácil.  Portanto, em vez de compilar o seu código com o gcc (ou g++) tradicionais, o documento README mostra um procedimento simples para criar variáveis de ambiente com o intuito de apontar para seus códigos drop-in do AFL.  Assim, seus programas estarão prontos para serem testados com ele.

O AFL tem apenas três parâmetros obrigatórios:  o diretório de caso de teste (onde o software irá ler os arquivos de caso de teste e usá-los como entrada para o código a ser testado), o diretório de saída, onde o AFL armazenará quaisquer descobertas de vulnerabilidade e o próprio arquivo binário a ser testado.

Casos de teste são nada mais que possíveis entradas válidas que o código aceitaria.  No código que usaremos como exemplo mais tarde, eles podem ser alguns arquivos contendo uma string (correspondendo ao nome) e um número inteiro (correspondendo à idade).  Explicarei um pouco mais à frente.

Outras opções possíveis são:

  • -f <file>: Arquivos de entrada para o código testado;
  • -t <msec>: Um timeout entrada cada loop de execução (default é 20 ms);
  • -m <MB>: O limite de memória RAM (em MB) para ser usado em cada processo filho do AFL;
  • -Q: A possibilidade de executar o QEMU como base da instrumentação binária;
  • -d: Uma opção para pular a verificação determinística default;
  • -n: Modo dumb (sem necessidade de instrumentação);
  • -x <diretório>: O diretório de dicionários.  São úteis quando o teste que você quer executar deve ser mais específico que as abordagens padrão usadas pelo AFL;
  • -T <texto>: A personalização da tela de banner;
  • -M / -S <id>: Uma opção para distribuir o processamento ao longo de muitas máquinas.  Bem útil quando a tarefa de teste é grande demais para um único núcleo de CPU ou mesmo uma máquina inteira;
  • -C: A possibilidade de rodar em crash mode (Peruvian rabbit).

Claro que cada opção pode ou não ser útil para seu código (o que você mesmo desenvolveu ou ainda um que você gostaria de testar).  De qualquer maneira, é importante que você as entenda e escolha uma eventual opção que possa tornar seu trabalho mais simples.

Você pode escolher entre testar o AFL contra um código já feito (algum programa popular ou mesmo o seu próprio) ou instrumentar o código fonte e aplicar o AFL da melhor maneira possível.  Isto será explicado em breve.

 

Instalação

Instalar o AFL é tão simples quanto compilar um código pequeno, sem erros.  Você começa baixando a tarball mais recente aqui.  Normalmente, é um pacote pequeno:

$ ls -l afl*
-rw-rw-r--  1 mauricio mauricio 832234 Jun  4 00:12 afl-latest.tgz

Depois, simplesmente descompacte, rode o tar para desempacotar e o “make” para disparar a compilação.  Será algo mais ou menos assim:

~/afl-2.42b$ make
[*] Checking for the ability to compile x86 code...
[+] Everything seems to be working, ready to compile.
cc -O3 -funroll-loops -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -DAFL_PATH=\"/usr/local/lib/afl\" -DDOC_PATH=\"/usr/local/share/doc/afl\" -DBIN_PATH=\"/usr/local/bin\" afl-gcc.c -o afl-gcc -ldl
set -e; for i in afl-g++ afl-clang afl-clang++; do ln -sf afl-gcc $i; done
cc -O3 -funroll-loops -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -DAFL_PATH=\"/usr/local/lib/afl\" -DDOC_PATH=\"/usr/local/share/doc/afl\" -DBIN_PATH=\"/usr/local/bin\" afl-fuzz.c -o afl-fuzz -ldl
cc -O3 -funroll-loops -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -DAFL_PATH=\"/usr/local/lib/afl\" -DDOC_PATH=\"/usr/local/share/doc/afl\" -DBIN_PATH=\"/usr/local/bin\" afl-showmap.c -o afl-showmap -ldl
cc -O3 -funroll-loops -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -DAFL_PATH=\"/usr/local/lib/afl\" -DDOC_PATH=\"/usr/local/share/doc/afl\" -DBIN_PATH=\"/usr/local/bin\" afl-tmin.c -o afl-tmin -ldl
cc -O3 -funroll-loops -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -DAFL_PATH=\"/usr/local/lib/afl\" -DDOC_PATH=\"/usr/local/share/doc/afl\" -DBIN_PATH=\"/usr/local/bin\" afl-gotcpu.c -o afl-gotcpu -ldl
cc -O3 -funroll-loops -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -DAFL_PATH=\"/usr/local/lib/afl\" -DDOC_PATH=\"/usr/local/share/doc/afl\" -DBIN_PATH=\"/usr/local/bin\" afl-analyze.c -o afl-analyze -ldl
cc -O3 -funroll-loops -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -DAFL_PATH=\"/usr/local/lib/afl\" -DDOC_PATH=\"/usr/local/share/doc/afl\" -DBIN_PATH=\"/usr/local/bin\" afl-as.c -o afl-as -ldl
ln -sf afl-as as
[*] Testing the CC wrapper and instrumentation output...
unset AFL_USE_ASAN AFL_USE_MSAN; AFL_QUIET=1 AFL_INST_RATIO=100 AFL_PATH=. ./afl-gcc -O3 -funroll-loops -Wall -D_FORTIFY_SOURCE=2 -g -Wno-pointer-sign -DAFL_PATH=\"/usr/local/lib/afl\" -DDOC_PATH=\"/usr/local/share/doc/afl\" -DBIN_PATH=\"/usr/local/bin\" test-instr.c -o test-instr -ldl
echo 0 | ./afl-showmap -m none -q -o .test-instr0 ./test-instr
echo 1 | ./afl-showmap -m none -q -o .test-instr1 ./test-instr
[+] All right, the instrumentation seems to be working!
[+] All done! Be sure to review README - it's pretty short and useful.

Após isso, você receberá um conjunto de arquivos binários para brincar.  Focaremos apenas em dois deles:  afl-fuzz e afl-gcc.  O primeiro é o próprio fuzzer e o segundo é o compilador C drop-in do gcc.

 

Os Testes

Durante a escrita deste artigo, rodei o AFL com alguns binários comuns do Linux, mas sem sucesso.  Felizmente! 🙂 Se sua distribuição está atualizada, dificilmente encontrará binários exploráveis pelo AFL.  Na verdade, conforme citado, o AFL é responsável pela descoberta de muitos bugs em códigos famosos.  Acesse o site da ferramenta para saber mais.

Em vez disso, vamos construir nosso próprio código e rodar os testes contra ele. No primeiro teste, compilaremos normalmente como qualquer programa em C e passaremos o AFL no modo dumb.  No segundo teste, compilaremos o código usando o afl-gcc e rodaremos novamente os testes (sem o modo dumb).  A diferença entre ambas as tarefas é a instrumentação.  Isso significa que o AFL irá inserir código especial para ajudar nos testes.  Em modo dumb, o AFL não possui quaisquer instrumentos para testar seu código e tentará, no lugar, fazer testes baseados em “força bruta”.  Isso pode levar a resultados não confiáveis e deve ser evitado o máximo possível.

Eis o código que usaremos como fonte em nossos testes (test.c):

/*
Este código aceita apenas pessoas cujos nomes tenham comprimento menor ou igual a 10 e cujas idades sejam menores ou iguais a 30 anos.
*/
 
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
 
int main() {
 
      char name[30], age[2];
      bool nameok, ageok;
 
      nameok = ageok = false;
 
      printf("Please, input your name:  ");
      gets(name);
 
      printf("Please, input your age:  ");
      gets(age);
 
      // Checando se o nome e a idade atendem aos requisitos.
      if (strlen(name) <= 10) {
            nameok = true;
            if (atoi(age) <= 30) {
                  ageok = true;
                  printf("\nYou're good to go!\n");
                  return 0;
            }
      }
 
      printf("\nRequirements were not met!\n");
      return -1;
}

Este código recebe duas entradas do usuário (via teclado):  um nome e uma idade.  Então, ele realiza comparações:  a primeira é para verificar se o comprimento do nome é menor ou igual a 10 caracteres e a segunda verificará se a idade é menor ou igual a 30 anos.  Eu sei; é um código completamente sem sentido.  Mas, servirá para nosso artigo.  É importante entender que este código tem três possíveis caminhos (formas de terminar):

  1. Nenhuma das condições será atendida (ambas falsas), “… not met” será mostrado e o código retornará -1;
  2. Apenas a primeira condição é verdadeira, significando que apenas a primeira variável booleana será verdadeira, mas “… not met” será mostrado e o código retornará -1 também;
  3. Ambas as condições são verdadeiras:  o que significa que “… good to go” será mostrado e o programa retornará 0.

O próximo passo é construir os arquivos de casos de teste.  Criei um subdiretório chamado “cases” e coloquei três arquivos texto nele:  case1, case2 e case3.  Cada arquivo corresponde a um número de caso respectivo.  Como eu gosto muito da trilogia “Senhor dos Anéis”, isto serviu como inspiração.  O primeiro foi preenchido com “Frodo Baggins” e 35.  O segundo foi preenchido com “Gandalf” e 99 (sabemos que magos são extremamente velhos) e o último foi preenchido com “Aragorn” e 30 (só desta vez, finjamos que o rei é um jovem adulto).  Finalmente, um diretório “outputs” foi criado para armazenar os resultados.

A compilação do código gerou a saída a seguir o arquivo de teste binário:

$ gcc teste.c –o test
test.c: In function ‘main’:
test.c:18:2: warning: implicit declaration of function ‘gets’ [-Wimplicit-function-declaration]
  gets(name);
  ^
test.c:26:7: warning: implicit declaration of function ‘atoi’ [-Wimplicit-function-declaration]
   if (atoi(age) <= 30) {
       ^
/tmp/ccB7nSSi.o: In function `main':
test.c:(.text+0x3e): warning: the `gets' function is dangerous and should not be used.

Perceba que, por default, esta versão do gcc já avisa quando você usa funções inseguras ou perigosas (estou usando o gcc 5.4.0 em um Ubuntu 16.04 LTS).  Como são apenas avisos, o arquivo binário é criado no final.  Observe que eu não compilei o código desabilitando qualquer característica específica; isto também pode ser feito para melhorar a resposta do AFL.  Para realizar o primeiro teste, simplesmente rode:

$ ./afl-fuzz –i ./cases/ -o ./outputs/ ./test

Como não implementei instrumentação, o AFL não sabe como manipular o código.  Assim, recebi esta mensagem de erro:

[-] PROGRAM ABORT : No instrumentation detected
         Location : check_binary(), afl-fuzz.c:6894

Assim, para usar desta forma, tive de ativar o modo dumb, ou seja, a opção “-n”:

$ ./afl-fuzz –n –i ./cases/ -o ./outputs/ ./test

A ferramenta então mostra uma tela com algumas variáveis interessantes.  Basicamente, você deve se concentrar em duas delas:  “paths” e “crashes”.  Como falei há pouco, “paths” são relacionados a quantos caminhos possíveis de execução o programa tem.  Como o código só possui duas instruções condicionais aninhadas, só há três formas possíveis de terminar.  A variável “crashes” representa a quantidade de falhas descobertas pelo AFL.  Quando a ferramenta alcança a quantidade máxima de paths, você não precisa mais esperar pela tarefa de fuzzing.  Finalize pressionando CTRL+C.  Observe as linhas “total paths” e “uniq crashes” abaixo:

american fuzzy lop 2.42b (test)
 
┌─ process timing ─────────────────────────────────────┬─ overall results ─────┐
│        run time : 0 days, 0 hrs, 0 min, 20 sec       │  cycles done : 17     │
│   last new path : n/a (non-instrumented mode)        │  total paths : 3      │
│ last uniq crash : 0 days, 0 hrs, 0 min, 0 sec        │ uniq crashes : 1303   │
│  last uniq hang : none seen yet                      │   uniq hangs : 0      │
├─ cycle progress ────────────────────┬─ map coverage ─┴───────────────────────┤
│  now processing : 2* (66.67%)       │    map density : 0.00% / 0.00%         │
│ paths timed out : 0 (0.00%)         │ count coverage : 0.00 bits/tuple       │
├─ stage progress ────────────────────┼─ findings in depth ────────────────────┤
│  now trying : splice 10             │ favored paths : 0 (0.00%)              │
│ stage execs : 31/32 (96.88%)        │  new edges on : 0 (0.00%)              │
│ total execs : 46.4k                 │ total crashes : 1303 (1303 unique)     │
│  exec speed : 2230/sec              │  total tmouts : 0 (0 unique)           │
├─ fuzzing strategy yields ───────────┴───────────────┬─ path geometry ────────┤
│   bit flips : 0/312, 0/309, 0/303                   │    levels : 1          │
│  byte flips : 0/39, 0/36, 0/30                      │   pending : 0          │
│ arithmetics : 0/2177, 0/228, 0/0                    │  pend fav : 0          │
│  known ints : 0/214, 0/1002, 0/1320                 │ own finds : 0          │
│  dictionary : 0/0, 0/0, 0/0                         │  imported : n/a        │
│       havoc : 512/16.1k, 787/24.3k                  │ stability : n/a        │
│        trim : n/a, 0.00%                            ├────────────────────────┘
^C────────────────────────────────────────────────────┘          [cpu000:133%]

Ao observar o diretório “outputs”, você encontrará um número grande de arquivos.  Cada um demonstra entradas que foram testadas contra o código e os erros (crashes) gerados:

outputs$ ls crashes/
id:000000,sig:06,src:000000,op:havoc,rep:4            id:000659,sig:06,src:000002+000001,op:splice,rep:64
id:000001,sig:06,src:000000,op:havoc,rep:32           id:000660,sig:06,src:000002+000001,op:splice,rep:32
id:000002,sig:06,src:000000,op:havoc,rep:128          id:000661,sig:06,src:000002+000000,op:splice,rep:64
id:000003,sig:06,src:000000,op:havoc,rep:64           id:000662,sig:06,src:000002+000000,op:splice,rep:64
id:000004,sig:06,src:000000,op:havoc,rep:64           id:000663,sig:06,src:000002+000000,op:splice,rep:2
id:000005,sig:06,src:000000,op:havoc,rep:64           id:000664,sig:06,src:000000,op:havoc,rep:128
id:000006,sig:06,src:000000,op:havoc,rep:8            id:000665,sig:06,src:000000,op:havoc,rep:64
id:000007,sig:06,src:000000,op:havoc,rep:32           id:000666,sig:06,src:000000,op:havoc,rep:16
id:000008,sig:06,src:000000,op:havoc,rep:32           id:000667,sig:06,src:000000,op:havoc,rep:128
id:000009,sig:06,src:000000,op:havoc,rep:128          id:000668,sig:06,src:000000,op:havoc,rep:8
<saída omitida>

Se você escolher qualquer uma deles e usá-lo como parâmetro de entrada para o código, algum erro será disparado.  Isto pode ser facilmente verificado:

outputs$ cat crashes/id:000664,sig:06,src:000000,op:havoc,rep:128 | ../test
*** stack smashing detected ***: ../test terminated
Please, input your name:  Please, input your age:  You're good to go!Aborted (core dumped)

Você encontrará um resultado semelhante para qualquer arquivo crash que usar.  Agora, vamos compilar o código usando o afl-gcc e ver o que acontece.  Como disse anteriormente, isto irá inserir a instrumentação no código:

$ ./afl-gcc ./test.c –o ./test2

Os resultados são equivalentes, mas bem mais efetivos.  Preste atenção à quantidade reduzida de crashes únicos que foram encontrados.  Ainda assim, o número de paths continua o mesmo (como esperado):

american fuzzy lop 2.42b (test2)
 
┌─ process timing ─────────────────────────────────────┬─ overall results ─────┐
│        run time : 0 days, 0 hrs, 0 min, 14 sec       │  cycles done : 14     │
│   last new path : 0 days, 0 hrs, 0 min, 13 sec       │  total paths : 3      │
│ last uniq crash : 0 days, 0 hrs, 0 min, 12 sec       │ uniq crashes : 4      │
│  last uniq hang : none seen yet                      │   uniq hangs : 0      │
├─ cycle progress ────────────────────┬─ map coverage ─┴───────────────────────┤
│  now processing : 5* (83.33%)       │    map density : 0.01% / 0.02%         │
│ paths timed out : 0 (0.00%)         │ count coverage : 1.40 bits/tuple       │
├─ stage progress ────────────────────┼─ findings in depth ────────────────────┤
│  now trying : havoc                 │ favored paths : 3 (50.00%)             │
│ stage execs : 224/256 (87.50%)      │  new edges on : 3 (50.00%)             │
│ total execs : 79.8k                 │ total crashes : 3338 (4 unique)        │
│  exec speed : 5619/sec              │  total tmouts : 0 (0 unique)           │
├─ fuzzing strategy yields ───────────┴───────────────┬─ path geometry ────────┤
│   bit flips : 0/664, 0/658, 0/646                   │    levels : 2          │
│  byte flips : 0/83, 0/77, 0/65                      │   pending : 0          │
│ arithmetics : 0/4636, 0/1790, 0/0                   │  pend fav : 0          │
│  known ints : 1/424, 0/2114, 0/2857                 │ own finds : 3          │
│  dictionary : 0/0, 0/0, 0/0                         │  imported : n/a        │
│       havoc : 6/30.5k, 0/35.0k                      │ stability : 100.00%    │
│        trim : 31.40%/26, 0.00%                      ├────────────────────────┘
^C────────────────────────────────────────────────────┘          [cpu000:198%]

Uma olhada rápida no diretório “outputs” nos mostra os resultados e, de novo, pegue qualquer um dos arquivos e use-o contra o código para provocar uma falha:

$ ls outputs/crashes/
id:000000,sig:11,src:000000,op:havoc,rep:128  id:000002,sig:11,src:000000,op:havoc,rep:128
id:000001,sig:11,src:000000,op:havoc,rep:16   id:000003,sig:11,src:000001,op:havoc,rep:16
$
$ cat outputs/crashes/id:000002,sig:11,src:000000,op:havoc,rep:128 | ./test2
Please, input your name:  Please, input your age:  You're good to go!
Segmentation fault

 

Corrigindo os Bugs

O código que desenvolvemos neste artigo tem funções sabidamente vulneráveis.  O próprio compilador nos mostrou isso.  Assim, para fixar este comportamento tendencioso a falhas, podemos substituir o “gets” por uma função mais segura, como o “fgets”, onde é possível limitar a quantidade de caracteres considerados ao ler algo do stdin (teclado).

Assim, nosso código corrigido se parece com isto (ligeiramente diferente do anterior):

/*
Este código aceita apenas pessoas cujos nomes tenham comprimento menor ou igual a 10 e cujas idades sejam menores ou iguais a 30 anos.  *** VERSÃO CORRIGIDA ***
*/
 
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
 
int main() {
 
      char name[30], age[2];
      bool nameok, ageok;
 
      nameok = ageok = false;
 
      printf("Please, input your name:  ");
      fgets(name, 30, stdin);
 
      printf("Please, input your age:  ");
      fgets(age, 2, stdin);
 
      // Checando se o nome e a idade atendem aos requisitos.
      if (strlen(name) <= 10) {
            nameok = true;
            if (atoi(age) <= 30) {
                  ageok = true;
                  printf("\nYou're good to go!\n");
                  return 0;
            }
      }
 
      printf("\nRequirements were not met!\n");
      return -1;
}

Ao compilar com o gcc convencional, não obtivemos qualquer erro.  Quando instrumentamos o código com o afl-gcc, a ferramenta nos mostra um aviso simples sobre o fgets (nada demais):

afl-cc 2.42b by <lcamtuf@google.com>
test-fix.c: In function ‘main’:
test-fix.c:19:2: warning: ignoring return value of ‘fgets’, declared with attribute warn_unused_result [-Wunused-result]
  fgets(name, 30, stdin);
  ^
test-fix.c:22:2: warning: ignoring return value of ‘fgets’, declared with attribute warn_unused_result [-Wunused-result]
  fgets(age, 2, stdin);
  ^
afl-as 2.42b by <lcamtuf@google.com>
[+] Instrumented 9 locations (64-bit, non-hardened mode, ratio 100%).

O diretório “outputs” está quase vazio.  Nenhum crash foi encontrado (único ou não) após esta pequena correção:

~/afl-2.42b$ ls -lR outputs/
outputs/:
total 84
drwx------ 2 mauricio mauricio  4096 Jun  9 15:00 crashes
-rw------- 1 mauricio mauricio 65536 Jun  9 15:01 fuzz_bitmap
-rw------- 1 mauricio mauricio   712 Jun  9 15:06 fuzzer_stats
drwx------ 2 mauricio mauricio  4096 Jun  9 15:00 hangs
-rw------- 1 mauricio mauricio  3944 Jun  9 15:06 plot_data
drwx------ 3 mauricio mauricio  4096 Jun  9 15:00 queue
 
outputs/crashes:
total 0
outputs/hangs:
total 0
outputs/queue:
total 20
-rw------- 1 mauricio mauricio 12 Jun  9 15:00 id:000000,orig:case1
-rw------- 1 mauricio mauricio  8 Jun  9 15:00 id:000001,orig:case2
-rw------- 1 mauricio mauricio  8 Jun  9 15:00 id:000002,orig:case3
-rw------- 1 mauricio mauricio  8 Jun  9 15:00 id:000003,src:000000,op:flip1,pos:5
-rw------- 1 mauricio mauricio 16 Jun  9 15:00 id:000004,src:000000,op:havoc,rep:128

Nosso teste final não resulta em nenhuma falha:

american fuzzy lop 2.42b (test-fix)
 
┌─ process timing ─────────────────────────────────────┬─ overall results ─────┐
│        run time : 0 days, 0 hrs, 6 min, 3 sec        │  cycles done : 576    │
│   last new path : 0 days, 0 hrs, 6 min, 3 sec        │  total paths : 3      │
│ last uniq crash : none seen yet                      │ uniq crashes : 0      │
│  last uniq hang : none seen yet                      │   uniq hangs : 0      │
├─ cycle progress ────────────────────┬─ map coverage ─┴───────────────────────┤
│  now processing : 1* (20.00%)       │    map density : 0.01% / 0.02%         │
│ paths timed out : 0 (0.00%)         │ count coverage : 1.30 bits/tuple       │
├─ stage progress ────────────────────┼─ findings in depth ────────────────────┤
│  now trying : havoc                 │ favored paths : 2 (40.00%)             │
│ stage execs : 218/256 (85.16%)      │  new edges on : 2 (40.00%)             │
│ total execs : 1.99M                 │ total crashes : 0 (0 unique)           │
│  exec speed : 5473/sec              │  total tmouts : 0 (0 unique)           │
├─ fuzzing strategy yields ───────────┴───────────────┬─ path geometry ────────┤
│   bit flips : 1/416, 0/411, 0/401                   │    levels : 2          │
│  byte flips : 0/52, 0/47, 0/37                      │   pending : 0          │
│ arithmetics : 0/2901, 0/639, 0/12                   │  pend fav : 0          │
│  known ints : 0/274, 0/1242, 0/1620                 │ own finds : 2          │
│  dictionary : 0/0, 0/0, 0/0                         │  imported : n/a        │
│       havoc : 1/742k, 0/1.24M                       │ stability : 100.00%    │
│        trim : 30.67%/15, 0.00%                      ├────────────────────────┘
^C────────────────────────────────────────────────────┘          [cpu000:194%]
 
+++ Testing aborted by user +++
[+] We're done here. Have a nice day!

 

Palavras Finais

Fuzzing em software é uma forma bem curiosa, efetiva e fácil de fazer exploração em código.  O fuzzing manual pode ser assustador, é claro, but com a ajuda de ferramentas como o AFL, o trabalho pode ser bastante reduzido.  Seus principais recursos, como dicionários, podem ser usados em conjunto com outras opções básicas para melhorar a efetividade da resposta e tentar alinhar os resultados com suas necessidades.  Seria bom, no entanto, se o AFL pudesse fazer a mesma instrumentação com código interpretado (como Python).  Como muitos programas e utilitários são baseados nesta linguagem, este recurso ajudaria bastante.

Compartilhe:
Categorias: Segurança

Maurício Harley

Olá! Meu nome é Maurício Harley. Tenho mais de 20 anos de experiência em Tecnologia da Informação. Durante minha carreira, trabalhei em setores diversos, como suporte a usuário final, manutenção de hardware, instalação e suporte a redes de computadores, programação, projetos avançados em Data Center, Cloud Computing, Cyber Security e Redes, incluindo Service Providers.

1 comentário

Dissecando Malware com o MobSF | Blog itHarley · 2018-02-20 às 17:07

[…] pode sofrer fuzzing usando o botão correspondente. Se você não sabe o que é fuzzing, verifique este artigo.  No entanto, como pude observar diversas vezes usando o emulador do OVA, nenhuma aplicação […]

Deixe um comentário

Avatar placeholder

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Translate

You cannot copy content of this page.