[or-cvs] r11289: cleaned up the source and added some examples (in topf/trunk: . binary doc examples lib lib/fuzz utils)
benedikt at seul.org
benedikt at seul.org
Mon Aug 27 16:54:33 UTC 2007
Author: benedikt
Date: 2007-08-27 12:54:30 -0400 (Mon, 27 Aug 2007)
New Revision: 11289
Added:
topf/trunk/binary/argument_example.c
topf/trunk/examples/
topf/trunk/examples/argument_fuzz_example.rb
topf/trunk/examples/example1.rb
topf/trunk/lib/fuzz/
topf/trunk/lib/fuzz/collection.rb
topf/trunk/lib/fuzz/connection.rb
topf/trunk/lib/fuzz/core.rb
topf/trunk/lib/fuzz/helper.rb
topf/trunk/lib/fuzz/observer.rb
topf/trunk/lib/fuzz/test.rb
topf/trunk/lib/fuzz/tests.rb
topf/trunk/utils/set_core_pattern
Modified:
topf/trunk/README
topf/trunk/doc/tutorial.tex
topf/trunk/lib/fuzz.rb
topf/trunk/tor-control-fuzz.rb
topf/trunk/tor-dir-fuzz.rb
Log:
cleaned up the source and added some examples
Modified: topf/trunk/README
===================================================================
--- topf/trunk/README 2007-08-27 15:38:17 UTC (rev 11288)
+++ topf/trunk/README 2007-08-27 16:54:30 UTC (rev 11289)
@@ -1,3 +1,6 @@
+Pointer for Google: The newest version is always on the subversion repo at:
+https://tor-svn.freehaven.net/svn/topf/trunk
+
(T)he (O)nion (P)rotocol (F)uzzer
This is my Google Summer of Code Project where I try to build a
Added: topf/trunk/binary/argument_example.c
===================================================================
--- topf/trunk/binary/argument_example.c (rev 0)
+++ topf/trunk/binary/argument_example.c 2007-08-27 16:54:30 UTC (rev 11289)
@@ -0,0 +1,17 @@
+/*
+ * DANGER !!! THIS FILE IS VULNERABLE TO A VERY SIMPLE BUG :)
+ * DONT USE THIS IN PRODUCTIVE ENVIRONMENTS.. THIS IS ONLY AN EXAMPLE,
+ * TO USE WITH THE T.O.P.F FUZZER EXAMPLES
+ */
+void vuln(char *ptr)
+{
+ char buffer[20];
+ strcpy(&buffer, ptr);
+}
+
+int main(int argc, char *argv[])
+{
+ if(argc > 1)
+ vuln(argv[1]);
+ return 0;
+}
Modified: topf/trunk/doc/tutorial.tex
===================================================================
--- topf/trunk/doc/tutorial.tex 2007-08-27 15:38:17 UTC (rev 11288)
+++ topf/trunk/doc/tutorial.tex 2007-08-27 16:54:30 UTC (rev 11289)
@@ -12,6 +12,7 @@
\tableofcontents
\newpage
\section{Introduction}
+\label{working}
T.O.P.F is a fuzzing Framework written in Ruby and developed to test the
Tor protocol-suite. It uses a block-based approch like the famous SPIKE
fuzzer written by Dave-Aitel. Block-based means that data is divided into
@@ -22,14 +23,11 @@
you should read into the links listed in Appendix \ref{links}.
\section{Working with T.O.P.F}
-\label{working}
To use T.O.P.F a few basic steps described in this section are necessary.
\subsection{Setting up a working Environment}
-As my aim is to make the Installation of T.O.P.F as easy as possible,
-a working Ruby Interpreter and a checkout of the latest T.O.P.F
-trunk should be enough to setup up a working Test-Environment on most
-systems. If you have any problems or errors you are encouraged to email
-me to benedikt.boss (at) gmail (dot) com .
+T.O.P.F needs Ruby and the Ruby-SSL library which is on some systems supplied
+as standart-library and not on others. On Debian you need to run
+"apt-get install ruby libruby-openssl" beside the normal Ruby library.
\subsubsection{Checking out the current T.O.P.F trunk}
Checking out T.O.P.F is as simple as starting a
@@ -86,91 +84,59 @@
the tests against the Dir-Port. If you're sure you did all config options
right you can execute "ruby tor-control-fuzz.rb" and the rest is done
automatically.
-\section{Writing T.O.P.F Structures}
-As described in Section~\ref{working} (yeah? where? -RD), T.O.P.F
-organizes its data in blocks. These blocks can have different types
-which are described in detail in Section~\ref{types}. For example you
-can use char, signed, and unsigned types.
-
+\subsection{Writing tests for TOR}
+To give you a more tor-specific example of how such a test might look i give you
+an example out of control.rb that helds the structures to fuzz the Tor
+control-port. From the control-spec we take the "SIGNAL" keyword and check out
+the syntax of the command: \begin{varbatim}"SIGNAL" SP Signal CRLF\end{verbatim}
+If we now want to check if the parsing of the given signal has a bug we also
+want to set the initial "SIGNAL" keyword and the appended crlf field to non-fuzzable.
+Implementing this as a FuzzStruct could look like this:\\
\begin{verbatim}
-#example1.rb
-require "lib/fuzz-generic"
+class SignalItem < FuzzStruct
+ text :item, 6, :fuzzable => false
+ text :signal, 13
+ char :crlf, 2*8, :fuzzable => false
-class Example < FuzzStruct
- text :example, 7, :fuzzable => false
- unsigned :version, 8
-
- initial_value.example = "example"
- initial_value.version = 1
+ initial_value.item = "SIGNAL"
+ initial_value.crlf = "\r\n"
end
-
-begin
- e = Example.new
- e.version = 2
- pp e
-end
\end{verbatim}
-This creates a Class called "Example" with the fields of a 8*8Bit long String,
-a 8Bit unsigned integer. and the initial values "example" and 1 for these.
-Also the Text-Field is declared as not fuzzable which means that later the
-value assigned to it remains the same. Next in the begin/end block the
-programm creates a Example object and sets the value of the version field to 2.
-This also demonstrates how you are able to access all fields after you created
-a fuzz-struct object.
-
-
-\subsection{Writing T.O.P.F Tests}
-Tests in the Framework are organized on a field-type base. This means
-that you write tests for a specific field. To generate a Test you must
-create a Fuzz::Test object and assign a type and code-block to
-it. For example if you want to test a char-field and assign many many
-"A"'s to the Field, which is a very common test :), you could write
-something like this:
+The signal-field is chosen to be 13 bytes long as the longest signal-string
+("CLEARDNSCACHE") is 13 bytes long.\\
+Now we want to test the signal "RELOAD" so we create a SignalItem object and set
+signal to "RELOAD".
\begin{verbatim}
- a_test = Fuzz::Test.new("char") {|arg, size| "A"*1000}
+si = SignalItem.new
+si.signal = "RELOAD"
\end{verbatim}
-To apply this test to a fuzz-struct you actually need another object which
-acts as a collector for many tests. This object is called Fuzz::Tests and
-is later applied to a fuzz-struct. The next example shows how you write some
-tests, assign them to the collector object and apply all tests to a structure
-and output all permutations calculateable through these tests.
-
+Next we want to apply two tests for the signal-field, a very long string and
+a format-string.
\begin{verbatim}
-#example2.rb
-require "lib/fuzz-generic"
+t = Fuzz::Tests.new
+t.register Fuzz::Test.new("char") {|arg, size| arg }
+t.register Fuzz::Test.new("char") {|arg, size| "A"*1000 } # long-string
+t.register Fuzz::Test.new("char") {|arg, size| "%n"*1000 } # format-string
-class Example < FuzzStruct
- text :example, 7
- unsigned :version, 8
-
- initial_value.example = "example"
- initial_value.version = 1
-end
-
-begin
- example_tests = Fuzz::Tests.new
-
- # tests for the text field
- example_tests.register Fuzz::Test.new("char") {|arg, size| arg} # return argument
- example_tests.register Fuzz::Test.new("char") {|arg, size| ""} # return empty string
- example_tests.register Fuzz::Test.new("char") {|arg, size| "A"*1000} # return many many A's
-
- # tests for unsigned numbers
- example_tests.register Fuzz::Test.new("unsigned") {|arg, size| arg } # return argument
- example_tests.register Fuzz::Test.new("unsigned") {|arg, size| 0 } # return zero
- example_tests.register Fuzz::Test.new("unsigned") {|arg, size| rand(5) } # return a small number
- example_tests.register Fuzz::Test.new("unsigned") {|arg, size| 2.power!(size) } # return biggest number
-
- e = Example.new
- e.prepare! example_tests
-end
+si.prepare! t
\end{verbatim}
-
-\subsection{Some example tests on Tor itself}
-
-[Show a good example of a fuzz test on one of Tor's structs, to
-make things more concrete.]
-
+Now we need a connection to the control-port through which we can send our data.
+\begin{verbatim}
+options = {
+ :host => "127.0.0.1", # ip where our tor process is running
+ :port => "2323", # number of the control-port
+ :debug => true, # much output but shows the permutations
+ :type => :tcp, # tcp-connection
+ :timeout => 0.5 # timeout between each sending
+}
+c = Fuzz::Connection.new(options)
+c.fuzz!( si, " " )
+\end{verbatim}
+This is allready enough to fuzz the Signal Item but it doesnt give us any
+information when the tor-process has been crashed by the fuzzer. Cause
+this is very important when running a larg amount of tests automatically
+one can use the Observer and Coremanager classes.\\
+To start a binary as a child of the observer class
\subsection{Do the Fuzz!}
\section{T.O.P.F Reference}
@@ -186,8 +152,9 @@
\subsection{Backtrace}
\subsection{Proxy}
\subsubsection{Http-Proxy}
+Not yet finished but should act as a fuzzing-proxy in the future.
\subsubsection{Https-Proxy}
-
+Not yet finished but should act as a fuzzing-proxy in the future.
\section{Fuzz-Struct Reference}
\subsection{Types}
\label{types}
@@ -205,7 +172,6 @@
\subsubsection{nested}
\subsubsection{pad}
-
\section{Appendix}
\section{Links}
\label{links}
Added: topf/trunk/examples/argument_fuzz_example.rb
===================================================================
--- topf/trunk/examples/argument_fuzz_example.rb (rev 0)
+++ topf/trunk/examples/argument_fuzz_example.rb 2007-08-27 16:54:30 UTC (rev 11289)
@@ -0,0 +1,16 @@
+require "../lib/fuzz-generic"
+
+class Argument < FuzzStruct
+ text :argument, 20
+ initial_value.argument = "BLAH"
+end
+
+begin
+ config = YAML::load_file "../config/config.yml"
+ app = config["BINDIR"]+"/argument_example"
+
+ arg = Argument.new
+ arg.prepare! Fuzz::DEFAULT_TESTS
+
+ Fuzz::argument(app, arg, config)
+end
Added: topf/trunk/examples/example1.rb
===================================================================
--- topf/trunk/examples/example1.rb (rev 0)
+++ topf/trunk/examples/example1.rb 2007-08-27 16:54:30 UTC (rev 11289)
@@ -0,0 +1,17 @@
+#!/usr/bin/ruby
+require "../lib/fuzz-generic"
+
+class Example < FuzzStruct
+ text :example, 7, :fuzzable => false
+ unsigned :version, 8
+
+ initial_value.example = "example"
+ initial_value.version = 1
+end
+
+begin
+ e = Example.new
+ e.version = 2
+ pp e
+end
+
Added: topf/trunk/lib/fuzz/collection.rb
===================================================================
--- topf/trunk/lib/fuzz/collection.rb (rev 0)
+++ topf/trunk/lib/fuzz/collection.rb 2007-08-27 16:54:30 UTC (rev 11289)
@@ -0,0 +1,53 @@
+module Fuzz
+ # This Class collects many FuzzStructs and takes a block to calculate the
+ # wanted output of everything.
+ class Collection
+ attr_reader :structs, :number_of_tests
+ def initialize(join_character = "")
+ @join_character = join_character
+ @structs = Array.new
+ @format_block = nil
+ @prepared = false
+ @fuzz_index = 0
+ @number_of_tests = 0
+ end
+
+ # adds a FuzzStruct to the collection
+ def add_struct(struct)
+ @structs << struct if struct.is_a?(FuzzStruct)
+ end
+
+ # apply tests to all objects in the collection
+ def add_tests(tests)
+ raise "argument must be a Fuzz::Tests object" if !tests.is_a?(Fuzz::Tests)
+ @structs.each do |struct|
+ struct.prepare! tests
+ @number_of_tests += struct.permutations
+ end
+ @prepared = true
+ end
+
+ # add the format block to the class
+ def add_format_block(&block)
+ @format_block = block
+ end
+
+ # take some arguments and call the format block with these for the correct output
+ def fuzz!(args)
+ raise "no tests have been added yet" if !@prepared
+ args[:fuzz_index] = @fuzz_index
+ resultString, @fuzz_index = to_s( args )
+ if @fuzz_index == @structs.size
+ raise "finished all tests"
+ end
+ @number_of_tests-=1
+ [ resultString, @number_of_tests]
+ end
+
+ # supply a block on howto format the collection
+ def to_s(args)
+ raise "no format block given yet!" if !@format_block
+ @format_block.call(args, @structs, @join_character)
+ end
+ end
+end
Added: topf/trunk/lib/fuzz/connection.rb
===================================================================
--- topf/trunk/lib/fuzz/connection.rb (rev 0)
+++ topf/trunk/lib/fuzz/connection.rb 2007-08-27 16:54:30 UTC (rev 11289)
@@ -0,0 +1,90 @@
+module Fuzz
+ # makes a Connection to a given target and sends our raw data to it
+ # see yourself what options are needed :>
+ class Connection
+ def initialize(args)
+ @type = args[:type]
+ @host = args[:host]
+ @port = args[:port]
+ @path = args[:path]
+ @http_direction = args[:http_direction]
+ @http_url = args[:http_url]
+ @observer = args[:observer]
+ @coremanager = @observer.coremanager if @observer.is_a?(BinaryObserver)
+ args[:timeout] ? @timeout = args[:timeout] : @timeout = 0.2
+
+ Fuzz::LOGGER.level = Logger::INFO if !args[:debug]
+
+ @socket = connect
+ end
+
+ # Send the data given as string to the configured target
+ def send( data )
+ begin
+ case @type
+ when :http
+ case @http_direction
+ when :post
+ @socket.post @http_url ,data
+ when :get
+ @socket.get @http_url, data
+ end
+ else
+ @socket.write data
+ end
+ rescue Exception => exception
+ Fuzz::output_crash_info(@coremanager) if @coremanager.new_corefile?
+ Kernel::exit
+ end
+ end
+
+ def fuzz!(structs, join_character="", args={})
+ structs = [structs] if !structs.is_a? Array
+ structs.each_with_index do |struct, index|
+ Fuzz::LOGGER.info "[x] fuzzing struct %d of %d with %d permutations" % [index+1, structs.size, 0]
+
+ Fuzz.fuzz(@observer, struct){|struct|
+ data = struct.join( join_character )
+ Fuzz::LOGGER.debug "sending data: %s" % data
+ self.send data
+ #assert(args[:assert] )
+ }
+ end
+ raise "finished all tests"
+ end
+
+ # receives data from the socket and tests it against a supplied array of strings, values
+ def assert(reply = [])
+ begin
+ timeout(@timeout) do
+ reply_data = Regexp.new( @socket.readline().to_regexp )
+ raise "ASSERTION %s FAILED\nreceived %s" % [ reply.join(","), reply_data] if !reply.find_all{|x| x=~ reply_data}
+ end
+ rescue Exception
+ Fuzz::LOGGER.debug "[!] Exception: %s" % $!
+ end
+ end
+
+ def close
+ @socket.close if @type != :http && @type != :tls
+ end
+ private
+ def connect
+ begin
+ case @type
+ when :tcp
+ return TCPSocket.new(@host, @port)
+ when :unix
+ return UNIXSocket.open( @path )
+ when :http
+ return Net::HTTP.new(@host, @port)
+ when :tls
+ raise "not implemented yet"
+ end
+ rescue Exception
+ Fuzz::LOGGER.info "[!] Exception: %s" % $!
+ exit 0
+ end
+ end
+ end
+end
Added: topf/trunk/lib/fuzz/core.rb
===================================================================
--- topf/trunk/lib/fuzz/core.rb (rev 0)
+++ topf/trunk/lib/fuzz/core.rb 2007-08-27 16:54:30 UTC (rev 11289)
@@ -0,0 +1,41 @@
+module Fuzz
+ class CoreManager
+ def initialize(executable, corepath)
+ @executable = executable
+ @corepath = corepath
+ @list = get_corefiles
+ enable_coredump
+ end
+
+ def latest_corefile
+ @list.last
+ end
+
+ def new_corefile?
+ new = get_corefiles
+ if new.size > 0 && new != @list
+ @list = new
+ true
+ else
+ false
+ end
+ end
+
+ def get_latest_backtrace
+ GDB::Interface.new( {:executable => @executable, :core => @corepath+"/"+ at list.last} ).bt if @list
+ end
+
+ def get_latest_registers
+ GDB::Interface.new( {:executable => @executable, :core => @corepath+"/"+ at list.last} ).info("registers") if @list
+ end
+
+ def enable_coredump
+ system("ulimit -c unlimited")
+ end
+ private
+
+ def get_corefiles
+ Dir.new(File.expand_path(@corepath)).entries.select{|entry| entry=~/core/ }.sort{|x, y| File.mtime(@corepath+"/"+x) <=> File.mtime(@corepath+"/"+y)}
+ end
+ end
+end
Added: topf/trunk/lib/fuzz/helper.rb
===================================================================
--- topf/trunk/lib/fuzz/helper.rb (rev 0)
+++ topf/trunk/lib/fuzz/helper.rb 2007-08-27 16:54:30 UTC (rev 11289)
@@ -0,0 +1,10 @@
+class String
+ # insert! copies the string before altering it compared to the normal insert
+ # method
+ def insert!(index, inject)
+ self.clone.insert(index, inject)
+ end
+ def to_regexp
+ self.to_s.gsub("{", "\{").gsub("}", "\}").gsub("[", "\[").gsub("]", "\]").gsub("(", "\(").gsub(")", "\)")
+ end
+end
Added: topf/trunk/lib/fuzz/observer.rb
===================================================================
--- topf/trunk/lib/fuzz/observer.rb (rev 0)
+++ topf/trunk/lib/fuzz/observer.rb 2007-08-27 16:54:30 UTC (rev 11289)
@@ -0,0 +1,47 @@
+module Fuzz
+ class BinaryObserver
+ attr_reader :coremanager
+ def initialize( programname, corepath, *arguments )
+ @programname = programname
+ @arguments = arguments.to_a
+ @pid = nil
+ @coremanager = Fuzz::CoreManager.new(@programname, corepath)
+ @running = false
+ end
+
+ def observe!
+ if !is_running?
+ Thread.new do
+ @pid = fork do
+ Fuzz::LOGGER.info "[x] starting #{@programname} #{@arguments.join(" ")}"
+ exec("#{@programname} #{@arguments.join(" ")} ")
+ end
+ @running = true
+ Process.wait
+ @running = false
+ end
+ else
+ Fuzz::LOGGER.info "[x] #{@programname} allready running"
+ end
+ end
+
+ def get_pid
+ @pid
+ end
+
+ def exit
+ Process.kill "KILL", @pid if @running
+ end
+
+ def is_running?
+ reg = Regexp.new( @programname.split("/").last )
+ result = IO.popen("ps faux").readlines.find_all{|x| x=~ reg }
+ if result.size > 0
+ true
+ else
+ false
+ end
+ end
+
+ end
+end
Added: topf/trunk/lib/fuzz/test.rb
===================================================================
--- topf/trunk/lib/fuzz/test.rb (rev 0)
+++ topf/trunk/lib/fuzz/test.rb 2007-08-27 16:54:30 UTC (rev 11289)
@@ -0,0 +1,25 @@
+module Fuzz
+ # This Class holds our test which is a ruby-block that must be able to process one argument
+ # which is the default value of the Field to be tested. This argument can be ignored if wanted.
+ class Test
+ attr_reader :type
+ def initialize(type, &block)
+ case type.downcase
+ when "char"
+ @type = "char"
+ when "unsigned"
+ @type = "unsigned"
+ when "signed"
+ @type = "signed"
+ when "octet"
+ @type = "octet"
+ else
+ raise "unknown type"
+ end
+ @test = block
+ end
+ def run(arg, size)
+ @test.call arg, size
+ end
+ end
+end
Added: topf/trunk/lib/fuzz/tests.rb
===================================================================
--- topf/trunk/lib/fuzz/tests.rb (rev 0)
+++ topf/trunk/lib/fuzz/tests.rb 2007-08-27 16:54:30 UTC (rev 11289)
@@ -0,0 +1,135 @@
+module Fuzz
+ # This Class holds all tests to related to a given field-type
+ class Tests
+ attr_reader :tests, :permutations, :signed_tests, :char_tests, :unsigned_tests
+ def initialize(struct=nil)
+ @char_tests = []
+ @unsigned_tests = []
+ @signed_tests = []
+ @octet_tests = []
+ @permutations = []
+ @tests = []
+ @struct = struct
+ @count = 0
+ @dimension = nil
+ end
+
+ # register a new test to a given type
+ def register(test)
+ raise "argument must be a Fuzz::Test object" if !test.is_a?(Fuzz::Test)
+ case test.type
+ when "char"
+ @char_tests.push test
+ when "unsigned"
+ @unsigned_tests.push test
+ when "signed"
+ @signed_tests.push test
+ when "octet"
+ @octet_tests.push test
+ end
+ end
+
+ # generate all possible permutations
+ def generate_test_data
+ Fuzz::LOGGER.debug "[x] generating %d char %d unsigned %d signed tests" % [@char_tests.size, @unsigned_tests.size, @signed_tests.size]
+
+ generate_test_data!
+ permutate
+ @count = 0
+ end
+
+ # get the next permutation
+ def get_permutation
+ raise "no tests have been generated yet" if !@permutations or !@tests
+ raise "end of tests" if @count.to_i == @dimension.to_i
+ test = []
+ perm = @permutations.collect{|element| element[@count] }
+ tempcount = 0
+ @count += 1
+ @tests.collect{ |element|
+ result = element[ perm[tempcount] ]
+ tempcount+=1
+ result
+ }
+ end
+
+ def get_all
+ end
+
+ def set_struct(struct)
+ raise "argument not a bitstruct!" if !struct.is_a?(BitStruct)
+ @struct = struct
+ generate_test_data!
+ permutate!
+ end
+
+ # debug function...
+ def show_permutation_indices
+ @permutations[0].each_with_index do |element, index|
+ pp @permutations.collect{|el| el[index] }
+ end
+ end
+ private
+ def generate_test_data!
+ result = []
+ raise "argument must be a BitStruct object" if !@struct.is_a?(BitStruct)
+ @struct.fields.each do |field|
+ temp = []
+ arg = @struct.method(field.name).call
+ length = field.length
+ case field.class.to_s
+ when "BitStruct::SignedField"
+ @signed_tests.each do |test|
+ temp.push( test.run(arg, length) )
+ end
+ result.push(temp)
+ when "BitStruct::UnsignedField"
+ @unsigned_tests.each do |test|
+ temp.push( test.run(arg, length) )
+ end
+ result.push(temp)
+ when "BitStruct::TextField" || "BitStruct::CharField"
+ @char_tests.each do |test|
+ temp.push( test.run(arg, length) )
+ end
+ result.push(temp)
+ when "BitStruct::HexOctetField"
+ @hex_tests.each do |test|
+ temp.push( test.run(arg, length) )
+ end
+ result.push(temp)
+ else
+ Fuzz::LOGGER.debug "new field-type: %s" % field.class.to_s
+ end
+ end
+ @tests = result
+ end
+
+ def permutate!
+ raise "tests have not been generated yet" if !@tests
+
+ sizes = @tests.collect {|element| element.size}
+ @dimension = sizes.inject(1){|prod, element| prod*element }
+ permutations = Array.new(sizes.size, Array.new(@dimension+1, 0) )
+ result = []
+ permutations.each_with_index do |element, index|
+ count = 0
+ temp = 0
+ tempdimension = sizes[(index+1)..-1].inject(1){|prod, el| prod*el}
+ result.push element.collect{|entry|
+ if count == tempdimension
+ temp+=1
+ temp = 1 if temp == (sizes[index]+1)
+
+ count = 0
+ end
+ count+=1
+ temp
+ }
+ end
+ Fuzz::LOGGER.debug "[x] calculated %d permutations of struct %s" % [result.first.size, self.class]
+ @permutations = result
+ end
+ end
+
+end
Modified: topf/trunk/lib/fuzz.rb
===================================================================
--- topf/trunk/lib/fuzz.rb 2007-08-27 15:38:17 UTC (rev 11288)
+++ topf/trunk/lib/fuzz.rb 2007-08-27 16:54:30 UTC (rev 11289)
@@ -1,312 +1,58 @@
-class String
- # insert! copies the string before altering it compared to the normal insert
- # method
- def insert!(index, inject)
- self.clone.insert(index, inject)
- end
- def to_regexp
- self.to_s.gsub("{", "\{").gsub("}", "\}").gsub("[", "\[").gsub("]", "\]").gsub("(", "\(").gsub(")", "\)")
- end
-end
+###################################################
+# INCLUDE SECTION
+###################################################
+require "fuzz/helper"
+require "fuzz/observer"
+require "fuzz/core"
+require "fuzz/connection"
+require "fuzz/collection"
+require "fuzz/test"
+require "fuzz/tests"
module Fuzz
- # define your own if you want :)
+ ###################################################
+ # GLOBAL VARIABLES SECTION
+ ###################################################
MAX_RAND = 1000
- # This Class collects many FuzzStructs and takes a block to calculate the
- # wanted output of everything.
- class Collection
- attr_reader :structs, :number_of_tests
- def initialize(join_character = "")
- @join_character = join_character
- @structs = Array.new
- @format_block = nil
- @prepared = false
- @fuzz_index = 0
- @number_of_tests = 0
- end
- # adds a FuzzStruct to the collection
- def add_struct(struct)
- @structs << struct if struct
- end
+ ###################################################
+ # MODULE METHODS SECTION
+ ###################################################
- # apply tests to all objects in the collection
- def add_tests(tests)
- raise "argument must be a Fuzz::Tests object" if !tests.is_a?(Fuzz::Tests)
- @structs.each do |struct|
- struct.prepare! tests
- @number_of_tests += struct.permutations
- end
- @prepared = true
- end
-
- def add_format_block(&block)
- @format_block = block
- end
-
- def fuzz!(args)
- raise "no tests have been added yet" if !@prepared
- args[:fuzz_index] = @fuzz_index
- resultString, @fuzz_index = to_s( args )
- if @fuzz_index == @structs.size
- raise "finished all tests"
- end
- @number_of_tests-=1
- [ resultString, @number_of_tests]
- end
-
- # supply a block on howto format the collection
- def to_s(args)
- raise "no format block given yet!" if !@format_block
- @format_block.call(args, @structs, @join_character)
- end
+ # dumps a backtrace and register info to the log
+ def Fuzz.output_crash_info(coremanager)
+ Fuzz::LOGGER.info "[?] found new corefile(%s)\nprinting backtrace:\n%s\nprinting registers:\n%s" % [ coremanager.latest_corefile, coremanager.get_latest_backtrace, coremanager.get_latest_registers ]
end
- class BinaryObserver
- attr_reader :coremanager
- def initialize( programname, corepath, *arguments )
- @programname = programname
- @arguments = arguments.to_a
- @pid = nil
- @coremanager = Fuzz::CoreManager.new(@programname, corepath)
- @running = false
+ # This method iterates the given block endless and gives each iteration
+ # a permutation of struct as argument.
+ # After the last permutation struct.fuzz! will raise an exception and so
+ # leaving the loop.
+ # If you dont catch this exception the execution will be stoped.
+ def Fuzz.fuzz_iterator(struct, coremanager, &block)
+ while true
+ block.call( struct.fuzz!, coremanager )
end
-
- def observe!
- Thread.new do
- @pid = fork do
- Fuzz::LOGGER.info "[x] starting #{@programname} #{@arguments.join(" ")}"
- exec("#{@programname} #{@arguments.join(" ")} ")
- end
- @running = true
- Process.wait
- Fuzz::LOGGER.debug "[!] %d coredumped? %s" % [@pid, process_status.coredump?]
- Fuzz::LOGGER.debug "[!] %d get uncaught signal %d" % [@pid, process_status.termsig] if process_status.signaled?
- Fuzz::LOGGER.debug "[!] %d stopped by signal %d" % [@pid, process_status.stopsig] if process_status.stopped?
- Fuzz::LOGGER.debug "[!] %d exited %d" % [@pid, process_status.exitstatus] if process_status.exited?
- @running = false
- end
- end
- def get_pid
- @pid
- end
- def exit
- Process.kill "KILL", @pid if @running
- end
end
- class CoreManager
- def initialize(executable, corepath)
- @executable = executable
- @corepath = corepath
- @list = get_corefiles
- enable_coredump
- end
-
- def latest_corefile
- @list.last
- end
-
- def new_corefile?
- new = get_corefiles
- if new.size > 0 && new != @list
- @list = new
- true
- else
- false
- end
- end
-
- def get_latest_backtrace
- GDB::Interface.new( {:executable => @executable, :core => @corepath+"/"+ at list.last} ).bt if @list
+ # This method combines the fuzz_iterator with a default exception handling
+ # which looks for a new corefile and if one is found prints out
+ # a backtrace and the last register state.
+ def Fuzz.fuzz(observerOrCoremanager, struct, &block)
+ if observerOrCoremanager.is_a?(BinaryObserver)
+ coremanager = observerOrCoremanager.coremanager
+ elsif observerOrCoremanager.is_a?(CoreManager)
+ coremanager = observerOrCoremanager
end
-
- def get_latest_registers
- GDB::Interface.new( {:executable => @executable, :core => @corepath+"/"+ at list.last} ).info("registers") if @list
- end
- def enable_coredump
- system("ulimit -c unlimited")
- end
- private
-
- def get_corefiles
- Dir.new(File.expand_path(@corepath)).entries.select{|entry| entry=~/core/ }.sort{|x, y| File.mtime(@corepath+"/"+x) <=> File.mtime(@corepath+"/"+y)}
- end
- end
-
- # This Class holds all tests to related to a given field-type
- class Tests
- attr_reader :tests, :permutations, :signed_tests, :char_tests, :unsigned_tests
- def initialize(struct=nil)
- @char_tests = []
- @unsigned_tests = []
- @signed_tests = []
- @octet_tests = []
- @permutations = []
- @tests = []
- @struct = struct
- @count = 0
- @dimension = nil
- end
-
- # register a new test to a given type
- def register(test)
- raise "argument must be a Fuzz::Test object" if !test.is_a?(Fuzz::Test)
- case test.type
- when "char"
- @char_tests.push test
- when "unsigned"
- @unsigned_tests.push test
- when "signed"
- @signed_tests.push test
- when "octet"
- @octet_tests.push test
- end
- end
-
- # generate all possible permutations
- def generate_test_data
- Fuzz::LOGGER.debug "[x] generating %d char %d unsigned %d signed tests" % [@char_tests.size, @unsigned_tests.size, @signed_tests.size]
-
- generate_test_data!
- permutate
- @count = 0
- end
-
- # get the next permutation
- def get_permutation
- raise "no tests have been generated yet" if !@permutations or !@tests
- raise "end of tests" if @count.to_i == @dimension.to_i
- test = []
- perm = @permutations.collect{|element| element[@count] }
- tempcount = 0
- @count += 1
- @tests.collect{ |element|
- result = element[ perm[tempcount] ]
- tempcount+=1
- result
- }
- end
-
- def get_all
- end
-
- def set_struct(struct)
- raise "argument not a bitstruct!" if !struct.is_a?(BitStruct)
- @struct = struct
- generate_test_data!
- permutate!
- end
-
- # debug function...
- def show_permutation_indices
- @permutations[0].each_with_index do |element, index|
- pp @permutations.collect{|el| el[index] }
- end
- end
- private
- def generate_test_data!
- result = []
- raise "argument must be a BitStruct object" if !@struct.is_a?(BitStruct)
- @struct.fields.each do |field|
- temp = []
- arg = @struct.method(field.name).call
- length = field.length
- case field.class.to_s
- when "BitStruct::SignedField"
- @signed_tests.each do |test|
- temp.push( test.run(arg, length) )
- end
- result.push(temp)
- when "BitStruct::UnsignedField"
- @unsigned_tests.each do |test|
- temp.push( test.run(arg, length) )
- end
- result.push(temp)
- when "BitStruct::TextField" || "BitStruct::CharField"
- @char_tests.each do |test|
- temp.push( test.run(arg, length) )
- end
- result.push(temp)
- when "BitStruct::HexOctetField"
- @hex_tests.each do |test|
- temp.push( test.run(arg, length) )
- end
- result.push(temp)
- else
- Fuzz::LOGGER.debug "new field-type: %s" % field.class.to_s
- end
- end
- @tests = result
- end
-
- def permutate!
- raise "tests have not been generated yet" if !@tests
-
- sizes = @tests.collect {|element| element.size}
- @dimension = sizes.inject(1){|prod, element| prod*element }
- permutations = Array.new(sizes.size, Array.new(@dimension+1, 0) )
- result = []
- permutations.each_with_index do |element, index|
- count = 0
- temp = 0
- tempdimension = sizes[(index+1)..-1].inject(1){|prod, el| prod*el}
- result.push element.collect{|entry|
- if count == tempdimension
- temp+=1
- temp = 1 if temp == (sizes[index]+1)
-
- count = 0
- end
- count+=1
- temp
- }
- end
- Fuzz::LOGGER.debug "[x] calculated %d permutations of struct %s" % [result.first.size, self.class]
- @permutations = result
- end
- end
-
- # This Class holds our test which is a ruby-block that must be able to process one argument
- # which is the default value of the Field to be tested. This argument can be ignored if wanted.
- class Test
- attr_reader :type
- def initialize(type, &block)
- case type.downcase
- when "char"
- @type = "char"
- when "unsigned"
- @type = "unsigned"
- when "signed"
- @type = "signed"
- when "octet"
- @type = "octet"
- else
- raise "unknown type"
- end
- @test = block
- end
- def run(arg, size)
- @test.call arg, size
- end
- end
-
- def Fuzz.fuzz_iterator(struct, &block)
- while true
- block.call( struct.fuzz! )
- end
- end
-
- def Fuzz.fuzz(observer, struct, &block)
begin
- Fuzz::fuzz_iterator(struct, &block)
+ Fuzz::fuzz_iterator(struct, coremanager, &block)
rescue Exception => exception
- Fuzz::LOGGER.info "[?] error while sending data:\n%s\n" % data
- Fuzz::LOGGER.info "[?] locking for new corefiles.."
- if observer.coremanager.new_corefile?
- Fuzz::LOGGER.info "[?] found new corefile(%s)\nprinting backtrace:\n%s\nprinting registers:\n%s" % [ observer.coremanager.latest_corefile, observer.coremanager.get_latest_backtrace, observer.coremanager.get_latest_registers ]
+ # TODO: IF NOT END OF FUZZ
+# Fuzz::LOGGER.info "[?] error while sending data:\n%s\n" % data
+ if coremanager.new_corefile? or exception.message == "core found"
+ Fuzz::output_crash_info(coremanager)
else
Fuzz::LOGGER.info "[?] no new corefile found"
end
@@ -314,130 +60,23 @@
end
end
+ # This method implements a simple argument fuzzer
def Fuzz.argument(application, struct, config)
- reg = Regexp.new "outch!"
+ raise "application \"%s\" not existent or not executable" % application if !File.executable?(application)
coremanager = Fuzz::CoreManager.new(application, config["COREDIR"])
- begin
- Fuzz::fuzz_iterator(struct){|argument|
- argument = argument.join if argument.is_a?(Array)
- system(application + " " + argument)
- raise "outch!\n%s" % argument if coremanager.new_corefile?
- }
- rescue Exception => exception
- if exception.to_s =~ reg
- argument = exception.to_s.gsub("outch!", "")
- puts "!"*40
- puts "argument:\n%s \nkilled the application!" % argument
- puts "backtrace:"
- puts coremanager.get_latest_backtrace
- puts "_"*40
- puts "registers:"
- puts coremanager.get_latest_registers
- puts "writing POC"
- File.open("POC", "w+").write "#!/bin/sh\n#{application.strip} #{argument.strip}"
- puts "!"*40
- end
- Kernel::exit
- end
- end
- # makes a Connection to a given target and sends our raw data to it
- # see yourself what options are needed :>
- class Connection
- def initialize(args)
- @type = args[:type]
- @host = args[:host]
- @port = args[:port]
- @path = args[:path]
- @http_direction = args[:http_direction]
- @http_url = args[:http_url]
- @observer = args[:observer]
- @coremanager = @observer.coremanager
- args[:timeout] ? @timeout = args[:timeout] : @timeout = 0.2
-
- Fuzz::LOGGER.level = Logger::INFO if !args[:debug]
-
- @socket = connect
- end
-
- def send( data )
- begin
- case @type
- when :http
- case @http_direction
- when :post
- @socket.post @http_url ,data
- when :get
- @socket.get @http_url, data
- end
- else
- @socket.write data
- end
- rescue Exception => exception
- if @coremanager.new_corefile?
- puts "argument:\n%s \nkilled the application!" % data
- puts "backtrace:"
- puts @coremanager.get_latest_backtrace
- puts "_"*40
- puts "registers:"
- puts @coremanager.get_latest_registers
- puts "writing POC"
- end
- Kernel::exit
- end
- end
-
- def fuzz!(structs, join_character="", args={})
- structs = [structs] if !structs.is_a? Array
- structs.each_with_index do |struct, index|
- Fuzz::LOGGER.info "[x] fuzzing struct %d of %d with %d permutations" % [index+1, structs.size, 0]
-
- Fuzz.fuzz(@observer, struct){|struct|
- data = struct.join( join_character )
- Fuzz::LOGGER.debug "sending data: %s" % data
- self.send data
- #assert(args[:assert] )
- }
- end
- raise "finished all tests"
- end
-
- # receives data from the socket and tests it against a supplied array of strings, values
- def assert(reply = [])
- begin
- timeout(@timeout) do
- reply_data = Regexp.new( @socket.readline().to_regexp )
- raise "ASSERTION %s FAILED\nreceived %s" % [ reply.join(","), reply_data] if !reply.find_all{|x| x=~ reply_data}
- end
- rescue Exception
- Fuzz::LOGGER.debug "[!] Exception: %s" % $!
- end
- end
-
- def close
- @socket.close if @type != :http && @type != :tls
- end
-private
- def connect
- begin
- case @type
- when :tcp
- return TCPSocket.new(@host, @port)
- when :unix
- return UNIXSocket.open( @path )
- when :http
- return Net::HTTP.new(@host, @port)
- when :tls
- raise "not implemented yet"
- end
- rescue Exception
- Fuzz::LOGGER.info "[!] Exception: %s" % $!
- exit 0
- end
- end
+ Fuzz::fuzz(coremanager, struct){|argument, coremanager|
+ argument = argument.join if argument.is_a?(Array)
+ system( application + " " + argument)
+ raise "core found" if coremanager.new_corefile?
+ }
end
begin
+ ###################################################
+ # TESTS SECTION
+ ###################################################
+
DEFAULT_TESTS = Tests.new
# Tests for character fields aka strings
DEFAULT_TESTS.register Fuzz::Test.new("char") {|arg, size| arg } # Return string
@@ -477,10 +116,6 @@
DEFAULT_TESTS.register Fuzz::Test.new("unsigned") {|arg, size| rand(5) } # return a small number
DEFAULT_TESTS.register Fuzz::Test.new("unsigned") {|arg, size| 2.power!(size) } # return biggest number
- DEBUG_TESTS = Tests.new
- DEBUG_TESTS.register Fuzz::Test.new("char") {|arg, size| arg } # Return string
- DEBUG_TESTS.register Fuzz::Test.new("char") {|arg, size| "" } # Return empty String
-
# debugging output
LOGGER = Logger.new(STDOUT)
LOGGER.level = Logger::DEBUG
Modified: topf/trunk/tor-control-fuzz.rb
===================================================================
--- topf/trunk/tor-control-fuzz.rb 2007-08-27 15:38:17 UTC (rev 11288)
+++ topf/trunk/tor-control-fuzz.rb 2007-08-27 16:54:30 UTC (rev 11289)
@@ -61,7 +61,6 @@
:assert => TOPF::Control::SETCONF_REPLYS } )
rescue Exception => error
- #Fuzz::LOGGER.info "[x] %s\n%s\nclosing everything down" % [ $!, error.backtrace.join("\n") ]
Fuzz::LOGGER.info "[x] closing everything down"
fuzzer.close if fuzzer
observer.exit if observer and started
Modified: topf/trunk/tor-dir-fuzz.rb
===================================================================
--- topf/trunk/tor-dir-fuzz.rb 2007-08-27 15:38:17 UTC (rev 11288)
+++ topf/trunk/tor-dir-fuzz.rb 2007-08-27 16:54:30 UTC (rev 11289)
@@ -16,7 +16,8 @@
:http_direction => :post,
:http_url => "/tor/",
:debug => config["DEBUG"],
- :observer => observer
+ :observer => observer,
+ :timeout => 0.01
}
osslkey = OpenSSL::PKey::RSA.new(1024)
Added: topf/trunk/utils/set_core_pattern
===================================================================
--- topf/trunk/utils/set_core_pattern (rev 0)
+++ topf/trunk/utils/set_core_pattern 2007-08-27 16:54:30 UTC (rev 11289)
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo "$PWD/../core/core" > /proc/sys/kernel/core_pattern
Property changes on: topf/trunk/utils/set_core_pattern
___________________________________________________________________
Name: svn:executable
+ *
More information about the tor-commits
mailing list