bscan 1.4.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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