bscan 1.4.4

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.
@@ -0,0 +1,324 @@
1
+ #!/usr/bin/env jruby
2
+
3
+ require 'pathname'
4
+ require 'buby'
5
+ require 'getoptlong'
6
+ require 'json'
7
+ require 'bscan/utils/bscan_helper'
8
+ require 'java'
9
+
10
+ class String
11
+ def camelize
12
+ self.split(/[^a-z0-9]/i).map{|w| w.capitalize}.join
13
+ end
14
+ def camelize!
15
+ self.replace(self.split(/[^a-z0-9]/i).map{|w| w.capitalize}.join)
16
+ end
17
+ end
18
+
19
+ module BScan
20
+
21
+ include BscanHelper
22
+
23
+ attr_accessor :activity
24
+ attr_reader :modules_only
25
+ attr_reader :bscan_config
26
+
27
+ def Log (mtype, *msgs)
28
+ pr = "Unknown:"
29
+ case mtype
30
+ when 0
31
+ pr = "ERROR:"
32
+ when 1
33
+ pr = "WARN:"
34
+ when 2
35
+ pr = "INFO:"
36
+ when 3
37
+ pr = "DEBUG:"
38
+ end
39
+ msgs.each do |msg|
40
+ if (@ll >= mtype)
41
+ dt = Time.now.strftime("%y%m%d %H%M%S")
42
+ @log.println "#{dt} #{pr} #{msg}"
43
+ end
44
+ end
45
+ @log.flush
46
+ end
47
+
48
+ def evt_commandline_args args
49
+ @cmd_params ||= JSON.parse args[0]
50
+ @ll = @cmd_params['loglevel'].to_i
51
+
52
+ lfile = @cmd_params['logfile']
53
+ if lfile != nil
54
+ begin
55
+ @log = java.io.PrintStream.new(lfile)
56
+ rescue Exception => e
57
+ $stderr.puts("BScan.evt_register_callbacks Error: can't open log file '#{lfile}', exception: #{e.message}")
58
+ $stderr.puts(e.backtrace.join("\n"))
59
+ Process.exit!(2)
60
+ end
61
+ else
62
+ @log = $stdout
63
+ end
64
+
65
+ Log 2, "BScan.evt_commandline_args CMD_PARAMS: #{@cmd_params}"
66
+ @cmd_params.each_pair do |k,v|
67
+ Log 2,"BScan.evt_commandline_args #{k}:#{v}"
68
+ end
69
+ end
70
+
71
+
72
+
73
+ def evt_http_message(tool_name, is_request, message_info)
74
+ super(tool_name, is_request, message_info)
75
+ if tool_name == 'Scanner'
76
+ if is_request
77
+ Log 2, "#"*70, "# BScan.evt_http_message REQUEST: #{message_info.url.toString}, tool: #{tool_name}", "#"*70
78
+ Log 3, "BScan.evt_http_message #{message_info.req_str}", ""
79
+ else
80
+ Log 2, "# BScan.evt_http_message RESPONSE CODE: #{message_info.statusCode}", ""
81
+ Log 3, "BScan.evt_http_message #{message_info.rsp_str}", ""
82
+ end
83
+ end
84
+ if tool_name == 'Spider'
85
+ if not is_request
86
+ @activity[0] = true
87
+ https = message_info.getProtocol() == "https" ? true : false
88
+ Log 2, "BScan.evt_http_message Passively scanning: #{message_info.url.to_string}"
89
+ do_passive_scan(message_info.getHost(), message_info.getPort(), https, message_info.getRequest(), message_info.getResponse())
90
+ if (is_in_scope(message_info.url))
91
+ Log 2, "BScan.evt_http_message Actively scanning: #{message_info.url.to_string}"
92
+ isqi = do_active_scan(message_info.getHost(), message_info.getPort(), https, message_info.getRequest(), [])
93
+ @queue.push(isqi)
94
+ run_modules message_info
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ def is_module_static n,p
101
+ pref = 'bscan.' + n + '.'
102
+ pref += p + '.' if p and p.length > 0
103
+ @bscan_config[pref + 'static_request'] == 'true'
104
+ end
105
+
106
+
107
+ def run_modules msg=nil
108
+ if @modules
109
+ @modules.each do |m|
110
+ begin
111
+ mod,prop=m.split(':',2)
112
+ prop ||=''
113
+ mn = File.basename(mod,".rb")
114
+ is_static = is_module_static(mn,prop)
115
+ Log 2, "BScan.run_modules executing module #{mod}:#{prop} #{is_static}"
116
+ mn.camelize!
117
+ if (is_static && !msg) || (!is_static && msg)
118
+ eval("
119
+ # puts '=====================MODULE PATH: ' + $:.join(':')
120
+ require '#{mod}'
121
+ require 'bscan/utils/bscan_helper.rb'
122
+
123
+ class #{mn}#{prop}Class
124
+ include #{mn}
125
+ include BscanHelper
126
+ end
127
+ #{mn}#{prop}Class.new.run(self, msg, prop)
128
+ ")
129
+ end
130
+ rescue Exception => e
131
+ Log 1, "BScan.run_modules Can't exceute module #{mod}, Exception: #{e.message}"
132
+ Log 1, e.backtrace.join("\n")
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ def evt_scan_issue issue
139
+ super(issue)
140
+ # Buby::HttpRequestResponseHelper.implant(issue.http_messages)
141
+ write_issue_state issue
142
+ end
143
+
144
+ def sync_save_state issue
145
+ @sync_state_mutex ||= Mutex.new
146
+ @sync_state_mutex.synchronize {
147
+ save_state(@sstream) if @sstream and issue.severity =~ /(High)/
148
+ }
149
+ end
150
+
151
+ def write_issue_state issue
152
+ # Log 2,"INSPECT: #{issue.http_messages[0].methods} #{issue.http_messages[0].inspect} #{issue.http_messages[0].to_s} "
153
+
154
+ Log 2,"BScan.write_issue_state #{not @istream} #{issue.http_messages[0].methods} #{issue.http_messages[0].to_s} "
155
+ @istream or return
156
+ begin
157
+ @istream.puts '#'*70,"#{issue.issue_name} : #{issue.url}",
158
+ "Severity: #{issue.severity}(#{issue.confidence})",
159
+ "Background: #{issue.issue_background}",
160
+ "Details: #{issue.issue_detail}",
161
+ "Remediation: #{issue.remediation_background}",
162
+ "Request: #{issue.http_messages[0].req_str}",
163
+ "Response: #{issue.http_messages[0].rsp_str}"
164
+ # sync_save_state issue throws exceptions
165
+ @istream.flush
166
+ rescue Exception => e
167
+ Log 0, "BScan.write_issue_state Can't write issue #{issue.issue_name}, Exception: #{e.message}"
168
+ Log 0, e.backtrace.join("\n")
169
+ end
170
+ end
171
+ def evt_application_closing
172
+ Log 2,"BScan.evt_application_closing"
173
+ @istream.close if @istream
174
+ @log.close if @log
175
+ end
176
+
177
+ def evt_register_callbacks cb
178
+ super(cb)
179
+ begin
180
+
181
+ Log 2, "="*30, "BScan.evt_register_callbacks registring, log = #{@cmd_params['logfile']} ", "="*30
182
+ @bscan_config = @cmd_params['bscan_config']
183
+ @burp_config = @cmd_params['burp_config']
184
+ @issues = @bscan_config['bscan.issues']
185
+ @modules_only = (@bscan_config['bscan.modules_only'] and @bscan_config['bscan.modules_only'] == 'true')
186
+
187
+ Log 1, "BScan.evt_register_callbacks No issues dir provided. Issues will not be logged." if not @issues
188
+ if (@issues)
189
+ begin
190
+ dt = Time.now.strftime("%y%m%d_%H%M%S")
191
+ File.directory? @issues or %x{mkdir -p "#{@issues}"}
192
+ @sstream = "#{@issues}/session.#{dt}.zip"
193
+ @istream = File.open("#{@issues}/issues.#{dt}.txt","w")
194
+ rescue Exception => e
195
+ Log 0, "BScan.evt_register_callbacks Can't create issues or session files, Exception: #{e.message}"
196
+ Log 0, e.backtrace.join("\n")
197
+
198
+ exit_suite
199
+ end
200
+ end
201
+
202
+ @queue ||= []
203
+ @activity = [false]
204
+ @inactivity_to = @bscan_config['bscan.inactivity_to']
205
+ @inactivity_to ||= '30'
206
+ @inactivity_to = @inactivity_to.to_i
207
+ # Will exit if @activity = 0 and @queue is empty two times
208
+ if @monitor == nil
209
+ Log 2, "="*30, "BScan.evt_register_callbacks Starting Monitor", "="*30
210
+ @monitor = Thread.new(@queue, @activity) {|q,a|
211
+ cnt=0;
212
+ while (true)
213
+ sleep(@inactivity_to/2)
214
+ q.delete_if {|e| e.getPercentageComplete() == 100}
215
+ if q.length == 0 and not a[0]
216
+ cnt += 1
217
+ else
218
+ cnt = 0
219
+ # Log 2, "BScan.evt_register_callbacks QUEUE: #{q.length}, Activity: #{a[0]}"
220
+ end
221
+ if cnt > 1
222
+ # Log 2, "BScan.evt_register_callbacks Scanning complete"
223
+ exit_suite
224
+ break
225
+ end
226
+ a[0] = false
227
+ end
228
+ }
229
+ end
230
+
231
+ Log 2, '='*30, 'BScan.evt_register_callbacks Params', '='*30
232
+ params = save_config
233
+ params['target.scopeinclude0']='**empty**' # burp can store the previous scope somehow, thus need to clean
234
+ @burp_config.each_pair do |k,v|
235
+ params[k] = v
236
+ end
237
+ load_config params
238
+ params.each_pair {|k,v| Log 2,"#{k}:#{v}"} # if k =~ /^(scanner|spider)/}
239
+
240
+ @modules ||= @bscan_config['bscan.modules']
241
+ @modules = [@modules] if not @modules.kind_of?(Array)
242
+
243
+ mods = []
244
+ @modules.each do |m|
245
+ mods << m.split(',')
246
+ end
247
+
248
+ @modules = mods.flatten
249
+
250
+ urls = @bscan_config['bscan.url']
251
+ Log 2, "BScan.evt_register_callbacks urls: #{@modules_only} #{urls.join('|')}"
252
+
253
+ run_modules # run_modules without 'msg' will do static reqs only
254
+ return if @modules_only
255
+
256
+ if not urls
257
+ Log 0, "BScan.evt_register_callbacks No URL's provided in config. Use bscan.url param. Multiple entries are OK"
258
+ exit_suite
259
+ end
260
+
261
+ urls = [urls] if not urls.kind_of?(Array)
262
+
263
+ urls.each do |u|
264
+ Log 2, "BScan.evt_register_callbacks checking url: #{u}"
265
+ if not is_in_scope(u)
266
+ Log 2, "BScan.evt_register_callbacks including url: #{u}"
267
+ include_in_scope(u)
268
+ end
269
+ Log 2, "BScan.evt_register_callbacks Sending to spider: #{u}"
270
+ send_to_spider(u)
271
+ end
272
+ end
273
+ rescue Exception => e
274
+ Log 0, "BScan.evt_register_callbacks Exception: #{e.message}"
275
+ Log 0, e.backtrace.join("\n")
276
+ exit_suite
277
+ end
278
+ end
279
+
280
+
281
+ def log ll, stream
282
+ stream.puts if true
283
+ end
284
+
285
+ def add_multi map,k,v
286
+ if (map[k])
287
+ ov = map[k];
288
+ if (ov.kind_of?(Array))
289
+ map[k] << v
290
+ else
291
+ map[k] = [ov,v]
292
+ end
293
+ else
294
+ map[k] = v
295
+ end
296
+ end
297
+
298
+ def read_config file
299
+
300
+ burp_config = {}
301
+ bscan_config = {}
302
+
303
+ begin
304
+ open_in_path(file).each_line do |line|
305
+ line.chomp!
306
+ line.strip!
307
+ next if (line =~ /^#/ or line.length < 1)
308
+ data = line.split(/=/)
309
+ val = nil
310
+ val = data[1..-1].join('=') if data.size > 1
311
+ if data[0] =~ /^bscan./
312
+ add_multi(bscan_config,data[0],val)
313
+ else
314
+ burp_config[data[0]] = val
315
+ end
316
+ end
317
+ rescue Exception => e
318
+ $stderr.puts("BScan.read_config Error: can't read config file '#{file}', exception: #{e.message}")
319
+ $stderr.puts(e.backtrace.join("\n"))
320
+ Process.exit(3)
321
+ end
322
+ [burp_config, bscan_config]
323
+ end
324
+
@@ -0,0 +1,142 @@
1
+ require 'bscan/utils/bscan_helper.rb'
2
+
3
+ module Injector
4
+
5
+ COMMENT_START='# '
6
+
7
+ def run *args
8
+
9
+ @bscan = args[0]
10
+ @config ||= @bscan.instance_variable_get("@bscan_config")
11
+ @bscan.activity[0]=true
12
+
13
+ @prop_pref = 'bscan.injector.'
14
+ @prop_pref += args[2] + '.' if args[2] && args[2].length > 0
15
+ @mid = args[2]?"Injector.#{args[2]}.":'Injector.'
16
+ msg = args[1]
17
+
18
+ if not msg
19
+ inject_to_pattern
20
+ return
21
+ end
22
+
23
+ url = msg.url.dup.to_s
24
+ @bscan.Log 2, "#{@mid}run for #{url}"
25
+ begin
26
+ if (url =~ /([^?]+)\?(.+)/)
27
+ beg = "#{$1}?"
28
+ params = $2
29
+ @bscan.Log 2, "#{@mid}run BEG: #{beg} PARAMS: #{params} FILE: #{@config[prop('file')]}"
30
+ injs = open_in_path(@config[prop('file')])
31
+ injs.each_line do |l|
32
+ l.chomp!
33
+ next if (l =~ /^#{COMMENT_START}/ or l.length < 1)
34
+ @bscan.Log 2, "#{@mid}run injecting: #{l}"
35
+
36
+ @bscan.activity[0]=true
37
+
38
+ do_scan(msg, beg + l, l) # in parameter name
39
+ do_scan(msg, beg.chop + l, l) # in URL
40
+
41
+ pos=0
42
+ while (m=params.match(/([^&]+)=([^&]+)/,pos))
43
+ trg = beg + params[0..m.begin(2)-1] + l + params[m.end(2)..-1]
44
+ @bscan.activity[0]=true
45
+ do_scan(msg, trg, l)
46
+ pos=m.end(1)+1
47
+ end
48
+ end
49
+ injs.close
50
+ end
51
+
52
+ inject_to_body msg if @config['bscan.injector.one.inject_to_body'] == 'true'
53
+
54
+ rescue Exception => e
55
+ @bscan.Log 0, "#{@mid}run Exception: #{e.message}"
56
+ @bscan.Log 0, e.backtrace.join("\n")
57
+ end
58
+ end
59
+
60
+ def inject_to_pattern
61
+ param = @config[prop('inject_instead_of')]
62
+ a=[]
63
+ if (not param or (a=param.split(':',3)).size < 3)
64
+ @bscan.Log 0, "#{@mid}inject_to_pattern: 'inject_instead_of' parameter is not valid #{param}"
65
+ return
66
+ end
67
+ @bscan.Log 2, "#{@mid}inject_to_pattern input: #{a.join('|')}"
68
+ begin
69
+ p,f,proto = Regexp.escape(a[0]), a[1], a[2]
70
+ file = open_in_path(f)
71
+ req = file.read
72
+ req.gsub!(/\^M\n/,"\r\n")
73
+ file.close
74
+
75
+ injs = open_in_path(@config[prop('file')])
76
+ injs.each_line do |l|
77
+ l.chomp!
78
+ next if (l =~ /^#{COMMENT_START}/ or l.length < 1)
79
+ @bscan.Log 2, "#{@mid}inject_to_pattern injecting: #{l}"
80
+
81
+ pos = 0
82
+ while (m=req.match(/(#{p}).*?(#{p})/,pos))
83
+ r = (req[0..m.begin(1)-1] + l + req[m.end(2)..-1]).gsub /#{p}(.*?)#{p}/,'\1'
84
+
85
+ @bscan.Log 2, "#{@mid}inject_to_pattern new req:\n#{r}"
86
+ set_len r
87
+ @bscan.activity[0]=true
88
+ send_req r,proto,l
89
+ pos=m.end(1)
90
+ end
91
+ end
92
+ injs.close
93
+
94
+ rescue Exception => e
95
+ @bscan.Log 0, "#{@mid}inject_to_pattern Exception: #{e.message}"
96
+ @bscan.Log 0, e.backtrace.join("\n")
97
+ end
98
+
99
+ end
100
+
101
+ def inject_to_body msg
102
+ scanf = false
103
+ @bscan.Log 2, "#{@mid}inject_to_body req: #{msg.req_str}"
104
+ msg.request_headers.each do |a|
105
+ @bscan.Log 2, "#{@mid}inject_to_body hdr: #{a[0]} #{a[1]}"
106
+ if a.size > 1 and a[0] =~ /content-type/i and a[1] =~ /application\/x-www-form-urlencoded/i
107
+ scanf = true
108
+ break
109
+ end
110
+ end
111
+ return if not scanf
112
+ m=msg.req_str.match(/\r?\n\r?\n/)
113
+ return if m.size < 1
114
+ start_pos = m.end(0)
115
+
116
+ begin
117
+ injs = open_in_path(@config[prop('file')])
118
+ injs.each_line do |l|
119
+ l.chomp!
120
+ next if (l =~ /^#{COMMENT_START}/ or l.length < 1)
121
+ @bscan.Log 2, "#{@mid}inject_to_body injecting: #{l}"
122
+ pos=start_pos
123
+ while (m=msg.req_str.match(/([^=]+)=([^=]+)/,pos))
124
+ req = msg.req_str[0..m.begin(2)-1] + l + msg.req_str[m.end(2)..-1]
125
+ req.sub!(/content-length\s*:\s*\d+/i, "Content-Length: "+(req.length-start_pos).to_s)
126
+ @bscan.Log 2, "#{@mid}inject_to_body #{pos} #{req}"
127
+ @bscan.activity[0]=true
128
+ send_req req, msg.getProtocol, l
129
+ pos=m.end(1)+1
130
+ end
131
+ end
132
+ injs.close
133
+ rescue Exception => e
134
+ @bscan.Log 0, "#{@mid}inject_to_body Exception: #{e.message}"
135
+ @bscan.Log 0, e.backtrace.join("\n")
136
+ end
137
+ end
138
+
139
+
140
+
141
+
142
+ end