miketracy-wwmd 0.2.16 → 0.2.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. data/History.txt +21 -0
  2. data/{README → README.rdoc} +27 -2
  3. data/lib/wwmd.rb +4 -4
  4. data/lib/wwmd/class_extensions.rb +2 -0
  5. data/lib/wwmd/{mixins.rb → class_extensions/extensions_base.rb} +25 -121
  6. data/lib/wwmd/class_extensions/extensions_encoding.rb +79 -0
  7. data/lib/wwmd/{mixins_external.rb → class_extensions/extensions_external.rb} +0 -0
  8. data/lib/wwmd/class_extensions/extensions_nilclass.rb +11 -0
  9. data/lib/wwmd/{mixins_extends.rb → class_extensions/extensions_rbkb.rb} +0 -0
  10. data/lib/wwmd/{encoding.rb → class_extensions/mixins_string_encoding.rb} +6 -6
  11. data/lib/wwmd/page.rb +3 -245
  12. data/lib/wwmd/page/auth.rb +0 -166
  13. data/lib/wwmd/page/constants.rb +7 -4
  14. data/lib/wwmd/page/form.rb +0 -15
  15. data/lib/wwmd/page/form_array.rb +96 -74
  16. data/lib/wwmd/page/headers.rb +25 -21
  17. data/lib/wwmd/page/helpers.rb +30 -0
  18. data/lib/wwmd/{hpricot_html2text.rb → page/html2text_hpricot.rb} +1 -1
  19. data/lib/wwmd/{nokogiri_html2text.rb → page/html2text_nokogiri.rb} +0 -0
  20. data/lib/wwmd/page/inputs.rb +1 -1
  21. data/lib/wwmd/page/irb_helpers.rb +37 -13
  22. data/lib/wwmd/page/page.rb +238 -0
  23. data/lib/wwmd/page/parsing_convenience.rb +8 -3
  24. data/lib/wwmd/page/scrape.rb +15 -19
  25. data/lib/wwmd/page/spider.rb +11 -11
  26. data/lib/wwmd/urlparse.rb +20 -5
  27. data/lib/wwmd/viewstate.rb +9 -112
  28. data/lib/wwmd/viewstate/viewstate.rb +101 -0
  29. data/lib/wwmd/viewstate/viewstate_deserializer_methods.rb +36 -36
  30. data/lib/wwmd/viewstate/viewstate_types.rb +0 -4
  31. data/lib/wwmd/viewstate/viewstate_utils.rb +6 -1
  32. data/lib/wwmd/viewstate/vs_stubs.rb +22 -0
  33. data/lib/wwmd/viewstate/{vs_array.rb → vs_stubs/vs_array.rb} +3 -1
  34. data/lib/wwmd/viewstate/{vs_binary_serialized.rb → vs_stubs/vs_binary_serialized.rb} +3 -1
  35. data/lib/wwmd/viewstate/{vs_hashtable.rb → vs_stubs/vs_hashtable.rb} +3 -1
  36. data/lib/wwmd/viewstate/{vs_hybrid_dict.rb → vs_stubs/vs_hybrid_dict.rb} +3 -1
  37. data/lib/wwmd/viewstate/{vs_indexed_string.rb → vs_stubs/vs_indexed_string.rb} +1 -1
  38. data/lib/wwmd/viewstate/{vs_indexed_string_ref.rb → vs_stubs/vs_indexed_string_ref.rb} +3 -1
  39. data/lib/wwmd/viewstate/{vs_int_enum.rb → vs_stubs/vs_int_enum.rb} +3 -1
  40. data/lib/wwmd/viewstate/{vs_list.rb → vs_stubs/vs_list.rb} +2 -1
  41. data/lib/wwmd/viewstate/{vs_pair.rb → vs_stubs/vs_pair.rb} +3 -1
  42. data/lib/wwmd/viewstate/vs_stubs/vs_read_types.rb +11 -0
  43. data/lib/wwmd/viewstate/{vs_read_value.rb → vs_stubs/vs_read_value.rb} +3 -1
  44. data/lib/wwmd/viewstate/{vs_sparse_array.rb → vs_stubs/vs_sparse_array.rb} +3 -1
  45. data/lib/wwmd/viewstate/{vs_string.rb → vs_stubs/vs_string.rb} +2 -1
  46. data/lib/wwmd/viewstate/{vs_string_array.rb → vs_stubs/vs_string_array.rb} +4 -2
  47. data/lib/wwmd/viewstate/{vs_string_formatted.rb → vs_stubs/vs_string_formatted.rb} +4 -2
  48. data/lib/wwmd/viewstate/{viewstate_class_helpers.rb → vs_stubs/vs_stub_helpers.rb} +2 -1
  49. data/lib/wwmd/viewstate/{vs_triplet.rb → vs_stubs/vs_triplet.rb} +3 -1
  50. data/lib/wwmd/viewstate/{vs_type.rb → vs_stubs/vs_type.rb} +3 -1
  51. data/lib/wwmd/viewstate/{vs_unit.rb → vs_stubs/vs_unit.rb} +3 -1
  52. data/lib/wwmd/viewstate/{vs_value.rb → vs_stubs/vs_value.rb} +4 -2
  53. data/lib/wwmd/wwmd_config.rb +44 -36
  54. data/lib/wwmd/wwmd_puts.rb +9 -0
  55. data/lib/wwmd/wwmd_utils.rb +22 -24
  56. data/tasks/setup.rb +1 -1
  57. metadata +41 -35
  58. data/README.txt +0 -62
  59. data/lib/wwmd/viewstate/vs_read_types.rb +0 -11
  60. data/wwmd.gemspec +0 -0
