bscan 1.4.5 → 2.0.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.
@@ -1,4 +1,28 @@
1
+ require 'pathname'
2
+ require 'java'
3
+ require "socket"
4
+ require "openssl"
5
+ require "uri"
6
+
7
+
8
+
9
+ class String
10
+ def camelize
11
+ self.split(/[^a-z0-9]/i).map{|w| w.capitalize}.join
12
+ end
13
+ def camelize!
14
+ self.replace(self.split(/[^a-z0-9]/i).map{|w| w.capitalize}.join)
15
+ end
16
+ end
17
+
1
18
  module BscanHelper
19
+
20
+ attr_reader :modules_only
21
+ attr_reader :bscan_config
22
+ attr_accessor :stat
23
+ attr_accessor :activity
24
+
25
+
2
26
  class Issue
3
27
  attr_accessor :issue_name
4
28
  attr_accessor :url
@@ -23,11 +47,26 @@ module BscanHelper
23
47
  @req_str,@rsp_str = req,rsp
24
48
  end
25
49
  end
50
+
51
+ def copy_vars from
52
+ from.instance_variables.each do |nm|
53
+ self.instance_variable_set(nm, from.instance_variable_get(nm))
54
+ # puts "#{nm} => #{from.instance_variable_get(nm)}"
55
+ end
56
+ end
26
57
 
27
58
  def prop nm
28
59
  @prop_pref + nm
29
60
  end
30
61
 
62
+ def get_par k,defv
63
+ p = @bscan_config[prop(k)]
64
+ p = p.to_i if p and p.to_i.to_s == p
65
+ p = true if p == 'true' or p == 'yes'
66
+ p = false if p == 'false' or p == 'no'
67
+ p ? p:defv
68
+ end
69
+
31
70
  def search_path
32
71
  path = []
33
72
  path << File.expand_path('.') << File.expand_path(File.join('.','lib')) << File.expand_path(File.join('~','.bscan')) << File.expand_path(File.join('etc','bscan')) << $:
@@ -58,8 +97,8 @@ module BscanHelper
58
97
 
59
98
 
60
99
  def do_scan msg, trg, inj
61
- @bscan.activity[0]=true
62
- @bscan.Log 2, "#{@mid}do_scan Scanning: #{trg}"
100
+ @activity[0]=true
101
+ Log 2, "#{@mid}do_scan Scanning: #{trg}"
63
102
  # msg.url = trg
64
103
  path = $1 if trg =~ /\/\/[^\/]+(\/.*)/
65
104
  path = '/' if (not path) or (path.length < 1)
@@ -84,24 +123,101 @@ module BscanHelper
84
123
  trg,host,port = get_url_host_port req,proto
85
124
  https = proto == "https" ? true : false
86
125
  start = Time.now
87
- @bscan.Log 2, "#{@mid}send_req make_req: '#{trg}' '#{host}' '#{port}'\n#{req}"
88
- rsp = @bscan.make_request(host, port, https, req)
126
+ Log 2, "#{@mid}send_req make_req: '#{trg}' '#{host}' '#{port}'\n#{req}"
127
+ # rsp = @bscan.make_request(host, port, https, req)
128
+ rsp = make_request_socket host, port, https, req
89
129
  rt = Time.now - start
90
130
  return [rsp,rt,trg,host,port]
91
131
  rescue Exception => e
92
- @bscan.Log 0, "#{@mid}send_req Exception: #{e.message}"
93
- @bscan.Log 0, e.backtrace.join("\n")
132
+ Log 0, "#{@mid}send_req Exception: #{e.message}"
133
+ Log 0, e.backtrace.join("\n")
94
134
  end
95
135
  end
96
136
 
