bscan 1.4.4 → 1.4.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CONFIG.rdoc +17 -1
- data/README.rdoc +7 -2
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/bscan.gemspec +5 -3
- data/lib/bscan.rb +44 -14
- data/lib/bscan/modules/injector.rb +1 -1
- data/lib/bscan/utils/bscan_helper.rb +6 -3
- data/lib/bscan/utils/mailer.rb +150 -0
- data/release_notes.txt +5 -0
- data/samples/config/xss.txt +2 -0
- metadata +5 -3
data/CONFIG.rdoc
CHANGED
@@ -30,7 +30,7 @@ path that configs refers to:
|
|
30
30
|
|
31
31
|
see BscannerHelper#search_path for details
|
32
32
|
|
33
|
-
== BScan
|
33
|
+
== BScan Parameters
|
34
34
|
* bscan.modules - see 'CONVENTIONS' for details
|
35
35
|
* bscan.inactivity_to - inactivity is sec that triggers exit
|
36
36
|
* bscan.issues=issues - output directory for findings/issues
|
@@ -39,6 +39,22 @@ see BscannerHelper#search_path for details
|
|
39
39
|
* bscan.url - URL to spider, multiple entries are OK. Have no effect
|
40
40
|
if scan.modules_only=true
|
41
41
|
|
42
|
+
== BScan SMTP Parameters
|
43
|
+
If specified an email will be sent. If 'include_report' is set
|
44
|
+
to 'true', the detailed zipped report will be attached. You'll ned
|
45
|
+
'zip' gem to make attachments working.
|
46
|
+
* bscan.smtp.server=<server> SMTP server
|
47
|
+
* bscan.smtp.port=n SMTP port
|
48
|
+
Default: 25 for plain, 465 for SSL
|
49
|
+
* bscan.smtp.ssl=<true|false> Use SSL
|
50
|
+
Default - false
|
51
|
+
* bscan.smtp.to=<to_emails_coma_separated>
|
52
|
+
* bscan.smtp.from=<from_email>
|
53
|
+
* bscan.smtp.domain=<from_domain>
|
54
|
+
Deafult: from_email's domain
|
55
|
+
* bscan.smtp.include_report=<true|false> attach report as zip
|
56
|
+
Default: false
|
57
|
+
|
42
58
|
== Modules Included to the Package
|
43
59
|
* injector.rb - injects malicious patterns provided in a file (e.g. Google's
|
44
60
|
fuzzdb) to URL or body parameters. It can also inject to pattern marked
|
data/README.rdoc
CHANGED
@@ -78,6 +78,13 @@ To get help for config layout run:
|
|
78
78
|
|
79
79
|
bscan --help config
|
80
80
|
|
81
|
+
== RUNNING, REGISTERING Burp Pro:
|
82
|
+
You need to register you burp.jar before you can run it headless
|
83
|
+
|
84
|
+
* Run your Burp as usual with GUI and provide you license.
|
85
|
+
After this is done, you can run it headless using 'bscan'
|
86
|
+
|
87
|
+
|
81
88
|
== DOCUMENTATION, SAMPLES, RUNNING HEADLESSLY
|
82
89
|
* Rdoc generated files: http://gryb.info/bscan/
|
83
90
|
* After installing BScan's gem find the location of 'samples' dir:
|
@@ -88,8 +95,6 @@ To get help for config layout run:
|
|
88
95
|
#!/bin/sh
|
89
96
|
jruby -J-Xmx1024M -J-Djava.awt.headless=true -S bscan -c ../config/conf -L 2 -l bscan.log
|
90
97
|
|
91
|
-
|
92
|
-
|
93
98
|
== CREDITS:
|
94
99
|
* Burp and Burp Suite are trademarks of PortSwigger(ltd)
|
95
100
|
Copyright 2012 PortSwigger Ltd. All rights reserved.
|
data/Rakefile
CHANGED
@@ -10,7 +10,7 @@ begin
|
|
10
10
|
gem.summary = %q{BScan is an extendable and configurable command line web application security scanner}
|
11
11
|
gem.description = %q{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}
|
12
12
|
gem.email = "oleg@gryb.info"
|
13
|
-
gem.homepage = "http://
|
13
|
+
gem.homepage = "http://gryb.info/bscan"
|
14
14
|
gem.authors = ["Oleg Gryb (ogryb)"]
|
15
15
|
#gem.platform = "java"
|
16
16
|
gem.test_files = ["test/bscan_test.rb"]
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.4.
|
1
|
+
1.4.5
|
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 = "1.4.
|
8
|
+
s.version = "1.4.5"
|
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-15"
|
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"]
|
@@ -32,16 +32,18 @@ Gem::Specification.new do |s|
|
|
32
32
|
"lib/bscan/modules/many_threads.rb",
|
33
33
|
"lib/bscan/modules/slowloris.rb",
|
34
34
|
"lib/bscan/utils/bscan_helper.rb",
|
35
|
+
"lib/bscan/utils/mailer.rb",
|
35
36
|
"release_notes.txt",
|
36
37
|
"samples/config/big_request.txt",
|
37
38
|
"samples/config/conf",
|
38
39
|
"samples/config/injector.txt",
|
39
40
|
"samples/config/request.txt",
|
41
|
+
"samples/config/xss.txt",
|
40
42
|
"samples/headless-bscan.sh",
|
41
43
|
"test.sh",
|
42
44
|
"test/bscan_test.rb"
|
43
45
|
]
|
44
|
-
s.homepage = "http://
|
46
|
+
s.homepage = "http://gryb.info/bscan"
|
45
47
|
s.rdoc_options = ["--main", "README.rdoc"]
|
46
48
|
s.require_paths = ["lib"]
|
47
49
|
s.rubygems_version = "1.8.24"
|
data/lib/bscan.rb
CHANGED
@@ -5,6 +5,7 @@ require 'buby'
|
|
5
5
|
require 'getoptlong'
|
6
6
|
require 'json'
|
7
7
|
require 'bscan/utils/bscan_helper'
|
8
|
+
require 'bscan/utils/mailer'
|
8
9
|
require 'java'
|
9
10
|
|
10
11
|
class String
|
@@ -19,10 +20,12 @@ end
|
|
19
20
|
module BScan
|
20
21
|
|
21
22
|
include BscanHelper
|
23
|
+
include Mailer
|
22
24
|
|
23
25
|
attr_accessor :activity
|
24
26
|
attr_reader :modules_only
|
25
27
|
attr_reader :bscan_config
|
28
|
+
attr_accessor :stat
|
26
29
|
|
27
30
|
def Log (mtype, *msgs)
|
28
31
|
pr = "Unknown:"
|
@@ -46,6 +49,7 @@ module BScan
|
|
46
49
|
end
|
47
50
|
|
48
51
|
def evt_commandline_args args
|
52
|
+
init_stat
|
49
53
|
@cmd_params ||= JSON.parse args[0]
|
50
54
|
@ll = @cmd_params['loglevel'].to_i
|
51
55
|
|
@@ -151,16 +155,22 @@ module BScan
|
|
151
155
|
def write_issue_state issue
|
152
156
|
# Log 2,"INSPECT: #{issue.http_messages[0].methods} #{issue.http_messages[0].inspect} #{issue.http_messages[0].to_s} "
|
153
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
|
+
|
154
163
|
Log 2,"BScan.write_issue_state #{not @istream} #{issue.http_messages[0].methods} #{issue.http_messages[0].to_s} "
|
155
164
|
@istream or return
|
156
165
|
begin
|
157
|
-
@istream.
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
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}"
|
164
174
|
# sync_save_state issue throws exceptions
|
165
175
|
@istream.flush
|
166
176
|
rescue Exception => e
|
@@ -169,9 +179,16 @@ module BScan
|
|
169
179
|
end
|
170
180
|
end
|
171
181
|
def evt_application_closing
|
172
|
-
|
173
|
-
|
174
|
-
|
182
|
+
begin
|
183
|
+
Log 2,"BScan.evt_application_closing #{@bscan_config['bscan.smtp.server']} #{@bscan_config['bscan.smtp.to']}"
|
184
|
+
@istream.close if @istream
|
185
|
+
@stat['end_time'] = Time.now.strftime("%Y-%m-%d %H:%M:%S")
|
186
|
+
send_email if @bscan_config['bscan.smtp.server'] and @bscan_config['bscan.smtp.to']
|
187
|
+
@log.close if @log
|
188
|
+
rescue Exception => e
|
189
|
+
Log 0, "BScan.evt_application_closing Exception: #{e.message}"
|
190
|
+
Log 0, e.backtrace.join("\n")
|
191
|
+
end
|
175
192
|
end
|
176
193
|
|
177
194
|
def evt_register_callbacks cb
|
@@ -190,11 +207,12 @@ module BScan
|
|
190
207
|
dt = Time.now.strftime("%y%m%d_%H%M%S")
|
191
208
|
File.directory? @issues or %x{mkdir -p "#{@issues}"}
|
192
209
|
@sstream = "#{@issues}/session.#{dt}.zip"
|
193
|
-
|
210
|
+
ifile = "#{@issues}/issues.#{dt}.txt"
|
211
|
+
@istream = java.io.PrintStream.new(ifile)
|
212
|
+
@stat['issues'] = ifile
|
194
213
|
rescue Exception => e
|
195
214
|
Log 0, "BScan.evt_register_callbacks Can't create issues or session files, Exception: #{e.message}"
|
196
215
|
Log 0, e.backtrace.join("\n")
|
197
|
-
|
198
216
|
exit_suite
|
199
217
|
end
|
200
218
|
end
|
@@ -238,6 +256,7 @@ module BScan
|
|
238
256
|
params.each_pair {|k,v| Log 2,"#{k}:#{v}"} # if k =~ /^(scanner|spider)/}
|
239
257
|
|
240
258
|
@modules ||= @bscan_config['bscan.modules']
|
259
|
+
@modules ||= [] if not @modules
|
241
260
|
@modules = [@modules] if not @modules.kind_of?(Array)
|
242
261
|
|
243
262
|
mods = []
|
@@ -247,8 +266,8 @@ module BScan
|
|
247
266
|
|
248
267
|
@modules = mods.flatten
|
249
268
|
|
250
|
-
|
251
|
-
|
269
|
+
urls = @bscan_config['bscan.url']
|
270
|
+
Log 2, "BScan.evt_register_callbacks urls: #{@modules_only} #{urls}"
|
252
271
|
|
253
272
|
run_modules # run_modules without 'msg' will do static reqs only
|
254
273
|
return if @modules_only
|
@@ -295,6 +314,17 @@ def add_multi map,k,v
|
|
295
314
|
end
|
296
315
|
end
|
297
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
|
+
|
298
328
|
def read_config file
|
299
329
|
|
300
330
|
burp_config = {}
|
@@ -37,12 +37,15 @@ module BscanHelper
|
|
37
37
|
Pathname.new(file).absolute? ? [file] : search_path.map! {|p| File.join(p,file)}
|
38
38
|
end
|
39
39
|
|
40
|
-
def open_in_path file
|
40
|
+
def open_in_path file,pathonly=false
|
41
41
|
io = nil
|
42
42
|
files = search_path_file(file)
|
43
43
|
files.each do |p|
|
44
|
-
|
45
|
-
|
44
|
+
if File.file?(p)
|
45
|
+
return p if pathonly
|
46
|
+
io = File.open(p,"r")
|
47
|
+
return io if io
|
48
|
+
end
|
46
49
|
end
|
47
50
|
raise "Can't find file in: #{files.join(':')}"
|
48
51
|
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'net/smtp'
|
2
|
+
require 'socket'
|
3
|
+
|
4
|
+
|
5
|
+
module Mailer
|
6
|
+
|
7
|
+
MARKER = "ABCDEFFEDCBA"
|
8
|
+
|
9
|
+
HEADERS =
|
10
|
+
%q{From: BScan-<ver> <from>
|
11
|
+
To: <to>
|
12
|
+
Subject: <subject>
|
13
|
+
Content-Type: multipart/mixed; boundary="<MARKER>"
|
14
|
+
MIME-Version: 1.0
|
15
|
+
|
16
|
+
--<MARKER>
|
17
|
+
}
|
18
|
+
|
19
|
+
SIMPLE =
|
20
|
+
%q{From: BScan-<ver> <from>
|
21
|
+
To: <to>
|
22
|
+
Subject: <subject>
|
23
|
+
Content-Type: text/plain; charset="UTF-8";
|
24
|
+
|
25
|
+
<BODY>}
|
26
|
+
|
27
|
+
BODY =
|
28
|
+
%q{
|
29
|
+
Start Time: <start_time>
|
30
|
+
End Time: <end_time>
|
31
|
+
High #: <high>
|
32
|
+
Med #: <med>
|
33
|
+
Low #: <low>
|
34
|
+
Issues: <issues>
|
35
|
+
URL's :
|
36
|
+
<urls>
|
37
|
+
}
|
38
|
+
|
39
|
+
MESSAGE =
|
40
|
+
%q{Content-Type: text/plain
|
41
|
+
Content-Transfer-Encoding:8bit
|
42
|
+
|
43
|
+
<BODY>
|
44
|
+
--<MARKER>
|
45
|
+
}
|
46
|
+
|
47
|
+
ATTACHMENT =
|
48
|
+
%q{Content-Type: application/zip; name="<filename>"
|
49
|
+
Content-Transfer-Encoding:base64
|
50
|
+
Content-Disposition: attachment; filename="<filename>"
|
51
|
+
|
52
|
+
<encoded>
|
53
|
+
--<MARKER>--
|
54
|
+
}
|
55
|
+
|
56
|
+
def pv par,defv=nil
|
57
|
+
val = bscan_config[@m_pref + par]
|
58
|
+
val ? val : defv
|
59
|
+
end
|
60
|
+
|
61
|
+
def sv par,defv=nil
|
62
|
+
val = @stat[ par]
|
63
|
+
val ? val : defv
|
64
|
+
end
|
65
|
+
def ver
|
66
|
+
file=::File.expand_path(File.join(::File.dirname(__FILE__), "../../../VERSION"))
|
67
|
+
::File.file?(file) ? File.read(file).chomp : ''
|
68
|
+
end
|
69
|
+
|
70
|
+
def send_email
|
71
|
+
begin
|
72
|
+
|
73
|
+
@m_pref = 'bscan.smtp.'
|
74
|
+
srv = pv 'server',nil
|
75
|
+
ssl = pv 'ssl',false
|
76
|
+
defport = ssl ? 465 : 25
|
77
|
+
port = pv 'port',defport
|
78
|
+
from = pv 'from',nil
|
79
|
+
dfrom = $1 if from =~ /@(.+)/
|
80
|
+
domain = pv 'domain',dfrom
|
81
|
+
user = pv 'user',nil
|
82
|
+
pwd = pv 'pwd',nil
|
83
|
+
attach = pv 'include_report','false'
|
84
|
+
attach = attach=='true' ? true:false
|
85
|
+
to = pv 'to', nil
|
86
|
+
file = @stat['issues']
|
87
|
+
file ||= ''
|
88
|
+
raise "Can't send '#{file}' that doesn't exist" if attach && !File.file?(file)
|
89
|
+
file = File.absolute_path(file)
|
90
|
+
|
91
|
+
@stat['subject'] = sv 'subject', "BScan @ #{Socket.gethostname}"
|
92
|
+
|
93
|
+
Log 2,"Mailer.send_mail #{@stat} #{pv 'server'} #{pv 'from'} #{pv 'to'} #{pv 'ssl'} #{pv 'port'} #{pv 'domwin'} #{pv 'user'} "
|
94
|
+
|
95
|
+
raise "Params 'server', 'from', 'to', 'domain' are mandatory" if !srv or !from or !to or !domain
|
96
|
+
|
97
|
+
if (attach)
|
98
|
+
msg = HEADERS + MESSAGE + ATTACHMENT
|
99
|
+
msg.gsub!( /<MARKER>/ , MARKER)
|
100
|
+
msg.gsub!( /<filename>/ , File.basename(sv('issues'),'.*') + '.zip')
|
101
|
+
msg.sub!(/<encoded>/,zip_encode(file))
|
102
|
+
else
|
103
|
+
msg = SIMPLE
|
104
|
+
end
|
105
|
+
|
106
|
+
msg.sub!( /<BODY>/ , BODY)
|
107
|
+
|
108
|
+
@stat.each_key do |k|
|
109
|
+
Log 2,"Mailer.send_mail replacing <#{k}>"
|
110
|
+
msg.gsub!( /<#{k}>/ , k=='issues'?file:sv(k,'').to_s)
|
111
|
+
end
|
112
|
+
|
113
|
+
msg.sub!( /<from>/, "<#{from}>")
|
114
|
+
msg.sub!( /<to>/ , "<#{to}>")
|
115
|
+
msg.sub!( /<ver>/ , ver)
|
116
|
+
|
117
|
+
msg.gsub!(/\r?\n/,"\r\n")
|
118
|
+
|
119
|
+
to = to.split(',')
|
120
|
+
|
121
|
+
smtp = Net::SMTP.new srv,port
|
122
|
+
smtp.enable_ssl if ssl
|
123
|
+
if (user)
|
124
|
+
smtp.start(domain,user,pwd)
|
125
|
+
else
|
126
|
+
smtp.start(domain)
|
127
|
+
end
|
128
|
+
Log 2,"Mailer.send_mail send_message #{from} #{to}\n#{msg}"
|
129
|
+
smtp.send_message msg, from, to
|
130
|
+
smtp.finish
|
131
|
+
rescue Exception => e
|
132
|
+
Log 0, "Mailer.send Exception: #{e.message}"
|
133
|
+
Log 0, e.backtrace.join("\n")
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def zip_encode f
|
138
|
+
require 'zip/zip'
|
139
|
+
fb = File.basename(f, '.*')
|
140
|
+
fbe = File.basename(f)
|
141
|
+
zipf = fb + '.zip'
|
142
|
+
zipp = File.join(File.dirname(f), zipf)
|
143
|
+
zip = Zip::ZipFile.open(zipp, Zip::ZipFile::CREATE)
|
144
|
+
zip.find_entry(fbe)?zip.replace(fbe,f):zip.add(fbe,f)
|
145
|
+
zip.close
|
146
|
+
content = File.read(zipp)
|
147
|
+
[content].pack("m")
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
data/release_notes.txt
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
== 1.4.5
|
2
|
+
* Mailer added to send scan status and detailed reports over SMTP
|
3
|
+
* Issues are logged with Java's PrintStream
|
4
|
+
* Some bugs have been fixed
|
5
|
+
|
1
6
|
== 1.4.4
|
2
7
|
* Added a module for apache killer (apache_killer.rb)
|
3
8
|
* Changed logging to use Java IO (Ruby's IO caused Java exceptions)
|
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
|
+
version: 1.4.5
|
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-
|
12
|
+
date: 2012-08-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: buby
|
@@ -50,15 +50,17 @@ files:
|
|
50
50
|
- lib/bscan/modules/many_threads.rb
|
51
51
|
- lib/bscan/modules/slowloris.rb
|
52
52
|
- lib/bscan/utils/bscan_helper.rb
|
53
|
+
- lib/bscan/utils/mailer.rb
|
53
54
|
- release_notes.txt
|
54
55
|
- samples/config/big_request.txt
|
55
56
|
- samples/config/conf
|
56
57
|
- samples/config/injector.txt
|
57
58
|
- samples/config/request.txt
|
59
|
+
- samples/config/xss.txt
|
58
60
|
- samples/headless-bscan.sh
|
59
61
|
- test.sh
|
60
62
|
- test/bscan_test.rb
|
61
|
-
homepage: http://
|
63
|
+
homepage: http://gryb.info/bscan
|
62
64
|
licenses: []
|
63
65
|
post_install_message:
|
64
66
|
rdoc_options:
|