@@ -25,58 +25,62 @@ module WWMD
25
25
  #
26
26
  # if clear == true then headers will be cleared before setting
27
27
  def set_headers(arg=nil,clear=false)
28
- self.clear_headers if clear
28
+ clear_headers if clear
29
29
  if arg.nil?
30
30
  begin
31
- self.clear_headers
31
+ clear_headers
32
32
  WWMD::DEFAULT_HEADERS.each { |k,v| self.headers[k] = v }
33
33
  return "headers set from default"
34
34
  rescue => e
35
- puts e
36
- return "error setting headers"
35
+ putw "WARN: " + e
36
+ return false
37
37
  end
38
38
  elsif arg.class == Symbol
39
- self.set_headers(WWMD::HEADERS[arg])
40
- return "headers set from #{arg}"
39
+ set_headers(WWMD::HEADERS[arg])
40
+ putw "headers set from #{arg}"
41
+ return true
41
42
  elsif arg.class == Hash
42
43
  arg.each { |k,v| self.headers[k] = v }
43
- return "headers set from hash"
44
+ putw "headers set from hash"
45
+ return true
44
46
  end
45
- "error setting headers"
47
+ putw "error setting headers"
48
+ return false
46
49
  end
47
50
 
48
51
  # set headers back to default headers
49
52
  def default_headers(arg=nil)
50
- self.set_headers
53
+ set_headers
51
54
  end
52
55
 
53
56
  alias_method :set_default, :default_headers
54
57
 
55
58
  # set headers from text
56
59
  def headers_from_array(arr)
57
- self.clear_headers
60
+ clear_headers
58
61
  arr.each do |line|
59
- h = line.split(":",2)
60
- next if h[1].class != String
61
- self.headers[h[0]] = h[1].strip
62
+ next if (line.empty? || line =~ /^(GET|POST)/)
63
+ k,v = line.split(":",2)
64
+ self.headers[k.strip] = v.strip
62
65
  end
66
+ nil
67
+ end
68
+
69
+ # set headers from paste
70
+ def headers_from_paste
71
+ headers_from_array(%x[pbpaste])
63
72
  end
64
73
 
65
74
  # set headers from file
66
75
  def headers_from_file(fn)