137
+ def make_request_socket host, port, https, req
138
+ begin
139
+ rsp = ''
140
+ s = TCPSocket.new(host, port)
141
+ Log 2, "#{@mid}make_request_socket new socket #{host} #{port} #{https}"
142
+ if (https)
143
+ ctx = OpenSSL::SSL::SSLContext.new
144
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
145
+ s = OpenSSL::SSL::SSLSocket.new(s, ctx)
146
+ s.connect
147
+ end
148
+ s.syswrite(req)
149
+ Log 2, "#{@mid}make_request_socket write succeeded for #{req}"
150
+ rsp = read_response_socket s
151
+ while ((u = redirect? rsp))
152
+ rsp = redirect u,req
153
+ end
154
+ Log 2, "#{@mid}make_request_socket read succeeded #{rsp}"
155
+ rescue Exception => e
156
+ Log 1, "#{@mid}make_request_socket failed: #{e.message}"
157
+ Log 1, e.backtrace.join("\n")
158
+ end
159
+ begin
160
+ s.close
161
+ rescue
162
+ end
163
+ rsp
164
+ end
97
165
 
166
+ def redirect? req
167
+ if req =~ /^HTTP\/[^\s]+\s+3\d\d/i
168
+ return $1 if req =~ /\nLocation\s*:\s*([^\r\n]+)\r?\n/i
169
+ end
170
+ nil
171
+ end
172
+
173
+ def redirect u, req
174
+ uri = URI.parse(u)
175
+ proto = uri.scheme
176
+ host = uri.host
177
+ port = uri.port
178
+ path = uri.path
179
+ path += '?' + uri.query if uri.query
180
+ make_request_socket host, port, ('https'==proto),
181
+ req.sub(/^(POST|GET)\s+\/[^\s]+/, "\\1 #{path}")
182
+ end
183
+
184
+
185
+ def msg_end rsp
186
+ rsp[-4..-1] == "\r\n\r\n" || rsp[-2..-1] == "\n\n"
187
+ end
188
+
189
+ def read_response_socket s
190
+ rsp=''
191
+ begin
192
+ while (ch=s.sysread(1))
193
+ rsp += ch
194
+ break if msg_end rsp
195
+ if rsp =~ /Content-Length\s*:\s*(\d+)\r?\n$/i
196
+ len = $1.to_i
197
+ while (c=s.sysread(1))
198
+ rsp += c
199
+ if msg_end rsp
200
+ return rsp if len <= 0
201
+ rsp += s.sysread(len)
202
+ return rsp;
203
+ end
204
+ end
205
+ break
206
+ end
207
+ end
208
+ rescue Exception => e
209
+ Log 1, "#{@mid}read_response_socket failed: #{e.message}\nResponse:\n#{rsp}"
210
+ Log 1, e.backtrace.join("\n")
211
+ end
212
+ rsp
213
+ end
98
214
 
99
215
  def send_req req, proto, inj
100
216
  rsp,rt,trg,host,port = send_only req, proto, inj
101
217
  https = proto == "https" ? true : false
102
- if not @bscan.modules_only
103
- @bscan.Log 2, "#{@mid}send_req do_passive: '#{trg}' '#{host}' '#{port}'\n#{req}\n#{rsp}"
104
- @bscan.do_passive_scan(host, port, https, req, rsp)
218
+ if not @modules_only
219
+ Log 2, "#{@mid}send_req do_passive: '#{trg}' '#{host}' '#{port}'\n#{req}\n#{rsp}"
220
+ @burp.do_passive_scan(host, port, https, req, rsp)
105
221
  end
106
222
  verify_response trg, req, rsp, inj, rt
107
223
  end
@@ -112,7 +228,7 @@ module BscanHelper
112
228
 
113
229
  def verify_response u, req, rsp, inj, time
114
230
 
115
- @bscan.Log 2, "#{@mid}verify_response: #{u} #{inj} #{time} #{req} #{rsp}"
231
+ Log 2, "#{@mid}verify_response: #{u} #{inj} #{time} #{req} #{rsp}"
116
232
 
117
233
  st = $1 if rsp =~ /^\s*HTTP.*\s+(\d+)\s+/
118
234
  st ||= '0'
@@ -130,7 +246,184 @@ module BscanHelper
130
246
  issue = Issue.new "#{@mid.chop}: Possible XSS", u, "High", "Retest", req, rsp, "The following input has been replayed in a response #{inj}"
131
247
  end
132
248
 
133
- @bscan.write_issue_state issue if issue
249
+ write_issue_state issue if issue
134
250
  end
135
251
 
