miketracy-wwmd 0.2.16 → 0.2.17

Sign up to get free protection for your applications and to get access to all the features.
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