less-dumb fuzzing and ruby metaprogramming

44
Less-Dumb Fuzzing & Ruby Metaprogramming Nephi Johnson

Upload: nephi-johnson

Post on 11-May-2015

2.769 views

Category:

Technology


0 download

DESCRIPTION

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

TRANSCRIPT

Page 1: Less-Dumb Fuzzing and Ruby Metaprogramming

Less-Dumb Fuzzing &Ruby Metaprogramming

Nephi Johnson

Page 2: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 2

Introduction‣ Who am I?

‣ Security Researcher at BreakingPoint‣ Why do I like ruby?

‣ flexibility‣ simple‣ fun

Page 3: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 3

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

Page 4: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 4

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

Page 5: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 5

Fuzzing

Page 6: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 6

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

Page 7: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 7

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

Page 8: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 8

… > 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

Page 9: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 9

… > Ways to fuzz > Dumb-Fuzzing

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        …    endend

‣ basic example

Page 10: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 10

… > 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‣ inflexible

‣ generate a class specific to each data-format‣ easier to maintain‣ more flexible

‣ create a re-usable framework for defining the formats‣ easy to maintain‣ very flexible

‣ specific to one format

‣ specific to one format

‣ many formats

Page 11: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 11

Metaprogramming

Page 12: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 12

Metaprogramming‣ Hooks‣ Code that writes code

Page 13: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 13

Metaprogramming > Hooks‣ defined in Module:

‣ const_missing‣ extended‣ included‣ method_added‣ method_removed‣ method_undefined

‣ defined in Class‣ inherited

‣ defined in Kernel:‣ method_missing

‣ defined in Object:‣ singleton_method_added‣ singleton_method_removed‣ singleton_method_undefined

Page 14: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 14

Metaprogramming > Hooksclass InheritMe    def self.inherited(other)        puts "#{other} inherited #{self.class}"    endend

class A < InheritMeend

# ==> A inherited InheritMe

Page 15: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 15

Metaprogramming > Hooksclass MethodMissing    def method_missing(method_name, *args)        puts "#{method_name} called with #{args.inspect}"    endend

m = MethodMissing.newm.nonexistent_method(1, "arg_2", Object.new)

# ==> nonexistent_method called with [1, "arg_2", #<Object:0xb7617594>]

Page 16: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 16

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)

Page 17: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 17

… > Code that ... > via method calls‣ Available method calls:‣ defined in Module:

‣ alias_method‣ class_variable_get/set‣ remove_class_variable‣ const_get/set‣ remove_const‣ extend_object‣ define_method‣ remove_method‣ undef_method

‣ defined in Object:‣ instance_variable_get/set‣ remove_instance_variable‣ send

Page 18: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 18

… > Code that ... > via method callsclass 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!"    endendclass F < Example ; end

f = F.newputs f.subclassed_example?     # ==> possiblyputs f.create_cookiemonster    # ==> #<Cookiemonster:0xb782c26c>

Page 19: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 19

… > Code that ... > via evals‣ Available ways to eval code:‣ defined in Module:

‣ class_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

‣ defined in Object:‣ instance_eval

Page 20: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 20

… > Code that ... > via evals‣ class_eval (aka module_eval)

class ClassEval   class << self   private   def private_static_method       puts "private static method"   end   endend

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.newc.made_by_class_eval               # ==> made by class evald = ClassEval.newd.made_by_class_eval               # ==> made by class eval

Page 21: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 21

… > Code that ... > via evals‣ instance_eval

class InstanceEval    private    def private_method        puts "this is a private method"    endend

i = InstanceEval.newi.instance_eval do    private_method          # ==> this is a private methodend

i.instance_eval do    def new_method        puts "this is a new method"    endendi.new_method                # ==> this is a new methodj = InstanceEval.newj.new_method                # ==> undefined method `new_method' ...

Page 22: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 22

… > Code that ... > via evals‣ eval

class Eval    attr_reader :greeting    def initialize        @greeting = "hello"    end    def greet        puts @greeting    endend