252
+ def Log (mtype, *msgs)
253
+ pr = "Unknown:"
254
+ case mtype
255
+ when 0
256
+ pr = "ERROR:"
257
+ when 1
258
+ pr = "WARN:"
259
+ when 2
260
+ pr = "INFO:"
261
+ when 3
262
+ pr = "DEBUG:"
263
+ end
264
+ msgs.each do |msg|
265
+ if (@ll >= mtype)
266
+ dt = Time.now.strftime("%y%m%d %H%M%S")
267
+ @log.println "#{dt} #{pr} #{msg}"
268
+ end
269
+ end
270
+ @log.flush
271
+ end
272
+
273
+ def run_modules base, msg=nil
274
+ if @modules
275
+ @modules.each do |m|
276
+ begin
277
+ mod,prop=m.split(':',2)
278
+ prop ||=''
279
+ mn = File.basename(mod,".rb")
280
+ is_static = is_module_static(mn,prop)
281
+ Log 2, "BscanHelper.run_modules executing module #{mod}:#{prop} #{is_static}"
282
+ mn.camelize!
283
+ if (is_static && !msg) || (!is_static && msg)
284
+ eval("
285
+ # puts '=====================MODULE PATH: ' + $:.join(':')
286
+ require '#{mod}'
287
+ require 'bscan/utils/bscan_helper.rb'
288
+
289
+ class #{mn}#{prop}Class
290
+ include #{mn}
291
+ include BscanHelper
292
+ end
293
+ modins = #{mn}#{prop}Class.new
294
+ modins.copy_vars base if base
295
+ modins.run(self, msg, prop)
296
+ ")
297
+ end
298
+ rescue Exception => e
299
+ Log 1, "BscanHelper.run_modules Can't exceute module #{mod}, Exception: #{e.message}"
300
+ Log 1, e.backtrace.join("\n")
301
+ end
302
+ end
303
+ end
304
+ end
305
+
306
+ def write_issue_state issue
307
+ # Log 2,"INSPECT: #{issue.http_messages[0].methods} #{issue.http_messages[0].inspect} #{issue.http_messages[0].to_s} "
308
+
309
+ @stat['high'] += 1 if issue.severity =~ /High/i
310
+ @stat['med'] += 1 if issue.severity =~ /Med/i
311
+ @stat['low'] += 1 if issue.severity =~ /Low/i
312
+ @stat['urls'] += " #{issue.url}\n"
313
+
314
+ Log 2,"BscanHelper.write_issue_state #{not @istream} #{issue.http_messages[0].methods} #{issue.http_messages[0].to_s} "
315
+ @istream or return
316
+ begin
317
+ @istream.println '#'*70
318
+ @istream.println "#{issue.issue_name} : #{issue.url}"
319
+ @istream.println "Severity: #{issue.severity}(#{issue.confidence})"
320
+ @istream.println "Background: #{issue.issue_background}"
321
+ @istream.println "Details: #{issue.issue_detail}"
322
+ @istream.println "Remediation: #{issue.remediation_background}"
323
+ @istream.println "Request: #{issue.http_messages[0].req_str}"
324
+ @istream.println "Response: #{issue.http_messages[0].rsp_str}"
325
+ # sync_save_state issue throws exceptions
326
+ @istream.flush
327
+ rescue Exception => e
328
+ Log 0, "BscanHelper.write_issue_state Can't write issue #{issue.issue_name}, Exception: #{e.message}"
329
+ Log 0, e.backtrace.join("\n")
330
+ end
331
+ end
332
+
333
+ def log ll, stream
334
+ stream.puts if true
335
+ end
336
+
337
+ def add_multi map,k,v
338
+ if (map[k])
339
+ ov = map[k];
340
+ if (ov.kind_of?(Array))
341
+ map[k] << v
342
+ else
343
+ map[k] = [ov,v]
344
+ end
345
+ else
346
+ map[k] = v
347
+ end
348
+ end
349
+
350
+ def init_stat
351
+ @stat ||= {}
352
+ @stat['start_time'] = Time.now.strftime("%Y-%m-%d %H:%M:%S")
353
+ @stat['end_time'] = ''
354
+ @stat['high'] = 0
355
+ @stat['med'] = 0
356
+ @stat['low'] = 0
357
+ @stat['issues'] = ''
358
+ @stat['urls'] = ''
359
+ end
360
+
361
+ def init_internals cmd_params
362
+
363
+ init_stat
364
+ @activity ||= [false]
365
+
366
+ # @cmd_params ||= JSON.parse args[0]
367
+ @cmd_params ||= cmd_params
368
+ @ll = @cmd_params['loglevel'].to_i
369
+
370
+ lfile = @cmd_params['logfile']
371
+ if lfile != nil
372
+ begin
373
+ @log = java.io.PrintStream.new(lfile)
374
+ rescue Exception => e
375
+ $stderr.puts("BscanHelper.init_internals Error: can't open log file '#{lfile}', exception: #{e.message}")
376
+ $stderr.puts(e.backtrace.join("\n"))
377
+ Process.exit!(2)
378
+ end
379
+ else
380
+ @log = $stdout
381
+ end
382
+
383
+ Log 2, "BscanHelper.init_internals CMD_PARAMS: #{@cmd_params}"
384
+ @cmd_params.each_pair do |k,v|
385
+ Log 2,"BscanHelper.init_internals #{k}:#{v}"
386
+ end
387
+
388
+ @bscan_config = @cmd_params['bscan_config']
389
+ @burp_config = @cmd_params['burp_config']
390
+ @issues = @bscan_config['bscan.issues']
391
+ @modules_only = (@bscan_config['bscan.modules_only'] and @bscan_config['bscan.modules_only'] == 'true')
392
+ @modules ||= @bscan_config['bscan.modules']
393
+ @modules ||= [] if not @modules
394
+ @modules = [@modules] if not @modules.kind_of?(Array)
395
+ mods = []
396
+ @modules.each do |m|
397
+ mods << m.split(',')
398
+ end
399
+ @modules = mods.flatten
400
+
401
+
402
+ Log 1, "BscanHelper.init_internals No issues dir provided. Issues will not be logged." if not @issues
403
+ if (@issues)
404
+ begin
405
+ dt = Time.now.strftime("%y%m%d_%H%M%S")
406
+ File.directory? @issues or %x{mkdir -p "#{@issues}"}
407
+ @sstream = "#{@issues}/session.#{dt}.zip"
408
+ ifile = "#{@issues}/issues.#{dt}.txt"
409
+ @istream = java.io.PrintStream.new(ifile)
410
+ @stat['issues'] = ifile
411
+ rescue Exception => e
412
+ Log 0, "BscanHelper.init_internals Can't create issues or session files, Exception: #{e.message}"
413
+ Log 0, e.backtrace.join("\n")
414
+ exit_suite
415
+ end
416
+ end
417
+
418
+ end
419
+
420
+ def is_module_static n,p
421
+ pref = 'bscan.' + n + '.'
422
+ pref += p + '.' if p and p.length > 0
423
+ @bscan_config[pref + 'static_request'] == 'true'
424
+ end
425
+
426
+
427
+
428
+
136
429
  end
@@ -1,3 +1,8 @@
1
+ == 2.0.0
2
+ Two major improvements:
3
+ * Modules with static requests do not require Burp or Buby anymore
4
+ * Slow server writes have been added to slowloris (see delay_on_write)
5
+
1
6
  == 1.4.5
2
7
  * Mailer added to send scan status and detailed reports over SMTP
3
8
  * Issues are logged with Java's PrintStream
data/test.sh CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/bin/sh
2
2
 
3
- jruby -J-Xmx1024M -J-Djava.awt.headless=true bin/bscan -c samples/config/conf2 -L 2 -l bscan.log
3
+ jruby -J-Xmx1024m -J-Djava.awt.headless=true bin/bscan -c samples/config/conf2 -L 2 -l bscan.log
metadata CHANGED
@@ -2,14 +2,14 @@
2
2
  name: bscan
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 1.4.5
5
+ version: 2.0.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Oleg Gryb (ogryb)
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-08-15 00:00:00.000000000 Z
12
+ date: 2012-08-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: buby