67
- self.clear_headers
68
- File.read(fn).each do |line|
69
- h = line.split(":",2)
70
- next if h[1].class != String
71
- self.headers[h[0]] = h[1].strip
72
- end
73
- return nil
76
+ headers_from_array(File.read(fn).split("\n"))
77
+ return "headers set from #{fn}"
74
78
  end
75
79
 
76
80
  # set headers to utf7 encoding post
77
81
  def set_utf7_headers
78
82
  self.headers["Content-Type"] = "application/x-www-form-urlencoded;charset=UTF-7"
79
- "headers set to utf7"
83
+ return "headers set to utf7"
80
84
  end
81
85
 
82
86
  # set headers to ajax
@@ -0,0 +1,30 @@
1
+ module WWMD
2
+ class Page
3
+ # copy and paste from burp request windows
4
+ # page object gets set with headers and url (not correct)
5
+ # returns [headers,form]
6
+ # form = page.from_paste
7
+ def from_paste
8
+ self.enable_cookies = false
9
+ req = %x[pbpaste]
10
+ return false if not req
11
+ h,b = req.chomp.split("\r\n\r\n",2)
12
+ oh = h
13
+ h = h.split("\r\n")
14
+ m,u,p = h.shift.split(" ")
15
+ return nil unless m =~ (/^(POST|GET)/)
16
+ self.url = self.base_url + u
17
+ self.headers_from_array(h)
18
+ self.body_data = b
19
+ self.set_data
20
+ form = b.to_form
21
+ form.action = @urlparse.parse(self.base_url, u).to_s
22
+ [oh,form]
23
+ end
24
+
25
+ def resp_paste
26
+ self.body_data = %x[pbpaste].split("\r\n\r\n",2)[1]
27
+ self.set_data
28
+ end
29
+ end
30
+ end
@@ -45,7 +45,7 @@ module WWMD
45
45
  s += node_to_text(c)
46
46
  end
47
47
  rescue => e
48
- # puts "WARNING: #{e.inspect}"
48
+ putw "WARN: #{e.inspect}"
49
49
  end
50
50
  return s
51
51
  end
@@ -10,7 +10,7 @@ module WWMD
10
10
  end
11
11
 
12
12
  def show
13
- puts @elems
13
+ putx @elems
14
14
  end
15
15
 
16
16
  # call me from Page.set_data
@@ -16,14 +16,14 @@ module WWMD
16
16
 
17
17
  # IRB: text report what has been parsed from this page
18
18
  def report(short=nil)
19
- putx "-------------------------------------------------"
19
+ puts "-------------------------------------------------"
20
20
  self.summary
21
- putx "---- links found [#{self.has_links?.to_s} | #{self.links.size}]"
22
- self.links.each_index { |i| putx "#{i.to_s} :: #{@links[i]}" } if short.nil?
23
- putx "---- javascript found [#{self.has_jlinks?.to_s} | #{self.jlinks.size}]"
24
- self.jlinks.each { |url| putx url } if short.nil?
25
- putx "---- forms found [#{self.has_form?.to_s} | #{self.forms.size}]"
26
- putx "---- comments found [#{self.has_comments?.to_s}]"
21
+ puts "---- links found [#{self.has_links?.to_s} | #{self.links.size}]"
22
+ self.links.each_index { |i| puts "#{i.to_s} :: #{@links[i]}" } if short.nil?
23
+ puts "---- javascript found [#{self.has_jlinks?.to_s} | #{self.jlinks.size}]"
24
+ self.jlinks.each { |url| puts url } if short.nil?
25
+ puts "---- forms found [#{self.has_form?.to_s} | #{self.forms.size}]"
26
+ puts "---- comments found [#{self.has_comments?.to_s}]"
27
27
  return nil
28
28
  end
29
29
 
@@ -32,13 +32,13 @@ module WWMD
32
32
  # IRB: display summary of what has been parsed from this page
33
33
  def summary
34
34
  status = self.page_status
