Upload
nephi-johnson
View
2.769
Download
0
Tags:
Embed Size (px)
DESCRIPTION
A talk I gave at the Lone Star Ruby Conference, Aug 28 2010
Citation preview
Less-Dumb Fuzzing &Ruby Metaprogramming
Nephi Johnson
08-28-2010 Nephi Johnson 2
Introduction‣ Who am I?
‣ Security Researcher at BreakingPoint‣ Why do I like ruby?
‣ flexibility‣ simple‣ fun
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
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
08-28-2010 Nephi Johnson 5
Fuzzing
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
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
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
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
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
08-28-2010 Nephi Johnson 11
Metaprogramming
08-28-2010 Nephi Johnson 12
Metaprogramming‣ Hooks‣ Code that writes code
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
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
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>]
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)
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
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>
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
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
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' ...
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
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>
08-28-2010 Nephi Johnson 24
Putting it all Together
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
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
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
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
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
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
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
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
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)
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
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
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
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"
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)
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) "
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
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
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
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)
08-28-2010 Nephi Johnson 44
The End