| 1 |
diff -Naru --exclude=.svn --exclude='*~' tarantula.orig/lib/relevance/tarantula/crawler.rb tarantula.xss_check/lib/relevance/tarantula/crawler.rb |
|---|
| 2 |
--- tarantula.orig/lib/relevance/tarantula/crawler.rb 2008-05-04 15:09:03.000000000 -0400 |
|---|
| 3 |
+++ tarantula.xss_check/lib/relevance/tarantula/crawler.rb 2008-05-04 16:25:18.000000000 -0400 |
|---|
| 4 |
@@ -7,7 +7,8 @@ |
|---|
| 5 |
|
|---|
| 6 |
attr_accessor :proxy, :handlers, :skip_uri_patterns, :log_grabber, |
|---|
| 7 |
:reporters, :links_to_crawl, :links_queued, :forms_to_crawl, |
|---|
| 8 |
- :form_signatures_queued, :max_url_length, :response_code_handler |
|---|
| 9 |
+ :form_signatures_queued, :max_url_length, :response_code_handler, |
|---|
| 10 |
+ :times_to_crawl, :fuzzers |
|---|
| 11 |
attr_reader :transform_url_patterns, :referrers, :failures, :successes |
|---|
| 12 |
|
|---|
| 13 |
def initialize |
|---|
| 14 |
@@ -30,7 +31,8 @@ |
|---|
| 15 |
] |
|---|
| 16 |
@reporters = [Relevance::Tarantula::IOReporter.new($stderr)] |
|---|
| 17 |
@decoder = HTMLEntities.new |
|---|
| 18 |
- |
|---|
| 19 |
+ @times_to_crawl = 1 |
|---|
| 20 |
+ @fuzzers = [Relevance::Tarantula::FormSubmission] |
|---|
| 21 |
end |
|---|
| 22 |
|
|---|
| 23 |
def method_missing(meth, *args) |
|---|
| 24 |
@@ -45,8 +47,18 @@ |
|---|
| 25 |
end |
|---|
| 26 |
|
|---|
| 27 |
def crawl(url = "/") |
|---|
| 28 |
- queue_link url |
|---|
| 29 |
- do_crawl |
|---|
| 30 |
+ @times_to_crawl.times do |i| |
|---|
| 31 |
+ queue_link url |
|---|
| 32 |
+ do_crawl |
|---|
| 33 |
+ |
|---|
| 34 |
+ puts "#{(i+1).ordinalize} crawl" if @times_to_crawl > 1 |
|---|
| 35 |
+ |
|---|
| 36 |
+ if i + 1 < @times_to_crawl |
|---|
| 37 |
+ @links_queued = Set.new |
|---|
| 38 |
+ @form_signatures_queued = Set.new |
|---|
| 39 |
+ @referrers = {} |
|---|
| 40 |
+ end |
|---|
| 41 |
+ end |
|---|
| 42 |
rescue Interrupt |
|---|
| 43 |
$stderr.puts "CTRL-C" |
|---|
| 44 |
ensure |
|---|
| 45 |
@@ -165,12 +177,16 @@ |
|---|
| 46 |
end |
|---|
| 47 |
|
|---|
| 48 |
def queue_form(form, referrer = nil) |
|---|
| 49 |
- fs = FormSubmission.new(Form.new(form)) |
|---|
| 50 |
- fs.action = transform_url(fs.action) |
|---|
| 51 |
- return if should_skip_form_submission?(fs) |
|---|
| 52 |
- @referrers[fs.action] = referrer if referrer |
|---|
| 53 |
- @forms_to_crawl << fs |
|---|
| 54 |
- @form_signatures_queued << fs.signature |
|---|
| 55 |
+ fuzzers.each do |fuzzer| |
|---|
| 56 |
+ fuzzer.mutate(Form.new(form)).each do |fs| |
|---|
| 57 |
+ # fs = fuzzer.new(Form.new(form)) |
|---|
| 58 |
+ fs.action = transform_url(fs.action) |
|---|
| 59 |
+ return if should_skip_form_submission?(fs) |
|---|
| 60 |
+ @referrers[fs.action] = referrer if referrer |
|---|
| 61 |
+ @forms_to_crawl << fs |
|---|
| 62 |
+ @form_signatures_queued << fs.signature |
|---|
| 63 |
+ end |
|---|
| 64 |
+ end |
|---|
| 65 |
end |
|---|
| 66 |
|
|---|
| 67 |
def report_dir |
|---|
| 68 |
diff -Naru --exclude=.svn --exclude='*~' tarantula.orig/lib/relevance/tarantula/form_submission.rb tarantula.xss_check/lib/relevance/tarantula/form_submission.rb |
|---|
| 69 |
--- tarantula.orig/lib/relevance/tarantula/form_submission.rb 2008-05-04 15:08:57.000000000 -0400 |
|---|
| 70 |
+++ tarantula.xss_check/lib/relevance/tarantula/form_submission.rb 2008-05-04 14:22:35.000000000 -0400 |
|---|
| 71 |
@@ -6,6 +6,10 @@ |
|---|
| 72 |
@data = mutate_selects(form).merge(mutate_text_areas(form)).merge(mutate_inputs(form)) |
|---|
| 73 |
end |
|---|
| 74 |
|
|---|
| 75 |
+ def self.mutate(form) |
|---|
| 76 |
+ [self.new(form)] |
|---|
| 77 |
+ end |
|---|
| 78 |
+ |
|---|
| 79 |
def to_s |
|---|
| 80 |
"#{action} #{method} #{data.inspect}" |
|---|
| 81 |
end |
|---|
| 82 |
diff -Naru --exclude=.svn --exclude='*~' tarantula.orig/lib/relevance/tarantula/html_report_helper.rb tarantula.xss_check/lib/relevance/tarantula/html_report_helper.rb |
|---|
| 83 |
--- tarantula.orig/lib/relevance/tarantula/html_report_helper.rb 2008-05-03 18:49:16.000000000 -0400 |
|---|
| 84 |
+++ tarantula.xss_check/lib/relevance/tarantula/html_report_helper.rb 2008-05-03 19:33:46.000000000 -0400 |
|---|
| 85 |
@@ -1,4 +1,6 @@ |
|---|
| 86 |
+require "erb" |
|---|
| 87 |
module Relevance::Tarantula::HtmlReportHelper |
|---|
| 88 |
+ include ERB::Util |
|---|
| 89 |
include Relevance::Tarantula |
|---|
| 90 |
def wrap_in_line_number_table(text, &blk) |
|---|
| 91 |
x = Builder::XmlMarkup.new |
|---|
| 92 |
@@ -43,12 +45,12 @@ |
|---|
| 93 |
|
|---|
| 94 |
def wrap_stack_trace_line(text) |
|---|
| 95 |
if text =~ %r{^\s*(/[^:]+):(\d+):([^:]+)$} |
|---|
| 96 |
- file = $1.to_s_xss_protected |
|---|
| 97 |
+ file = h($1) # .to_s_xss_protected |
|---|
| 98 |
line_number = $2 |
|---|
| 99 |
- message = $3.to_s_xss_protected |
|---|
| 100 |
- "<a href='#{textmate_url(file, line_number)}'>#{file}:#{line_number}</a>:#{message}".mark_as_xss_protected |
|---|
| 101 |
+ message = h($3) # .to_s_xss_protected |
|---|
| 102 |
+ "<a href='#{textmate_url(file, line_number)}'>#{file}:#{line_number}</a>:#{message}" # .mark_as_xss_protected |
|---|
| 103 |
else |
|---|
| 104 |
- text.to_s_xss_protected |
|---|
| 105 |
+ h(text) # .to_s_xss_protected |
|---|
| 106 |
end |
|---|
| 107 |
end |
|---|
| 108 |
end |
|---|
| 109 |
diff -Naru --exclude=.svn --exclude='*~' tarantula.orig/lib/relevance/tarantula/xss_document_checker_handler.rb tarantula.xss_check/lib/relevance/tarantula/xss_document_checker_handler.rb |
|---|
| 110 |
--- tarantula.orig/lib/relevance/tarantula/xss_document_checker_handler.rb 1969-12-31 19:00:00.000000000 -0500 |
|---|
| 111 |
+++ tarantula.xss_check/lib/relevance/tarantula/xss_document_checker_handler.rb 2008-05-04 20:46:50.000000000 -0400 |
|---|
| 112 |
@@ -0,0 +1,35 @@ |
|---|
| 113 |
+require 'hpricot' |
|---|
| 114 |
+ |
|---|
| 115 |
+class Relevance::Tarantula::XssDocumentCheckerHandler |
|---|
| 116 |
+ |
|---|
| 117 |
+ def initialize(attacks) |
|---|
| 118 |
+ @attacks = attacks |
|---|
| 119 |
+ @regexp = '(' + attacks.map {|a| Regexp.escape a['code']}.join('|') + ')' |
|---|
| 120 |
+ end |
|---|
| 121 |
+ |
|---|
| 122 |
+ def handle(result) |
|---|
| 123 |
+ response = result.response |
|---|
| 124 |
+ return unless response.html? |
|---|
| 125 |
+ if n = (response.body =~ /#{@regexp}/) |
|---|
| 126 |
+ error_result = result.dup |
|---|
| 127 |
+ error_result.success = false |
|---|
| 128 |
+ error_result.description = "XSS error found, match was: #{$1}" |
|---|
| 129 |
+ error_result.data = <<-STR |
|---|
| 130 |
+ ######################################################################## |
|---|
| 131 |
+ # Text around unescaped string: #{$1} |
|---|
| 132 |
+ ######################################################################## |
|---|
| 133 |
+ #{response.body[[0, n - 200].max , 400]} |
|---|
| 134 |
+ |
|---|
| 135 |
+ |
|---|
| 136 |
+ |
|---|
| 137 |
+ |
|---|
| 138 |
+ |
|---|
| 139 |
+ ######################################################################## |
|---|
| 140 |
+ # Attack information: |
|---|
| 141 |
+ ######################################################################## |
|---|
| 142 |
+ #{@attacks.select {|a| a['code'] == $1}[0].to_yaml} |
|---|
| 143 |
+ STR |
|---|
| 144 |
+ error_result |
|---|
| 145 |
+ end |
|---|
| 146 |
+ end |
|---|
| 147 |
+end |
|---|
| 148 |
diff -Naru --exclude=.svn --exclude='*~' tarantula.orig/lib/relevance/tarantula/xss_form_submission.rb tarantula.xss_check/lib/relevance/tarantula/xss_form_submission.rb |
|---|
| 149 |
--- tarantula.orig/lib/relevance/tarantula/xss_form_submission.rb 1969-12-31 19:00:00.000000000 -0500 |
|---|
| 150 |
+++ tarantula.xss_check/lib/relevance/tarantula/xss_form_submission.rb 2008-05-04 21:38:31.000000000 -0400 |
|---|
| 151 |
@@ -0,0 +1,60 @@ |
|---|
| 152 |
+class Relevance::Tarantula::XssFormSubmission |
|---|
| 153 |
+ attr_accessor :method, :action, :data, :attack |
|---|
| 154 |
+ |
|---|
| 155 |
+ cattr_accessor :attacks |
|---|
| 156 |
+ |
|---|
| 157 |
+ def initialize(form, attack = nil) |
|---|
| 158 |
+ @method = form.method |
|---|
| 159 |
+ @action = form.action |
|---|
| 160 |
+ @attack = attack |
|---|
| 161 |
+ @data = mutate_selects(form).merge(mutate_text_areas(form)).merge(mutate_inputs(form)) |
|---|
| 162 |
+ end |
|---|
| 163 |
+ |
|---|
| 164 |
+ def self.mutate(form) |
|---|
| 165 |
+ attacks and attacks.map do |attack| |
|---|
| 166 |
+ self.new(form, attack) |
|---|
| 167 |
+ end |
|---|
| 168 |
+ end |
|---|
| 169 |
+ |
|---|
| 170 |
+ def to_s |
|---|
| 171 |
+ "#{action} #{method} #{data.inspect} #{attack.inspect}" |
|---|
| 172 |
+ end |
|---|
| 173 |
+ |
|---|
| 174 |
+ # a form's signature is what makes it unique (e.g. action + fields) |
|---|
| 175 |
+ # used to keep track of which forms we have submitted already |
|---|
| 176 |
+ def signature |
|---|
| 177 |
+ [action, data.keys.sort, attack['name']] |
|---|
| 178 |
+ end |
|---|
| 179 |
+ |
|---|
| 180 |
+ def create_random_data_for(form, tag_selector) |
|---|
| 181 |
+ form.search(tag_selector).inject({}) do |form_args, input| |
|---|
| 182 |
+ # TODO: test |
|---|
| 183 |
+ form_args[input['name']] = random_data(input) if input['name'] |
|---|
| 184 |
+ form_args |
|---|
| 185 |
+ end |
|---|
| 186 |
+ end |
|---|
| 187 |
+ |
|---|
| 188 |
+ def mutate_inputs(form) |
|---|
| 189 |
+ create_random_data_for(form, 'input') |
|---|
| 190 |
+ end |
|---|
| 191 |
+ |
|---|
| 192 |
+ def mutate_text_areas(form) |
|---|
| 193 |
+ create_random_data_for(form, 'textarea') |
|---|
| 194 |
+ end |
|---|
| 195 |
+ |
|---|
| 196 |
+ def mutate_selects(form) |
|---|
| 197 |
+ form.search('select').inject({}) do |form_args, select| |
|---|
| 198 |
+ options = select.search('option') |
|---|
| 199 |
+ option = options.rand |
|---|
| 200 |
+ form_args[select['name']] = option['value'] |
|---|
| 201 |
+ form_args |
|---|
| 202 |
+ end |
|---|
| 203 |
+ end |
|---|
| 204 |
+ |
|---|
| 205 |
+ def random_data(input) |
|---|
| 206 |
+ case input['name'] |
|---|
| 207 |
+ when /^_method$/ : input['value'] |
|---|
| 208 |
+ else attack['code'] |
|---|
| 209 |
+ end |
|---|
| 210 |
+ end |
|---|
| 211 |
+end |
|---|
| 212 |
diff -Naru --exclude=.svn --exclude='*~' tarantula.orig/lib/relevance/tarantula.rb tarantula.xss_check/lib/relevance/tarantula.rb |
|---|
| 213 |
--- tarantula.orig/lib/relevance/tarantula.rb 2008-05-04 15:09:03.000000000 -0400 |
|---|
| 214 |
+++ tarantula.xss_check/lib/relevance/tarantula.rb 2008-05-04 21:02:28.000000000 -0400 |
|---|
| 215 |
@@ -9,9 +9,9 @@ |
|---|
| 216 |
gem 'actionpack' |
|---|
| 217 |
require 'active_support' |
|---|
| 218 |
require 'action_controller' |
|---|
| 219 |
-xss_shield_path = File.join(TARANTULA_ROOT, %w{vendor xss-shield}) |
|---|
| 220 |
-$: << File.join(xss_shield_path, "lib") |
|---|
| 221 |
-require File.join(xss_shield_path, "init") |
|---|
| 222 |
+#xss_shield_path = File.join(TARANTULA_ROOT, %w{vendor xss-shield}) |
|---|
| 223 |
+#$: << File.join(xss_shield_path, "lib") |
|---|
| 224 |
+#require File.join(xss_shield_path, "init") |
|---|
| 225 |
|
|---|
| 226 |
gem 'facets' |
|---|
| 227 |
gem 'htmlentities' |
|---|
| 228 |
@@ -57,5 +57,7 @@ |
|---|
| 229 |
require 'relevance/tarantula/crawler' |
|---|
| 230 |
require 'relevance/tarantula/form' |
|---|
| 231 |
require 'relevance/tarantula/form_submission' |
|---|
| 232 |
+require 'relevance/tarantula/xss_form_submission' |
|---|
| 233 |
+require 'relevance/tarantula/xss_document_checker_handler' |
|---|
| 234 |
|
|---|
| 235 |
require 'relevance/tarantula/tidy_handler' if ENV['TIDY_PATH'] |
|---|
| 236 |
diff -Naru --exclude=.svn --exclude='*~' tarantula.orig/test/relevance/tarantula/xss_document_checker_handler_test.rb tarantula.xss_check/test/relevance/tarantula/xss_document_checker_handler_test.rb |
|---|
| 237 |
--- tarantula.orig/test/relevance/tarantula/xss_document_checker_handler_test.rb 1969-12-31 19:00:00.000000000 -0500 |
|---|
| 238 |
+++ tarantula.xss_check/test/relevance/tarantula/xss_document_checker_handler_test.rb 2008-05-04 21:39:32.000000000 -0400 |
|---|
| 239 |
@@ -0,0 +1,14 @@ |
|---|
| 240 |
+require File.join(File.dirname(__FILE__), "..", "..", "test_helper.rb") |
|---|
| 241 |
+include Relevance::Tarantula |
|---|
| 242 |
+ |
|---|
| 243 |
+describe "Relevance::Tarantula::XssDocumentCheckerHandler" do |
|---|
| 244 |
+ before do |
|---|
| 245 |
+ @handler = Relevance::Tarantula::XssDocumentCheckerHandler.new([{'name' => 'foo_name', 'code' => 'foo_code', 'desc' => 'foo_desc'}]) |
|---|
| 246 |
+ end |
|---|
| 247 |
+ |
|---|
| 248 |
+ it "detects the supplied code" do |
|---|
| 249 |
+ # @handler.expects(:queue_link).never |
|---|
| 250 |
+ result = @handler.handle(Result.new(:response => stub(:html? => true, :body => '<a href="/foo">foo_code</a>'))) |
|---|
| 251 |
+ result.success.should == false |
|---|
| 252 |
+ end |
|---|
| 253 |
+end |
|---|
| 254 |
diff -Naru --exclude=.svn --exclude='*~' tarantula.orig/test/relevance/tarantula/xss_form_submission_test.rb tarantula.xss_check/test/relevance/tarantula/xss_form_submission_test.rb |
|---|
| 255 |
--- tarantula.orig/test/relevance/tarantula/xss_form_submission_test.rb 1969-12-31 19:00:00.000000000 -0500 |
|---|
| 256 |
+++ tarantula.xss_check/test/relevance/tarantula/xss_form_submission_test.rb 2008-05-04 21:39:17.000000000 -0400 |
|---|
| 257 |
@@ -0,0 +1,74 @@ |
|---|
| 258 |
+require File.join(File.dirname(__FILE__), "..", "..", "test_helper.rb") |
|---|
| 259 |
+ |
|---|
| 260 |
+describe "Relevance::Tarantula::XssFormSubmission" do |
|---|
| 261 |
+ |
|---|
| 262 |
+ # TODO: add more from field types to this example form as needed |
|---|
| 263 |
+ before do |
|---|
| 264 |
+ @tag = Hpricot(<<END) |
|---|
| 265 |
+<form action="/session" method="post"> |
|---|
| 266 |
+ <input id="email" name="email" size="30" type="text" /> |
|---|
| 267 |
+ <textarea id="comment" name="comment"value="1" /> |
|---|
| 268 |
+ <input name="commit" type="submit" value="Postit" /> |
|---|
| 269 |
+ <input name="secret" type="hidden" value="secret" /> |
|---|
| 270 |
+ <select id="foo_opened_on_1i" name="foo[opened_on(1i)]"> |
|---|
| 271 |
+ <option value="2003">2003</option> |
|---|
| 272 |
+ <option value="2004">2004</option> |
|---|
| 273 |
+ </select> |
|---|
| 274 |
+</form> |
|---|
| 275 |
+END |
|---|
| 276 |
+ @form = Relevance::Tarantula::Form.new(@tag.at('form')) |
|---|
| 277 |
+ @fs = Relevance::Tarantula::XssFormSubmission.new(@form, {'name' => 'foo_name', 'code' => 'foo_code', 'desc' => 'foo_desc'}) |
|---|
| 278 |
+ end |
|---|
| 279 |
+ |
|---|
| 280 |
+ it "can mutate text areas" do |
|---|
| 281 |
+ @fs.mutate_text_areas(@form).should == {"comment" => "foo_code"} |
|---|
| 282 |
+ end |
|---|
| 283 |
+ |
|---|
| 284 |
+ it "can mutate selects" do |
|---|
| 285 |
+ Hpricot::Elements.any_instance.stubs(:rand).returns(stub(:[] => "2006-stub")) |
|---|
| 286 |
+ @fs.mutate_selects(@form).should == {"foo[opened_on(1i)]" => "2006-stub"} |
|---|
| 287 |
+ end |
|---|
| 288 |
+ |
|---|
| 289 |
+ it "can mutate inputs" do |
|---|
| 290 |
+ @fs.mutate_inputs(@form).should == {"commit"=>"foo_code", "secret"=>"foo_code", "email"=>"foo_code"} |
|---|
| 291 |
+ end |
|---|
| 292 |
+ |
|---|
| 293 |
+ it "has a signature based on action, fields, and attack name" do |
|---|
| 294 |
+ @fs.signature.should == ['/session', [ |
|---|
| 295 |
+ "comment", |
|---|
| 296 |
+ "commit", |
|---|
| 297 |
+ "email", |
|---|
| 298 |
+ "foo[opened_on(1i)]", |
|---|
| 299 |
+ "secret"], |
|---|
| 300 |
+ "foo_name" |
|---|
| 301 |
+ ] |
|---|
| 302 |
+ end |
|---|
| 303 |
+ |
|---|
| 304 |
+ it "has a friendly to_s" do |
|---|
| 305 |
+ @fs.to_s.should =~ %r{^/session post} |
|---|
| 306 |
+ end |
|---|
| 307 |
+ |
|---|
| 308 |
+ it "processes all its attacks" do |
|---|
| 309 |
+ Relevance::Tarantula::XssFormSubmission.attacks = [ |
|---|
| 310 |
+ {'name' => 'foo_name1', 'code1' => 'foo_code1', 'desc' => 'foo_desc1'}, |
|---|
| 311 |
+ {'name' => 'foo_name2', 'code2' => 'foo_code2', 'desc' => 'foo_desc2'}, |
|---|
| 312 |
+ ] |
|---|
| 313 |
+ Relevance::Tarantula::XssFormSubmission.mutate(@form).size.should == 2 |
|---|
| 314 |
+ end |
|---|
| 315 |
+end |
|---|
| 316 |
+ |
|---|
| 317 |
+describe "Relevance::Tarantula::XssFormSubmission for a crummy form" do |
|---|
| 318 |
+ before do |
|---|
| 319 |
+ @tag = Hpricot(<<END) |
|---|
| 320 |
+<form action="/session" method="post"> |
|---|
| 321 |
+ <input value="no_name" /> |
|---|
| 322 |
+</form> |
|---|
| 323 |
+END |
|---|
| 324 |
+ @form = Relevance::Tarantula::Form.new(@tag.at('form')) |
|---|
| 325 |
+ @fs = Relevance::Tarantula::XssFormSubmission.new(@form, {'name' => 'foo_name', 'code' => 'foo_code', 'desc' => 'foo_desc'}) |
|---|
| 326 |
+ end |
|---|
| 327 |
+ |
|---|
| 328 |
+ it "ignores unnamed inputs" do |
|---|
| 329 |
+ @fs.mutate_inputs(@form).should == {} |
|---|
| 330 |
+ end |
|---|
| 331 |
+end |
|---|