35
- putx "XXXX[#{self.report_flags}] | #{self.response_code.to_s} | #{status} | #{self.url} | #{self.size}"
35
+ puts "XXXX[#{self.report_flags}] | #{self.response_code.to_s} | #{status} | #{self.url} | #{self.size}"
36
36
  return nil
37
37
  end
38
38
 
39
39
  # IRB: display current headers
40
40
  def request_headers
41
- self.headers.each_pair { |k,v| putx "#{k}: #{v}" }
41
+ self.headers.each_pair { |k,v| puts "#{k}: #{v}" }
42
42
  return nil
43
43
  end
44
44
 
@@ -47,7 +47,7 @@ module WWMD
47
47
 
48
48
  # IRB: display response headers
49
49
  def response_headers
50
- self.header_data.each { |x| putx "#{x[0]} :: #{x[1]}" }
50
+ self.header_data.each { |x| puts "#{x[0]} :: #{x[1]}" }
51
51
  return nil
52
52
  end
53
53
 
@@ -55,18 +55,18 @@ module WWMD
55
55
 
56
56
  # display self.body_data
57
57
  def dump_body
58
- putx self.body_data
58
+ puts self.body_data
59
59
  end
60
60
 
61
61
  alias_method :dump, :dump_body#:nodoc:
62
62
 
63
63
  # IRB: puts the page filtered through html2text
64
- def to_text; putx self.html2text; end
64
+ def to_text; puts self.html2text; end
65
65
  def text; self.html2text; end
66
66
 
67
67
  # IRB: display a human readable report of all forms contained in page.body_data
68
68
  def all_forms
69
- self.forms.each_index { |x| putx "[#{x.to_s}]-------"; self.forms[x].report }
69
+ self.forms.each_index { |x| puts "[#{x.to_s}]-------"; self.forms[x].report }
70
70
  nil
71
71
  end
72
72
 
