Introdução
A comunidade de segurança da informação já está bastante acostumada com a palavra fuzzing. Para aqueles que ainda não conhecem, fuzzing é uma técnica de teste tendo como objetivo a descoberta de vulnerabilidades. Existem diversas ferramentas (fuzzers) disponíveis que implementam esta técnica. Neste post é apresentado alternativas Ruby-like para a realização de fuzzing, são elas: Ruckus[1] e DFuzz[2]. Primeiramente é apresentado o Ruckus, os principais tipos de dados que o mesmo é capaz de representar e como é possível através dele modelar um formato de dados. Em seguida é mostrado como o DFuzz pode ser utilizado para a realização de fuzzing. Por fim, é feita a combinação do Ruckus com o DFuzz para demonstrar que a dupla pode ser usada na construção de um smart fuzzer.
Ruckus
Ruckus é um smart fuzzer escrito em Ruby. Considera-se smart fuzzer as ferramentas que fazem fuzzing levando em consideração o tipo dos dados que estão sendo testados [3]. Com o Ruckus pode-se fazer a modelagem de formatos de dados (formato de arquivo ou protocolo) utilizando a própria linguagem Ruby para isto. Para começar a modelar uma estrutura basta criar uma nova classe herdando de Ruckus::Structure, conforme visto na Figura 1.
Tipos
Uma vez feito isto, é hora de representar os campos que o formato a ser testado contém. No Ruckus os principais tipos de dados disponíveis são: Number e Str.
Number, como o nome já diz, é usado para representar diversos tipos de inteiros. Para campos deste tipo é possível definir algumas características, conforme visto na tabela 1.
:width
|
Quantidade de bits do campo.
|
:endian
|
Representação em :big ou :little endian.
|
:ascii
|
Representação textual do campo (:true ou :false).
|
:radix
|
Definir base do número caso o campo seja textual.
|
Do tipo Number originaram-se outros tipos, são eles: byte, be16, be32, le16, le32, le64, be64, decimal, entre outros. Na Figura 2 pode-se ver um exemplo de definição de campos inteiros (be32 e be16).
Para um campo do tipo Str, também é possível definir características. Estas características podem ser vistas na tabela 2.
:size
|
Tamanho (absoluto) do campo (forçará padding se preciso).
|
:min
|
Tamanho mínimo do campo.
|
:max
|
Tamanho máximo do campo.
|
:padding
|
Caractere usado em caso de padding (padrão: ”)
|
:pad_to
|
Alinhamento do padding.
|
:unicode
|
Define se o campo deve ser unicode.
|
:nul_terminated
|
Define se o campo deve ser terminado com nulo.
|
Na Figura 3 pode-se observar um exemplo de uso do Str, neste exemplo é definido um campo chamado filename e setado seu valor padrão para “default.txt”.
Há outros campos interessantes que podem ser usados no Ruckus como: ipv4, bit e nibble. Com os tipos disponíveis é possível modelar diversos formatos de arquivos e protocolos.
Relação
O Ruckus é capaz de manter relações entre campos. Na definição do formato PseudoFileFormat existe o campo len que é um inteiro de 16 bits, big endian e o campo filename que contém uma string. Pode-se relacionar o campo len com o tamanho da string em filename. Toda vez que essa string for alterada este campo será automaticamente atualizado. Na Figura 4, pode-se ver a criação do relacionamento utilizando o método relate_value.
Resultado do PseudoFileFormat
Para visualização em hexdump o formato definido, foi utilizanda a gem Ruby BlackBag[4]. Pode-se observar na Figura 5 que o campo len é setado automaticamente para o tamanho da string filename. Na Figura 6 vemos que após a alteração de filename o campo len é atualizado para refletir a modificação.
format = PseudoFileFormat.new
puts format.to_s.hexdump
format.filename = “post.txt”
puts format.to_s.hexdump
DFuzz
Embora exista a capacidade de fazer fuzzing utilizando somente o código do próprio Ruckus, para este post foi escolhido o DFuzz porque o mesmo possui uma interface mais amigável para condução dos testes. A boa notícia é que o DFuzz já vem embutido no Ruckus. Mas afinal, o que é o DFuzz? DFuzz é um código em Ruby escrito pelo pesquisador Dino Dai Zovi que provê uma série de inputs que podem ser utilizados para se testar sistemas. Os inputs são gerados de acordo com o tipo de dado a ser testado. Os principais tipos suportados pelo DFuzz são: Integer, Byte, Short, Long e String. Na Figura 7 é possível ver um exemplo de uso do DFuzz. Utilizando DFuzz::String é possível encontrar falhas como buffer overflows, format strings, path traversal, entre outros.
PseudoFuzzer
Para este post foi escrito um código a fim de exemplificar como o Ruckus e o DFuzz podem ser utilizados em conjunto para criação de uma ferramenta de fuzzing. Na Figura 8 segue o que seria um fuzzer para o PseudoFileFormat. Neste exemplo, o teste focou-se apenas no campo filename, mas o código poderia ser estendido para testar os outros campos. Nem o Ruckus, nem DFuzz possuem um mecanismo para monitorar as exceções que podem ocorrer durante os testes. Para isto foi feita uma abstração na linha 27.
Conclusão
Utilizando o Ruckus é possível modelar um formato de dados e com o DFuzz pode-se obter inputs úteis para realização de fuzzing. Com a combinação destas duas ferramentas é possível a criação de smart fuzzers utilizando funcionalidades e facilidades que estão presentes na linguagem Ruby.
Referências
[1] https://github.com/tqbf/ruckus/
[2] https://github.com/tqbf/ruckus/blob/master/ruckus/dfuzz.rb
[3] http://peachfuzzer.com/SmartFuzzer
[4] https://github.com/emonti/rbkb
[5] http://www.matasano.com/research/ruby_for_pentesters/Ruby-For-Pentesters.pdf