e = Eval.newe.greet               # ==> Hello

# binding is a private methodeval('@greeting = "Glueck Auf"', e.send(:binding))e.greet               # ==> Glueck Auf

e.send(:eval, '@greeting = "Tag"')e.greet               # ==> Tag

Page 23: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 23

… > Code that ... > via evalsclass 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!"    endendclass F < Example ; end

f = F.newputs f.subclassed_example?     # ==> possiblyputs f.create_cookiemonster    # ==> #<Cookiemonster:0xb782c26c>

Page 24: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 24

Putting it all Together

Page 25: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 25

Putting it all Together‣ Basic

‣ functionality‣ usability‣ fuzzing

‣ Funder‣ highlights

‣ actions, sections, arrays, binding‣ fuzzing with Funder

‣ static inheritance, options

Page 26: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 26

Putting it all Together > Basic‣ Goals

‣ Easy to define/modify data-formats‣ Reusable

‣ Common needs‣ lengths‣ checksums‣ basic logic‣ compression‣ enumerable

‣ embedded formats

Page 27: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 27

Putting it all Together > Basic‣ Want it to look something like:class DataFormat < Library    int32 field1 length(field2, field3)    int32 field2 value    string field3 valueend

d = DataFormat.newd.field2 = 100d.field3 = “hello there”...# etc    

‣ Basically, keep it as simple as possible

Page 28: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 28

... > Basic > Functionalityclass Library < Field    class << self        attr_accessor :order        def field(name, value)            @order ||= []            @order << [name, value]        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"enda = A.newputs a.inspect    #==> #<A:0xb77d2f50 @order=[], @field_2="this is field2", @field_1=10>

‣ Create the fields class Field    attr_accessor :name, :value    def initialize(name, value, opts={})        @name = name ; @value = value        @opts = opts    end    def to_out ; endend

Page 29: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 29

... > Basic > Usability

class Library < Field    class << self    def field(name, klass, value, opts={})        @order ||= []        @order << PreField.new(name,klass,                               value, opts)    end    end # class << self    def to_out        @order.map{|field| field.to_out }.join    end    def     def initialize(*args)        super(*args) if args.length == 2        @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    endend

‣ Make it usefulclass PreField    attr_accessor :name, :klass, :value, :opts    def initialize(name, klass, value, opts)        @name = name ; @klass = klass        @value = value ; @opts = opts    end    def create ; @klass.new(@name, @value,                            @opts) ; endend

class Int < Field    def to_out ; [@value].pack(“n”) ; endend

class Str < Field    def to_out ; @value.to_s ; endend

Page 30: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 30

... > Basic > Usabilityrequire 'library'

class A < Library    field :field_1, Int, 10    field :field_2, Str, "this is field2"end

a = A.newputs a.to_out.inspect                 # ==> "\000\nthis is field2"

a.field_1.value = 100a.field_2.value = "field2 changed!"puts a.to_out.inspect                 # ==> “\000dfield2 changed!"

‣ Make it useful – in use

Page 31: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 31

... > 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

Page 32: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 32

... > Basic > Fuzzingrequire '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)            end             child.reset        end     end endclass Field    def snapshot ; @cache = @value ; end     def reset ; @value = @cache ; end     def get_all_fields        return [self] unless @order        @order.each.map {|field| field.get_all_fields }.flatten    end end

class Int < Field    def get_fvals        (0..3).to_a    end endclass Str < Field    def get_fvals        (1..4).map {|i| "A"*(2**i) }    end end

Page 33: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 33

... > Basic > Fuzzingrequire 'library'

class A < Library    field :field_1, Int, 0xffff    field :field_2, Str, "this is field2"end

a = A.newf = Fuzzer.new(a)f.fuzz do |new_a|    puts new_a.to_out.inspectend

‣ Fuzzer – in use

      "\000\000this is field2"      "\000\001this is field2"# ==> "\000\002this is field2"      "\000\003this is field2"      "\377\377AA"      "\377\377AAAA"      "\377\377AAAAAAAA"      "\377\377AAAAAAAAAAAAAAAA"

