Less-Dumb Fuzzing and Ruby Metaprogramming
Upcoming SlideShare
Loading in...5
×
 

Less-Dumb Fuzzing and Ruby Metaprogramming

on

  • 2,965 views

A talk I gave at the Lone Star Ruby Conference, Aug 28 2010

A talk I gave at the Lone Star Ruby Conference, Aug 28 2010

Statistics

Views

Total Views
2,965
Views on SlideShare
2,965
Embed Views
0

Actions

Likes
4
Downloads
28
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Less-Dumb Fuzzing and Ruby Metaprogramming Less-Dumb Fuzzing and Ruby Metaprogramming Presentation Transcript

    • Less-Dumb Fuzzing & Ruby Metaprogramming Nephi Johnson
    • Introduction ‣ Who am I? ‣ Security Researcher at BreakingPoint ‣ Why do I like ruby? ‣ flexibility ‣ simple ‣ fun 08-28-2010 Nephi Johnson 2
    • Outline ‣ Main points I hope to convey ‣ Ruby metaprogramming is fun! ‣ there are more intelligent ways to fuzz ‣ data-format generation is easier (-est?) with Ruby 08-28-2010 Nephi Johnson 3
    • Outline ‣ Fuzzing ‣ What? and why? ‣ Ways to fuzz ‣ dumb-fuzzing (byte fuzzing), less-dumb ‣ Metaprogramming ‣ Concepts ‣ hooks, code that writes code ‣ Putting it all together ‣ Basic Example, Funder 08-28-2010 Nephi Johnson 4
    • Fuzzing 08-28-2010 Nephi Johnson 5
    • Fuzzing > What? and why? ‣ What? ‣ a means of finding bugs in applications ‣ throw all combinations of data at any and all means of input ‣ and why? ‣ fix the bugs ‣ less crashes/errors for the end-user ‣ more secure applications ‣ feel more confident that your code will correctly handle any input ‣ fun and profit 08-28-2010 Nephi Johnson 6
    • Fuzzing > Ways to fuzz ‣ Dumb-Fuzzing (aka byte-fuzzing) ‣ create sets of valid input ‣ shoot for code coverage ‣ iterate over each byte/word/dword in each set of valid input ‣ akin to brute-forcing passwords ‣ Less-Dumb ‣ create a valid structure of the data format in memory ‣ target critical fields/values (and combinations) ‣ ignore unimportant values 08-28-2010 Nephi Johnson 7
    • … > Ways to fuzz > Dumb-Fuzzing ‣ Why is it so dumb? ‣ wastes time iterating over meaningless values ‣ can't change values and keep other fields valid ‣ checksums ‣ lengths ‣ can only modify the data, not add to or remove 08-28-2010 Nephi Johnson 8
    • … > Ways to fuzz > Dumb-Fuzzing ‣ basic example file_name = ARGV[0] base_input = File.read(file_name) base_input.length.times do |offset|     new_input = base_input.clone     256.times do |new_val|         new_input[offset, 1] = new_val.chr         …         # send new_input to program         # monitor for crashes, exceptions, etc         …     end end 08-28-2010 Nephi Johnson 9
    • … > Ways to fuzz > Less-Dumb ‣ Several ways to be “less-dumb” about generating the data used in fuzzing ‣ no classes/code re-use, just one massive function/script ‣ hard to maintain/modify ‣ specific to one format ‣ inflexible ‣ generate a class specific to each data-format ‣ easier to maintain ‣ specific to one format ‣ more flexible ‣ create a re-usable framework for defining the formats ‣ easy to maintain ‣ many formats ‣ very flexible 08-28-2010 Nephi Johnson 10
    • Metaprogramming 08-28-2010 Nephi Johnson 11
    • Metaprogramming ‣ Hooks ‣ Code that writes code 08-28-2010 Nephi Johnson 12
    • Metaprogramming > Hooks ‣ defined in Module: ‣ defined in Kernel: ‣ const_missing ‣ method_missing ‣ extended ‣ included ‣ defined in Object: ‣ method_added ‣ singleton_method_added ‣ method_removed ‣ singleton_method_removed singleton_method_undefined method_undefined ‣ ‣ ‣ defined in Class ‣ inherited 08-28-2010 Nephi Johnson 13
    • Metaprogramming > Hooks class InheritMe     def self.inherited(other)         puts "#{other} inherited #{self.class}"     end end class A < InheritMe end # ==> A inherited InheritMe 08-28-2010 Nephi Johnson 14
    • Metaprogramming > Hooks class MethodMissing     def method_missing(method_name, *args)         puts "#{method_name} called with #{args.inspect}"     end end m = MethodMissing.new m.nonexistent_method(1, "arg_2", Object.new) # ==> nonexistent_method called with [1, "arg_2", #<Object:0xb7617594>] 08-28-2010 Nephi Johnson 15
    • Meta > Code that writes code ‣ Two main ways to create code that modifies and/or creates code: ‣ via method calls ‣ through various evals ‣ eval a string ‣ call a block/Proc/lambda ‣ (almost all the same thing) 08-28-2010 Nephi Johnson 16
    • … > Code that ... > via method calls ‣ Available method calls: ‣ defined in Module: ‣ defined in Object: ‣ alias_method ‣ instance_variable_get/set ‣ class_variable_get/set ‣ remove_instance_variable ‣ remove_class_variable ‣ send ‣ const_get/set ‣ remove_const ‣ extend_object ‣ define_method ‣ remove_method ‣ undef_method 08-28-2010 Nephi Johnson 17
    • … > Code that ... > via method calls class Example     def self.inherited(other)         # define_method is a private method         other.send(:define_method, :subclassed_example?) do             return "possibly"         end     end     def method_missing(method, *args) if method.to_s =~ /create_(.*)/             class_name = $1.capitalize             new_klass = begin                 if (Object.constants.include?(class_name))                     Object.const_get(class_name)                 else                     Object.const_set(class_name, Class.new)                 end             end                             return new_klass.new         end         raise "Method #{method} doesn't exist!"     end end class F < Example ; end f = F.new puts f.subclassed_example?     # ==> possibly puts f.create_cookiemonster    # ==> #<Cookiemonster:0xb782c26c> 08-28-2010 Nephi Johnson 18
    • … > Code that ... > via evals ‣ Available ways to eval code: ‣ defined in Module: ‣ defined in Object: ‣ class_eval ‣ instance_eval ‣ module_eval ‣ defined in Kernel: ‣ eval ‣ class_eval, module_eval, and instance eval can each accept a string or a block to evaluate ‣ however, eval only accepts a string 08-28-2010 Nephi Johnson 19
    • … > Code that ... > via evals ‣ class_eval (aka module_eval) class ClassEval    class << self    private    def private_static_method        puts "private static method"    end    end end ClassEval.private_static_method    # ==> private method `private_static_method' called                                              for ClassEval:Class (NoMethodError) ClassEval.class_eval do     private_static_method()        # ==> private static method     def made_by_class_eval         puts "made by class eval"     end  end c = ClassEval.new c.made_by_class_eval               # ==> made by class eval d = ClassEval.new d.made_by_class_eval               # ==> made by class eval 08-28-2010 Nephi Johnson 20
    • … > Code that ... > via evals ‣ instance_eval class InstanceEval     private     def private_method         puts "this is a private method"     end end i = InstanceEval.new i.instance_eval do     private_method          # ==> this is a private method end i.instance_eval do     def new_method         puts "this is a new method"     end end i.new_method                # ==> this is a new method j = InstanceEval.new j.new_method                # ==> undefined method `new_method' ... 08-28-2010 Nephi Johnson 21
    • … > Code that ... > via evals ‣ eval class Eval     attr_reader :greeting     def initialize         @greeting = "hello"     end     def greet         puts @greeting     end end e = Eval.new e.greet               # ==> Hello # binding is a private method eval('@greeting = "Glueck Auf"', e.send(:binding)) e.greet               # ==> Glueck Auf e.send(:eval, '@greeting = "Tag"') e.greet               # ==> Tag 08-28-2010 Nephi Johnson 22
    • … > Code that ... > via evals class Example     def self.inherited(other)         other.class_eval do             def subclassed_example?                 return "possibly"             end         end     end     def method_missing(method, *args) if method.to_s =~ /create_(.*)/             class_name = $1.capitalize             eval("class #{class_name} ; end")             new_klass = eval(class_name)             return new_klass.new         end         raise "Method #{method} doesn't exist!"     end end class F < Example ; end f = F.new puts f.subclassed_example?     # ==> possibly puts f.create_cookiemonster    # ==> #<Cookiemonster:0xb782c26c> 08-28-2010 Nephi Johnson 23
    • Putting it all Together 08-28-2010 Nephi Johnson 24
    • Putting it all Together ‣ Basic ‣ functionality ‣ usability ‣ fuzzing ‣ Funder ‣ highlights ‣ actions, sections, arrays, binding ‣ fuzzing with Funder ‣ static inheritance, options 08-28-2010 Nephi Johnson 25
    • Putting it all Together > Basic ‣ Goals ‣ Easy to define/modify data-formats ‣ Reusable ‣ Common needs ‣ lengths ‣ embedded formats ‣ checksums ‣ basic logic ‣ compression ‣ enumerable 08-28-2010 Nephi Johnson 26
    • Putting it all Together > Basic ‣ Want it to look something like: class DataFormat < Library     int32 field1 length(field2, field3)     int32 field2 value     string field3 value end d = DataFormat.new d.field2 = 100 d.field3 = “hello there” ... # etc      ‣ Basically, keep it as simple as possible 08-28-2010 Nephi Johnson 27
    • ... > Basic > Functionality ‣ Create the fields class Field     attr_accessor :name, :value class Library < Field     def initialize(name, value, opts={})     class << self         attr_accessor :order         @name = name ; @value = value         def field(name, value)         @opts = opts             @order ||= []     end             @order << [name, value]     def to_out ; end         end  end     end # class << self     def initialize(*args)         super(*args) if args.length == 2         self.class.order.each do |name, value|             self.class.class_eval { attr_accessor name }             instance_variable_set("@#{name}", value)         end      end  end class A < Library     field :field_1, 10     field :field_2, "this is field2" end a = A.new puts a.inspect    #==> #<A:0xb77d2f50 @order=[], @field_2="this is field2", @field_1=10> 08-28-2010 Nephi Johnson 28
    • ... > Basic > Usability ‣ Make it useful class PreField     attr_accessor :name, :klass, :value, :opts     def initialize(name, klass, value, opts) class Library < Field         @name = name ; @klass = klass     class << self         @value = value ; @opts = opts     def field(name, klass, value, opts={})     end         @order ||= []     def create ; @klass.new(@name, @value,         @order << PreField.new(name,klass,                             @opts) ; end                                value, opts) end     end     end # class << self class Int < Field     def to_out     def to_out ; [@value].pack(“n”) ; end         @order.map{|field| field.to_out }.join end     end     def  class Str < Field     def initialize(*args)     def to_out ; @value.to_s ; end         super(*args) if args.length == 2 end         @order = []         self.class.order.each do |pre_field|             self.class.class_eval { attr_accessor pre_field.name }             @order << pre_field.create             instance_variable_set("@#{name}", @order.last)         end     end end 08-28-2010 Nephi Johnson 29
    • ... > Basic > Usability ‣ Make it useful – in use require 'library' class A < Library     field :field_1, Int, 10     field :field_2, Str, "this is field2" end a = A.new puts a.to_out.inspect                 # ==> "000nthis is field2" a.field_1.value = 100 a.field_2.value = "field2 changed!" puts a.to_out.inspect                 # ==> “000dfield2 changed!" 08-28-2010 Nephi Johnson 30
    • ... > Basic > Fuzzing ‣ Enumerate the fields ‣ Do what you want with them ‣ shuffle the order ‣ omit / duplicate fields ‣ mutate the original data ‣ iterate over a set of values 08-28-2010 Nephi Johnson 31
    • ... > Basic > Fuzzing require 'library' class Fuzzer     def initialize(field) ; @field = field ; end      def fuzz(&block)         @field.get_all_fields.each do |child|             child.snapshot             child.get_fvals.each do |new_val|                 child.value = new_val                 block.call(@field) class Int < Field             end      def get_fvals             child.reset         (0..3).to_a         end      end      end  end end class Str < Field class Field     def get_fvals     def snapshot ; @cache = @value ; end          (1..4).map {|i| "A"*(2**i) }     def reset ; @value = @cache ; end      end      def get_all_fields end         return [self] unless @order         @order.each.map {|field| field.get_all_fields }.flatten     end  end 08-28-2010 Nephi Johnson 32
    • ... > Basic > Fuzzing ‣ Fuzzer – in use require 'library' class A < Library     field :field_1, Int, 0xffff     field :field_2, Str, "this is field2" end a = A.new f = Fuzzer.new(a)       "000000this is field2" f.fuzz do |new_a|       "000001this is field2"     puts new_a.to_out.inspect # ==> "000002this is field2" end       "000003this is field2"       "377377AA"       "377377AAAA"       "377377AAAAAAAA"       "377377AAAAAAAAAAAAAAAA" ‣ 100 total lines of code! ‣ (see next slide) 08-28-2010 Nephi Johnson 33
    • ... > Basic > Fuzzing > 100 lines # library.rb  56 # FUZZing # test.rb  57    1 class Field  58 class Int < Field   1 require 'library'   2     attr_accessor :name, :value  59     def get_fvals   2    3     def initialize(name, value)  60         (0..3).to_a   3 class A < Library   4         @name = name ; @value = value  61     end   4     field :field_1, Int, 0xffff   5     end  62 end   5     field :field_2, Str, "this is field2"   6     def to_out ; end  63 class Str < Field   6 end   7 end  64     def get_fvals   7    8   65         (1..4).map {|i| "A" * (2**i) }   8 a = A.new  66     end   9 f = Fuzzer.new(a)   9 class Int < Field  67 end  10 f.fuzz do |new_a|  10     def to_out  68 class Field  11     puts new_a.to_out.inspect  11         [@value].pack("n")  69     def snapshot ; @cache = @value ; end  12 end  12     end  13 end  70     def reset ; @value = @cache ; end  71     def get_all_fields  14   72         return [self] unless @order  15 class Str < Field  73         @order.each.map {|field| field.get_all_fields }.flatten  16     def to_out  74     end  17         @value  75 end  18     end  76 class Fuzzer  19 end  77     def initialize(field) ; @field = field ; end  20   78     def fuzz(&block)  21 class PreField  79         @field.get_all_fields.each do |child|  22     attr_accessor :name, :klass, :value  80             child.snapshot  23     def initialize(name, klass, value)  81             child.get_fvals.each do |new_val|  24         @name = name  82                 child.value = new_val  25         @klass = klass  83                 block.call(@field)  26         @value = value  84             end  27     end  85             child.reset  28     def create  86         end  29         @klass.new(name, value)  87     end  30     end  88 end  31 end  32   33 class Library < Field  34     class << self  35         attr_accessor :order  36         def field(name, klass, value)  37             @order ||= []  38             @order << PreField.new(name, klass, value)  39         end  40     end # class << self  41     def initialize(*args)  42         super(*args) if args.length == 2  43         @order = []  44         self.class.order.each do |pre_field|  45             self.class.class_eval { attr_accessor pre_field.name }  46             @order << pre_field.create  47             instance_variable_set("@#{pre_field.name}", @order.last)  48         end  49     end  50     def to_out  51         @order.map{|field| field.to_out }.join  52     end  53 end  54   55  08-28-2010 Nephi Johnson 34
    • Putting it all Together > Basic ‣ In these simple examples, I didn't meet these common requirements: ‣ lengths ‣ checksums ‣ basic logic ‣ compression ‣ I'll talk about these next with my project, Funder 08-28-2010 Nephi Johnson 35
    • Putting it all Together > Funder ‣ Funder = Format UNDERstander ‣ essentially a fuller version of the previous example ‣ has: ‣ actions ‣ “unfields” ‣ more syntactic sugar ‣ irb-friendliness ‣ options ‣ offset calculations ‣ arrays ‣ inheritance ‣ field binding ‣ sections ‣ svn co http://funder.googlecode.com/svn/trunk funder 08-28-2010 Nephi Johnson 36
    • ... > Funder > Actions require 'funder' class ActionTest < Funder     int32 :length, action(Length, lambda{[data, base64]})     str :data, "compressed data", :a=>action(ZlibDeflate)     str :base64, action(Base64Encode, lambda{[data]}) end >> a = ActionTest.new => #<ActionTest length=Action:Length(data, base64) data=Action:ZlibDeflate("compressed  data") base64=Action:Base64Encode(data)> >> a.to_out => "0000000007x234K316317­(J­.NMQHI,I0040000351005360eJxLzs8tKEotLk5NUUhJLEkEADDpBfA=" >> a.base64.to_out => "eJxLzs8tKEotLk5NUUhJLEkEADDpBfA=" >> Base64.decode64(a.base64.to_out) => "x234K316317­(J­.NMQHI,I0040000351005360" >> Zlib::Inflate.inflate(Base64.decode64(a.base64.to_out)) => "compressed data" 08-28-2010 Nephi Johnson 37
    • ... > Funder > Arrays & Binding require 'funder' class Item < Funder     byte :id_num     byte :length, action(Length, lambda{[item_name, space]})     str :item_name, lambda{"item_number(##{id_num.value.resolve})"}     str :space, " " end class IndexEntry < Funder     byte :id_num, counter(:index_entry)     int16 :item_offset end class ArrayTest < Funder     array :index, IndexEntry, :b=>bind(lambda{items}, {:item_offset=>:offset})     str :separator, "  ITEMS:  "     array :items, Item, :b=>bind(lambda{index}, {:id_num=>"id_num.value"}) end # (usage on next slide) 08-28-2010 Nephi Johnson 38
    • ... > Funder > Arrays & Binding >> a = ArrayTest.new => #<ArrayTest index=#<IndexEntry[3]> separator="  ITEMS:  " items=#<Item[3]>> >> res = a.to_out => "000000023001000%0020007  ITEMS:  021000item_number(#0)  021001item_number(#1) 021002item_number(#2) " >> item_1_offset = res[4, 2].unpack("n")[0] => 37 >> item_1_length = res[item_1_offset, 1].unpack("c")[0] => 17 >> item_1_data = res[item_1_offset+1, item_1_length] => "001item_number(#1) " 08-28-2010 Nephi Johnson 39
    • ... > Funder > Png Example ‣ Png images are made up of chunks: class Chunk < Funder     int32 :length, action(Length, lambda{[data]})     str :type_code, nil, :min=>4     str :data     int32 :crc, action(Crc32, lambda{[type_code,data]}) end ‣ Specific chunks can inhert from Chunk: class IHDR < Chunk     str :type_code, "IHDR"     section :data do         int32 :width         int32 :height          byte :bit_depth, 8         byte :color_type, Png::COLOR_TYPE_TRUECOLOR         byte :comp_method, 0         byte :filter_method, 0         byte :interlace_method, 0     end  end 08-28-2010 Nephi Johnson 40
    • ... > Funder > Png Example ‣ Final core PNG class: class Png < Funder     str :png_header, "211PNGrn032n"     field :ihdr, IHDR     field :srgb, SRGB     field :idat, IDAT     field :iend, IEND end 08-28-2010 Nephi Johnson 41
    • ... > Funder > Png – In Use >> require 'formats/png' => true >> p = Png.new => #<Png png_header="211PNGrn032n" ihdr=#<IHDR> srgb=#<SRGB> idat=#<IDAT>  iend=#<IEND>> >> p.ihdr.data.width.value = p.ihdr.data.height.value = 200 => 200 >> p.idat.data.red.value = 0xff => 255 >> p.idat.data.green.value = 0 => 0 >> p.idat.data.blue.value = 0 => 0 >> File.open("test.png", "w"){|f| f.write p.to_out } => 696 08-28-2010 Nephi Johnson 42
    • ... > Funder > Fuzzing ‣ My fuzzing code is similar to the example code, but has ‣ more options, control ‣ a form static inheritance to propagate options from classes to subclass instances ‣ random and deterministic fuzzing ‣ svn checkout the source or browse it online ‣ (I'm probably desperately out of time by the time I reach this slide) 08-28-2010 Nephi Johnson 43
    • The End 08-28-2010 Nephi Johnson 44