bscan 1.4.5 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CONFIG.rdoc +18 -7
- data/README.rdoc +6 -3
- data/VERSION +1 -1
- data/bin/bscan +41 -3
- data/bscan.gemspec +2 -2
- data/lib/bscan.rb +11 -213
- data/lib/bscan/modules/injector.rb +23 -24
- data/lib/bscan/modules/kill_apache.rb +40 -36
- data/lib/bscan/modules/many_threads.rb +7 -8
- data/lib/bscan/modules/slowloris.rb +115 -100
- data/lib/bscan/utils/bscan_helper.rb +304 -11
- data/release_notes.txt +5 -0
- data/test.sh +1 -1
- metadata +2 -2
data/CONFIG.rdoc
CHANGED
@@ -91,7 +91,12 @@ to 'true', the detailed zipped report will be attached. You'll ned
|
|
91
91
|
It's important not to exceed the maximum file number on your
|
92
92
|
client, otherwise it might not work. It's also important to
|
93
93
|
set up a correct timeout (sleep_time) that should not be
|
94
|
-
bigger than server's read timeout.
|
94
|
+
bigger than server's read or write timeout. The timeouts are different
|
95
|
+
for different servers and attack types: for slow reads the
|
96
|
+
timeout is usally bigger (100-200-.. secs), while for
|
97
|
+
slow writes it worked well for 5 - 10 sec interval. I general,
|
98
|
+
I found that slow writes are far more dangerous than slow reads
|
99
|
+
and that's why I set the default for 'delay_on_write' to 'true'.
|
95
100
|
|
96
101
|
* bscan.slowloris.hostport=<host>:<port>
|
97
102
|
no defaults, must provide both
|
@@ -100,23 +105,29 @@ bigger than server's read timeout.
|
|
100
105
|
* bscan.slowloris.method=<http method>
|
101
106
|
POST or GET, default - GET
|
102
107
|
* bscan.slowloris.threads=n
|
103
|
-
Thread number, default - 20
|
108
|
+
Thread number, default - 20 for reads, 500 for writes
|
104
109
|
* bscan.slowloris.con_nbr_per_thread=n
|
105
|
-
Number of connections per thread
|
110
|
+
Number of connections per thread.
|
111
|
+
Default - 50 for reads, 1 for writes
|
106
112
|
* bscan.slowloris.pack_per_con=n
|
107
113
|
Max number of data packets to be sent in each connection
|
108
|
-
|
114
|
+
Default - 5 for reads, 50 - for writes
|
109
115
|
* bscan.slowloris.response_time_factor=n
|
110
116
|
Normal response time will be multipled by this number to determine
|
111
117
|
when report an issue, e.g. if normal reposne time is 2 sec then if
|
112
118
|
a response time under attack is bigger than 2*n, it will
|
113
119
|
be logged as an issue. Default - 5
|
114
120
|
* bscan.slowloris.sleep_time=n
|
115
|
-
number of seconds to sleep
|
116
|
-
|
121
|
+
number of seconds to sleep after beginning of a request has been send
|
122
|
+
for reads or a delay to read message body for writes.
|
123
|
+
Default: 100 - for reads, 5 - for writes
|
117
124
|
* bscan.slowloris.static_request=true
|
118
125
|
Must be always set to 'true' for this module
|
119
|
-
|
126
|
+
* delay_on_write=<true|false>
|
127
|
+
Make delays on server's writes if true. Default: true
|
128
|
+
* health_check_int=n
|
129
|
+
Health check interval in seconds. Default: 2
|
130
|
+
|
120
131
|
== kill_apache.rb Module Parameters
|
121
132
|
Similar to slowloris a monitoring thread will be checking
|
122
133
|
a response time and log an issue if a threshold is reached
|
data/README.rdoc
CHANGED
@@ -28,14 +28,13 @@ Burp Suite from PortSwigger and Buby from Eric Monti and Timur Duehr
|
|
28
28
|
== REQUIREMENTS:
|
29
29
|
* JRuby - http://jruby.org
|
30
30
|
* Burp pro if you want to use default Burp's scanners
|
31
|
-
*
|
32
|
-
* Buby 1.3.1 (see http://emonti.github.com/buby/)
|
31
|
+
* Buby 1.3.1 (see http://emonti.github.com/buby/) (not neccessary for modules_only mode)
|
33
32
|
|
34
33
|
== BUILD/INSTALL:
|
35
34
|
|
36
35
|
=== Gem
|
37
36
|
|
38
|
-
sudo jruby -S gem install buby -d --source=http://gemcutter.org
|
37
|
+
sudo jruby -S gem install buby -d --source=http://gemcutter.org (not necessary for modules_only)
|
39
38
|
sudo jruby -S gem install bscan --source=http://gemcutter.org
|
40
39
|
|
41
40
|
After Buby and BScan are installed, you'll need to link BScan to Burp's JAR (see below)
|
@@ -51,6 +50,8 @@ Burp Suite from PortSwigger and Buby from Eric Monti and Timur Duehr
|
|
51
50
|
|
52
51
|
==== Linking BScan and Buby to Burp's JAR.
|
53
52
|
|
53
|
+
* This step is not neccessary for modules_only mode
|
54
|
+
|
54
55
|
After Buby and BScan are installed (either manually or by gem)
|
55
56
|
you'll need to link them to a Burp's JAR.
|
56
57
|
|
@@ -81,6 +82,8 @@ To get help for config layout run:
|
|
81
82
|
== RUNNING, REGISTERING Burp Pro:
|
82
83
|
You need to register you burp.jar before you can run it headless
|
83
84
|
|
85
|
+
* Not neccessary for modules_only mode
|
86
|
+
|
84
87
|
* Run your Burp as usual with GUI and provide you license.
|
85
88
|
After this is done, you can run it headless using 'bscan'
|
86
89
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
2.0.0
|
data/bin/bscan
CHANGED
@@ -3,10 +3,9 @@
|
|
3
3
|
#require File.expand_path(File.join(File.dirname(__FILE__), %w[.. lib bscan]))
|
4
4
|
$: << File.expand_path(File.join(File.dirname(__FILE__), %w[.. lib]))
|
5
5
|
|
6
|
-
require 'buby'
|
7
6
|
require 'getoptlong'
|
8
|
-
require 'bscan'
|
9
7
|
require 'bscan/utils/bscan_helper'
|
8
|
+
require 'json'
|
10
9
|
|
11
10
|
include BscanHelper
|
12
11
|
|
@@ -29,6 +28,35 @@ USAGE: jruby [-J-Xmx<nnn>M] [-J-Djava.awt.headless=true] -S bscan --config path_
|
|
29
28
|
exit(1)
|
30
29
|
end
|
31
30
|
|
31
|
+
def read_config file
|
32
|
+
|
33
|
+
burp_config = {}
|
34
|
+
bscan_config = {}
|
35
|
+
|
36
|
+
begin
|
37
|
+
open_in_path(file).each_line do |line|
|
38
|
+
line.chomp!
|
39
|
+
line.strip!
|
40
|
+
next if (line =~ /^#/ or line.length < 1)
|
41
|
+
data = line.split(/=/)
|
42
|
+
val = nil
|
43
|
+
val = data[1..-1].join('=') if data.size > 1
|
44
|
+
if data[0] =~ /^bscan./
|
45
|
+
add_multi(bscan_config,data[0],val)
|
46
|
+
else
|
47
|
+
burp_config[data[0]] = val
|
48
|
+
end
|
49
|
+
end
|
50
|
+
rescue Exception => e
|
51
|
+
$stderr.puts("BScan.read_config Error: can't read config file '#{file}', exception: #{e.message}")
|
52
|
+
$stderr.puts(e.backtrace.join("\n"))
|
53
|
+
Process.exit(3)
|
54
|
+
end
|
55
|
+
[burp_config, bscan_config]
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
|
32
60
|
def get_cmd_params
|
33
61
|
params = {}
|
34
62
|
|
@@ -74,6 +102,16 @@ def get_cmd_params
|
|
74
102
|
params
|
75
103
|
end
|
76
104
|
|
105
|
+
params = get_cmd_params
|
106
|
+
|
107
|
+
init_internals params
|
108
|
+
run_modules self
|
109
|
+
exit 0 if @modules_only
|
110
|
+
|
111
|
+
require 'buby'
|
112
|
+
require 'burp'
|
113
|
+
require 'bscan'
|
114
|
+
|
77
115
|
$burp = Buby.new()
|
78
116
|
$burp.extend(BScan)
|
79
|
-
$burp.start_burp([
|
117
|
+
$burp.start_burp([params.to_json])
|
data/bscan.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "bscan"
|
8
|
-
s.version = "
|
8
|
+
s.version = "2.0.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Oleg Gryb (ogryb)"]
|
12
|
-
s.date = "2012-08-
|
12
|
+
s.date = "2012-08-22"
|
13
13
|
s.description = "BScan is a configurable and extendable web application security scanner that can be run from a command line headless (without UI). It's built on top of arguably the most popular commercial security testing tool Burp Suite from PortSwigger and Buby from Eric Monti and Timur Duehr"
|
14
14
|
s.email = "oleg@gryb.info"
|
15
15
|
s.executables = ["bscan"]
|
data/lib/bscan.rb
CHANGED
@@ -1,75 +1,20 @@
|
|
1
1
|
#!/usr/bin/env jruby
|
2
2
|
|
3
|
-
require 'pathname'
|
4
3
|
require 'buby'
|
5
4
|
require 'getoptlong'
|
6
|
-
require 'json'
|
7
5
|
require 'bscan/utils/bscan_helper'
|
8
6
|
require 'bscan/utils/mailer'
|
9
7
|
require 'java'
|
10
|
-
|
11
|
-
class String
|
12
|
-
def camelize
|
13
|
-
self.split(/[^a-z0-9]/i).map{|w| w.capitalize}.join
|
14
|
-
end
|
15
|
-
def camelize!
|
16
|
-
self.replace(self.split(/[^a-z0-9]/i).map{|w| w.capitalize}.join)
|
17
|
-
end
|
18
|
-
end
|
8
|
+
require 'json'
|
19
9
|
|
20
10
|
module BScan
|
21
11
|
|
22
12
|
include BscanHelper
|
23
13
|
include Mailer
|
24
14
|
|
25
|
-
attr_accessor :activity
|
26
|
-
attr_reader :modules_only
|
27
|
-
attr_reader :bscan_config
|
28
|
-
attr_accessor :stat
|
29
15
|
|
30
|
-
def Log (mtype, *msgs)
|
31
|
-
pr = "Unknown:"
|
32
|
-
case mtype
|
33
|
-
when 0
|
34
|
-
pr = "ERROR:"
|
35
|
-
when 1
|
36
|
-
pr = "WARN:"
|
37
|
-
when 2
|
38
|
-
pr = "INFO:"
|
39
|
-
when 3
|
40
|
-
pr = "DEBUG:"
|
41
|
-
end
|
42
|
-
msgs.each do |msg|
|
43
|
-
if (@ll >= mtype)
|
44
|
-
dt = Time.now.strftime("%y%m%d %H%M%S")
|
45
|
-
@log.println "#{dt} #{pr} #{msg}"
|
46
|
-
end
|
47
|
-
end
|
48
|
-
@log.flush
|
49
|
-
end
|
50
|
-
|
51
16
|
def evt_commandline_args args
|
52
|
-
|
53
|
-
@cmd_params ||= JSON.parse args[0]
|
54
|
-
@ll = @cmd_params['loglevel'].to_i
|
55
|
-
|
56
|
-
lfile = @cmd_params['logfile']
|
57
|
-
if lfile != nil
|
58
|
-
begin
|
59
|
-
@log = java.io.PrintStream.new(lfile)
|
60
|
-
rescue Exception => e
|
61
|
-
$stderr.puts("BScan.evt_register_callbacks Error: can't open log file '#{lfile}', exception: #{e.message}")
|
62
|
-
$stderr.puts(e.backtrace.join("\n"))
|
63
|
-
Process.exit!(2)
|
64
|
-
end
|
65
|
-
else
|
66
|
-
@log = $stdout
|
67
|
-
end
|
68
|
-
|
69
|
-
Log 2, "BScan.evt_commandline_args CMD_PARAMS: #{@cmd_params}"
|
70
|
-
@cmd_params.each_pair do |k,v|
|
71
|
-
Log 2,"BScan.evt_commandline_args #{k}:#{v}"
|
72
|
-
end
|
17
|
+
init_internals JSON.parse(args[0])
|
73
18
|
end
|
74
19
|
|
75
20
|
|
@@ -95,50 +40,13 @@ module BScan
|
|
95
40
|
Log 2, "BScan.evt_http_message Actively scanning: #{message_info.url.to_string}"
|
96
41
|
isqi = do_active_scan(message_info.getHost(), message_info.getPort(), https, message_info.getRequest(), [])
|
97
42
|
@queue.push(isqi)
|
98
|
-
run_modules message_info
|
43
|
+
run_modules self, message_info
|
99
44
|
end
|
100
45
|
end
|
101
46
|
end
|
102
47
|
end
|
103
48
|
|
104
|
-
|
105
|
-
pref = 'bscan.' + n + '.'
|
106
|
-
pref += p + '.' if p and p.length > 0
|
107
|
-
@bscan_config[pref + 'static_request'] == 'true'
|
108
|
-
end
|
109
|
-
|
110
|
-
|
111
|
-
def run_modules msg=nil
|
112
|
-
if @modules
|
113
|
-
@modules.each do |m|
|
114
|
-
begin
|
115
|
-
mod,prop=m.split(':',2)
|
116
|
-
prop ||=''
|
117
|
-
mn = File.basename(mod,".rb")
|
118
|
-
is_static = is_module_static(mn,prop)
|
119
|
-
Log 2, "BScan.run_modules executing module #{mod}:#{prop} #{is_static}"
|
120
|
-
mn.camelize!
|
121
|
-
if (is_static && !msg) || (!is_static && msg)
|
122
|
-
eval("
|
123
|
-
# puts '=====================MODULE PATH: ' + $:.join(':')
|
124
|
-
require '#{mod}'
|
125
|
-
require 'bscan/utils/bscan_helper.rb'
|
126
|
-
|
127
|
-
class #{mn}#{prop}Class
|
128
|
-
include #{mn}
|
129
|
-
include BscanHelper
|
130
|
-
end
|
131
|
-
#{mn}#{prop}Class.new.run(self, msg, prop)
|
132
|
-
")
|
133
|
-
end
|
134
|
-
rescue Exception => e
|
135
|
-
Log 1, "BScan.run_modules Can't exceute module #{mod}, Exception: #{e.message}"
|
136
|
-
Log 1, e.backtrace.join("\n")
|
137
|
-
end
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
49
|
+
|
142
50
|
def evt_scan_issue issue
|
143
51
|
super(issue)
|
144
52
|
# Buby::HttpRequestResponseHelper.implant(issue.http_messages)
|
@@ -152,32 +60,7 @@ module BScan
|
|
152
60
|
}
|
153
61
|
end
|
154
62
|
|
155
|
-
|
156
|
-
# Log 2,"INSPECT: #{issue.http_messages[0].methods} #{issue.http_messages[0].inspect} #{issue.http_messages[0].to_s} "
|
157
|
-
|
158
|
-
@stat['high'] += 1 if issue.severity =~ /High/i
|
159
|
-
@stat['med'] += 1 if issue.severity =~ /Med/i
|
160
|
-
@stat['low'] += 1 if issue.severity =~ /Low/i
|
161
|
-
@stat['urls'] += " #{issue.url}\n"
|
162
|
-
|
163
|
-
Log 2,"BScan.write_issue_state #{not @istream} #{issue.http_messages[0].methods} #{issue.http_messages[0].to_s} "
|
164
|
-
@istream or return
|
165
|
-
begin
|
166
|
-
@istream.println '#'*70
|
167
|
-
@istream.println "#{issue.issue_name} : #{issue.url}"
|
168
|
-
@istream.println "Severity: #{issue.severity}(#{issue.confidence})"
|
169
|
-
@istream.println "Background: #{issue.issue_background}"
|
170
|
-
@istream.println "Details: #{issue.issue_detail}"
|
171
|
-
@istream.println "Remediation: #{issue.remediation_background}"
|
172
|
-
@istream.println "Request: #{issue.http_messages[0].req_str}"
|
173
|
-
@istream.println "Response: #{issue.http_messages[0].rsp_str}"
|
174
|
-
# sync_save_state issue throws exceptions
|
175
|
-
@istream.flush
|
176
|
-
rescue Exception => e
|
177
|
-
Log 0, "BScan.write_issue_state Can't write issue #{issue.issue_name}, Exception: #{e.message}"
|
178
|
-
Log 0, e.backtrace.join("\n")
|
179
|
-
end
|
180
|
-
end
|
63
|
+
|
181
64
|
def evt_application_closing
|
182
65
|
begin
|
183
66
|
Log 2,"BScan.evt_application_closing #{@bscan_config['bscan.smtp.server']} #{@bscan_config['bscan.smtp.to']}"
|
@@ -193,32 +76,12 @@ module BScan
|
|
193
76
|
|
194
77
|
def evt_register_callbacks cb
|
195
78
|
super(cb)
|
79
|
+
@burp ||= self
|
196
80
|
begin
|
197
|
-
|
198
81
|
Log 2, "="*30, "BScan.evt_register_callbacks registring, log = #{@cmd_params['logfile']} ", "="*30
|
199
|
-
|
200
|
-
@burp_config = @cmd_params['burp_config']
|
201
|
-
@issues = @bscan_config['bscan.issues']
|
202
|
-
@modules_only = (@bscan_config['bscan.modules_only'] and @bscan_config['bscan.modules_only'] == 'true')
|
203
|
-
|
204
|
-
Log 1, "BScan.evt_register_callbacks No issues dir provided. Issues will not be logged." if not @issues
|
205
|
-
if (@issues)
|
206
|
-
begin
|
207
|
-
dt = Time.now.strftime("%y%m%d_%H%M%S")
|
208
|
-
File.directory? @issues or %x{mkdir -p "#{@issues}"}
|
209
|
-
@sstream = "#{@issues}/session.#{dt}.zip"
|
210
|
-
ifile = "#{@issues}/issues.#{dt}.txt"
|
211
|
-
@istream = java.io.PrintStream.new(ifile)
|
212
|
-
@stat['issues'] = ifile
|
213
|
-
rescue Exception => e
|
214
|
-
Log 0, "BScan.evt_register_callbacks Can't create issues or session files, Exception: #{e.message}"
|
215
|
-
Log 0, e.backtrace.join("\n")
|
216
|
-
exit_suite
|
217
|
-
end
|
218
|
-
end
|
82
|
+
|
219
83
|
|
220
84
|
@queue ||= []
|
221
|
-
@activity = [false]
|
222
85
|
@inactivity_to = @bscan_config['bscan.inactivity_to']
|
223
86
|
@inactivity_to ||= '30'
|
224
87
|
@inactivity_to = @inactivity_to.to_i
|
@@ -255,22 +118,11 @@ module BScan
|
|
255
118
|
load_config params
|
256
119
|
params.each_pair {|k,v| Log 2,"#{k}:#{v}"} # if k =~ /^(scanner|spider)/}
|
257
120
|
|
258
|
-
|
259
|
-
|
260
|
-
@modules = [@modules] if not @modules.kind_of?(Array)
|
261
|
-
|
262
|
-
mods = []
|
263
|
-
@modules.each do |m|
|
264
|
-
mods << m.split(',')
|
265
|
-
end
|
121
|
+
# run_modules
|
122
|
+
# exit_suite
|
266
123
|
|
267
|
-
|
268
|
-
|
269
|
-
urls = @bscan_config['bscan.url']
|
270
|
-
Log 2, "BScan.evt_register_callbacks urls: #{@modules_only} #{urls}"
|
271
|
-
|
272
|
-
run_modules # run_modules without 'msg' will do static reqs only
|
273
|
-
return if @modules_only
|
124
|
+
urls = @bscan_config['bscan.url']
|
125
|
+
Log 2, "BScan.evt_register_callbacks urls: #{@modules_only} #{urls}"
|
274
126
|
|
275
127
|
if not urls
|
276
128
|
Log 0, "BScan.evt_register_callbacks No URL's provided in config. Use bscan.url param. Multiple entries are OK"
|
@@ -297,58 +149,4 @@ module BScan
|
|
297
149
|
end
|
298
150
|
|
299
151
|
|
300
|
-
def log ll, stream
|
301
|
-
stream.puts if true
|
302
|
-
end
|
303
|
-
|
304
|
-
def add_multi map,k,v
|
305
|
-
if (map[k])
|
306
|
-
ov = map[k];
|
307
|
-
if (ov.kind_of?(Array))
|
308
|
-
map[k] << v
|
309
|
-
else
|
310
|
-
map[k] = [ov,v]
|
311
|
-
end
|
312
|
-
else
|
313
|
-
map[k] = v
|
314
|
-
end
|
315
|
-
end
|
316
|
-
|
317
|
-
def init_stat
|
318
|
-
@stat ||= {}
|
319
|
-
@stat['start_time'] = Time.now.strftime("%Y-%m-%d %H:%M:%S")
|
320
|
-
@stat['end_time'] = ''
|
321
|
-
@stat['high'] = 0
|
322
|
-
@stat['med'] = 0
|
323
|
-
@stat['low'] = 0
|
324
|
-
@stat['issues'] = ''
|
325
|
-
@stat['urls'] = ''
|
326
|
-
end
|
327
|
-
|
328
|
-
def read_config file
|
329
|
-
|
330
|
-
burp_config = {}
|
331
|
-
bscan_config = {}
|
332
|
-
|
333
|
-
begin
|
334
|
-
open_in_path(file).each_line do |line|
|
335
|
-
line.chomp!
|
336
|
-
line.strip!
|
337
|
-
next if (line =~ /^#/ or line.length < 1)
|
338
|
-
data = line.split(/=/)
|
339
|
-
val = nil
|
340
|
-
val = data[1..-1].join('=') if data.size > 1
|
341
|
-
if data[0] =~ /^bscan./
|
342
|
-
add_multi(bscan_config,data[0],val)
|
343
|
-
else
|
344
|
-
burp_config[data[0]] = val
|
345
|
-
end
|
346
|
-
end
|
347
|
-
rescue Exception => e
|
348
|
-
$stderr.puts("BScan.read_config Error: can't read config file '#{file}', exception: #{e.message}")
|
349
|
-
$stderr.puts(e.backtrace.join("\n"))
|
350
|
-
Process.exit(3)
|
351
|
-
end
|
352
|
-
[burp_config, bscan_config]
|
353
|
-
end
|
354
152
|
|