‣ 100 total lines of code!‣ (see next slide)

Page 34: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 34

... > Basic > Fuzzing > 100 lines# library.rb

  1 class Field  2     attr_accessor :name, :value  3     def initialize(name, value)  4         @name = name ; @value = value  5     end  6     def to_out ; end  7 end  8   9 class Int < Field 10     def to_out 11         [@value].pack("n") 12     end 13 end 14  15 class Str < Field 16     def to_out 17         @value 18     end 19 end 20  21 class PreField 22     attr_accessor :name, :klass, :value 23     def initialize(name, klass, value) 24         @name = name 25         @klass = klass 26         @value = value 27     end 28     def create 29         @klass.new(name, value) 30     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 

 56 # FUZZing 57  58 class Int < Field 59     def get_fvals 60         (0..3).to_a 61     end 62 end 63 class Str < Field 64     def get_fvals 65         (1..4).map {|i| "A" * (2**i) } 66     end 67 end 68 class Field 69     def snapshot ; @cache = @value ; end 70     def reset ; @value = @cache ; end 71     def get_all_fields 72         return [self] unless @order 73         @order.each.map {|field| field.get_all_fields }.flatten 74     end 75 end 76 class Fuzzer 77     def initialize(field) ; @field = field ; end 78     def fuzz(&block) 79         @field.get_all_fields.each do |child| 80             child.snapshot 81             child.get_fvals.each do |new_val| 82                 child.value = new_val 83                 block.call(@field) 84             end 85             child.reset 86         end 87     end 88 end

# test.rb

  1 require 'library'  2   3 class A < Library  4     field :field_1, Int, 0xffff  5     field :field_2, Str, "this is field2"  6 end  7   8 a = A.new  9 f = Fuzzer.new(a) 10 f.fuzz do |new_a| 11     puts new_a.to_out.inspect 12 end

Page 35: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 35

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

Page 36: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 36

Putting it all Together > Funder‣ Funder = Format UNDERstander

‣ essentially a fuller version of the previous example‣ has:

‣ actions‣ more syntactic sugar‣ options‣ arrays‣ field binding‣ sections

‣ svn co http://funder.googlecode.com/svn/trunk funder

‣ “unfields”‣ irb-friendliness‣ offset calculations‣ inheritance

Page 37: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 37

... > Funder > Actionsrequire '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=> "\000\000\0007x\234K\316\317­(J­.NMQHI,I\004\0000\351\005\360eJxLzs8tKEotLk5NUUhJLEkEADDpBfA="

>> a.base64.to_out=> "eJxLzs8tKEotLk5NUUhJLEkEADDpBfA="

>> Base64.decode64(a.base64.to_out)=> "x\234K\316\317­(J­.NMQHI,I\004\0000\351\005\360"

>> Zlib::Inflate.inflate(Base64.decode64(a.base64.to_out))=> "compressed data"

Page 38: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 38

... > Funder > Arrays & Bindingrequire '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_offsetend

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)

Page 39: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 39

... > Funder > Arrays & Binding>> a = ArrayTest.new=> #<ArrayTest index=#<IndexEntry[3]> separator="  ITEMS:  " items=#<Item[3]>>

>> res = a.to_out=> "\000\000\023\001\000%\002\0007  ITEMS:  \021\000item_number(#0) \021\001item_number(#1) \021\002item_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) "

Page 40: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 40

... > 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

Page 41: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 41

... > Funder > Png Example‣ Final core PNG class:

class Png < Funder    str :png_header, "\211PNG\r\n\032\n"    field :ihdr, IHDR    field :srgb, SRGB    field :idat, IDAT    field :iend, IENDend

Page 42: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 42

... > Funder > Png – In Use>> require 'formats/png'=> true

>> p = Png.new=> #<Png png_header="\211PNG\r\n\032\n" 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

Page 43: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 43

... > 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)

Page 44: Less-Dumb Fuzzing and Ruby Metaprogramming

08-28-2010 Nephi Johnson 44

The End