@@ -87,4 +87,28 @@ module WWMD
87
87
  %x[open #{fn}]
88
88
  end
89
89
  end
90
+
91
+ class Form
92
+ def report
93
+ return nil if not WWMD::console
94
+ puts "action = #{self.action}"
95
+ self.fields.each { |field| puts field.to_text }
96
+ return nil
97
+ end
98
+ alias_method :show, :report
99
+ end
100
+
101
+ class FormArray
102
+ # IRB: puts the form in human readable format
103
+ # if you <tt>form.show(true)</tt> it will show unescaped values
104
+ def show(unescape=false)
105
+ if unescape
106
+ self.each_index { |i| puts i.to_s + " :: " + self[i][0].to_s + " = " + self[i][1].to_s.unescape }
107
+ else
108
+ self.each_index { |i| puts i.to_s + " :: " + self[i][0].to_s + " = " + self[i][1].to_s }
109
+ end
110
+ return nil
111
+ end
112
+
113
+ end
90
114
  end
@@ -0,0 +1,238 @@
1
+ module WWMD
2
+ attr_accessor :curl_object
3
+ attr_accessor :body_data
4
+ attr_accessor :post_data
5
+ attr_accessor :header_data
6
+ attr_accessor :use_referer
7
+ attr_reader :forms
8
+ attr_reader :last_error
9
+ attr_reader :links # array of links (urls)
10
+ attr_reader :jlinks # array of included javascript files
11
+ attr_reader :spider # spider object
12
+ attr_reader :scrape # scrape object
13
+ attr_reader :urlparse # urlparse object
14
+ attr_reader :comments
15
+
16
+ attr_accessor :base_url # needed to properly munge relative urls into fq urls
17
+ attr_accessor :logged_in # are we logged in?
18
+
19
+ attr_accessor :opts
20
+ attr_accessor :inputs
21
+
22
+ # WWMD::Page is an extension of a Curl::Easy object which provides methods to
23
+ # enhance and ease the performance of web application penetration testing.
24
+ class Page
25
+
26
+ include WWMDUtils
27
+
28
+ def initialize(opts={}, &block)
29
+ @opts = opts.clone
30
+ DEFAULTS.each { |k,v| @opts[k] = v unless opts[k] }
31
+ @spider = Spider.new(opts)
32
+ @scrape = Scrape.new
33
+ @base_url ||= opts[:base_url]
34
+ @scrape.warn = opts[:scrape_warn] if !opts[:scrape_warn].nil? # yeah yeah... bool false
35
+ @urlparse = URLParse.new()
36
+ @inputs = Inputs.new(self)
37
+ @logged_in = false
38
+ @body_data = ""
39
+ @post_data = ""
40
+ @comments = []
41
+ @header_data = FormArray.new
42
+
43
+ @curl_object = Curl::Easy.new
44
+ @opts.each do |k,v|
45
+ next if k == :proxy_url
46
+ self.instance_variable_set("@#{k.to_s}".intern,v)
47
+ if (@curl_object.methods.include?("#{k}="))
48
+ @curl_object.send("#{k}=",v)
49
+ end
50
+ end
51
+ @curl_object.on_body { |data| self._body_cb(data) }
52
+ @curl_object.on_header { |data| self._header_cb(data) }
53
+
54
+ # cookies?
55
+ @curl_object.enable_cookies = @opts[:enable_cookies]
56
+ if @curl_object.enable_cookies?
57
+ @curl_object.cookiejar = @opts[:cookiejar] || "./__cookiejar"
58
+ end
59
+
60
+ #proxy?
61
+ @curl_object.proxy_url = @opts[:proxy_url] if @opts[:use_proxy]
62
+ instance_eval(&block) if block_given?
63
+ if opts.empty? && @scrape.warn
64
+ putw "Page initialized without opts"
65
+ @scrape.warn = false
66
+ end
67
+ end
68
+
69
+ #:section: Heavy Lifting
70
+
71
+ # set reporting data for the page
72
+ #
73
+ # Scan for comments, anchors, links and javascript includes and
74
+ # set page flags. The heavy lifting for parsing is done in the
75
+ # scrape class.
76
+ #
77
+ # returns: <tt>array [ code, page_status, body_data.size ]</tt>
78
+ def set_data
79
+ # reset scrape and inputs object
80
+ # transparently gunzip
81
+ begin
82
+ io = StringIO.new(self.body_data)
83
+ gz = Zlib::GzipReader.new(io)
84
+ self.body_data.replace(gz.read)
85
+ rescue => e
86
+ end
87
+ @scrape.reset(self.body_data)
88
+ @inputs.set
89
+
90
+ # remove comments that are css selectors for IE silliness
91
+ @comments = @scrape.for_comments.reject do |c|
92
+ c =~ /\[if IE\]/ ||
93
+ c =~ /\[if IE \d/ ||
94
+ c =~ /\[if lt IE \d/
95
+ end
96
+ @links = @scrape.for_links.map do |url|
97
+ @urlparse.parse(self.last_effective_url,url).to_s
98
+ end
99
+ @jlinks = @scrape.for_javascript_links
100
+ @forms = @scrape.for_forms
101
+ @spider.add(self.last_effective_url,@links)
102
+ return [self.code,self.body_data.size]
103
+ end
104
+
105
+ # clear self.body_data and self.header_data
106
+ def clear_data
107
+ return false if self.opts[:parse] = false
108
+ @body_data = ""
109
+ @header_data.clear
110
+ @last_error = nil
111
+ end
112
+
113
+ # override Curl::Easy.perform to perform page actions,
114
+ # call <tt>self.set_data</tt>
115
+ #
116
+ # returns: <tt>array [ code, page_status, body_data.size ]</tt>
117
+ #
118
+ # don't call this directly if we are in console mode
119
+ # use get and submit respectively for GET and POST
120
+ def perform
121
+ self.clear_data
122
+ self.headers["Referer"] = self.cur if self.use_referer
123
+ begin
124
+ @curl_object.perform
125
+ rescue => e
126
+ @last_error = e
127
+ putw "WARN: #{e.class}" if e.class =~ /Curl::Err/
128
+ end
129
+ self.set_data
130
+ end
131
+
132
+ # replacement for Curl::Easy.http_post
133
+ #
134
+ # post the form attempting to remove curl supplied headers (Expect, X-Forwarded-For
135
+ # call <tt>self.set_data</tt>
136
+ #
137
+ # if passed a regexp, escape values in the form using regexp before submitting
138
+ # if passed nil for the regexp arg, the form will not be escaped
139
+ # default: WWMD::ESCAPE[:url]
140
+ #
141
+ # returns: <tt>array [ code, body_data.size ]</tt>
142
+ def submit(iform=nil,reg=WWMD::ESCAPE[:default])
143
+ ## this is just getting worse and worse
144
+ if iform.class == "Symbol"
145
+ reg = iform
146
+ iform = nil
147
+ end
148
+ self.clear_data
149
+ ["Expect","X-Forwarded-For","Content-length"].each { |s| self.clear_header(s) }
150
+ self.headers["Referer"] = self.cur if self.use_referer
151
+ unless iform
152
+ unless self.form.empty?
153
+ sform = self.form.clone
154
+ else
155
+ return "no form provided"
156
+ end
157
+ else
158
+ sform = iform.clone # clone the form so that we don't change the original
159
+ end
160
+ sform.escape_all!(reg)
161
+ self.url = sform.action if sform.action
162
+ if sform.empty?
163
+ self.http_post('')
164
+ else
165
+ self.http_post(self.post_data = sform.to_post)
166
+ end
167
+ self.set_data
168
+ end
169
+
170
+ # submit a form using POST string
171
+ def submit_string(post_string)
172
+ self.clear_data
173
+ self.http_post(post_string)
174
+ putw "WARN: authentication headers in response" if self.auth?
175
+ self.set_data
176
+ end
177
+
178
+ # override for Curl::Easy.perform
179
+ #
180
+ # if the passed url string doesn't contain an fully qualified
181
+ # path, we'll guess and prepend opts[:base_url]
182
+ #
183
+ # returns: <tt>array [ code, body_data.size ]</tt>
184
+ def get(url=nil,parse=true)
185
+ if !(url =~ /[a-z]+:\/\//) && parse
186
+ self.url = @urlparse.parse(self.opts[:base_url],url).to_s if url
187
+ elsif url
188
+ self.url = url
189
+ end
190
+ self.perform
191
+ putw "WARN: authentication headers in response" if self.auth?
192
+ self.set_data
193
+ end
194
+
195
+ # GET with params and POST it as a form
196
+ def post(url=nil)
197
+ ep = url.clip
198
+ self.url = @urlparse.parse(self.opts[:base_url],ep).to_s if ep
199
+ form = url.clop.to_form
200
+ self.submit(form)
201
+ end
202
+
203
+ # send arbitrary verb (only works with patch to taf2-curb
204
+ def verb(verb,url=nil)
205
+ return false if !@curl_object.respond_to?(:http_verb)
206
+ self.url = url if url
207
+ self.clear_data
208
+ self.headers["Referer"] = self.cur if self.use_referer
209
+ self.http_verb(verb)
210
+ self.set_data
211
+ end
212
+
213
+ #:section: Data callbacks and method_missing
214
+
215
+ # callback for <tt>self.on_body</tt>
216
+ def _body_cb(data)
217
+ @body_data << data if data
218
+ return data.length.to_i
219
+ end
220
+
221
+ # callback for <tt>self.on_header</tt>
222
+ def _header_cb(data)
223
+ myArr = Array.new(data.split(":",2))
224
+ @header_data.extend! myArr[0].to_s.strip,myArr[1].to_s.strip
225
+ return data.length.to_i
226
+ end
227
+
228
+ # send methods not defined here to <tt>@curl_object</tt>
229
+ def method_missing(methodname, *args)
230
+ if WWMD.respond_to?(methodname)
231
+ WWMD.send(methodname, *args)
232
+ else
233
+ @curl_object.send(methodname, *args)
234
+ end
235
+ end
236
+
237
+ end
238
+ end