wasp 0.2.0
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.
- data/Gemfile +5 -0
- data/LICENSE +20 -0
- data/README.md +77 -0
- data/Rakefile +15 -0
- data/bin/wasp +6 -0
- data/config/aws.yml +4 -0
- data/lib/wasp.rb +14 -0
- data/lib/wasp/config.rb +11 -0
- data/lib/wasp/const.rb +21 -0
- data/lib/wasp/core_ext.rb +113 -0
- data/lib/wasp/ec2.rb +355 -0
- data/lib/wasp/nest.rb +321 -0
- data/lib/wasp/queenwasp.rb +7 -0
- data/lib/wasp/stingab.rb +355 -0
- data/lib/wasp/wasp.rb +514 -0
- data/report/makeplot.rb +131 -0
- data/test/test_wasp.rb +9 -0
- data/wasp.gemspec +38 -0
- metadata +112 -0
data/lib/wasp/wasp.rb
ADDED
@@ -0,0 +1,514 @@
|
|
1
|
+
require 'aws-sdk'
|
2
|
+
require 'net/ssh'
|
3
|
+
require 'net/scp'
|
4
|
+
|
5
|
+
require File.dirname(__FILE__) + '/ec2'
|
6
|
+
|
7
|
+
module WASP
|
8
|
+
class Wasp
|
9
|
+
attr_reader :ec2
|
10
|
+
attr_reader :num_wasps
|
11
|
+
attr_reader :weapon_name
|
12
|
+
attr_reader :weapon
|
13
|
+
attr_reader :report
|
14
|
+
|
15
|
+
LINE_LENGTH = 80
|
16
|
+
|
17
|
+
def initialize(args)
|
18
|
+
@num_wasps = if args[:server].nil? then WASP::Const::DEFAULT_WASPS
|
19
|
+
else
|
20
|
+
args[:server].to_i
|
21
|
+
end
|
22
|
+
|
23
|
+
@weapon_name = if args[:weapon].nil? then WASP::Const::DEFAULT_WASPS
|
24
|
+
else
|
25
|
+
args[:weapon]
|
26
|
+
end
|
27
|
+
|
28
|
+
@header = args[:header]
|
29
|
+
|
30
|
+
@report = if args[:report].nil? then false
|
31
|
+
else
|
32
|
+
true
|
33
|
+
end
|
34
|
+
|
35
|
+
@keepalive = if args[:keepalive].nil? then false
|
36
|
+
else
|
37
|
+
true
|
38
|
+
end
|
39
|
+
@without_cookie = if args[:wo_cookie].nil? then false
|
40
|
+
else
|
41
|
+
true
|
42
|
+
end
|
43
|
+
@compact = if args[:compact].nil? then false
|
44
|
+
else
|
45
|
+
true
|
46
|
+
end
|
47
|
+
|
48
|
+
@ec2 = WASP::Aws.new(args)
|
49
|
+
@params = []
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
def ready
|
54
|
+
@ec2.get_keypair
|
55
|
+
@ec2.get_security_group
|
56
|
+
end
|
57
|
+
|
58
|
+
def breed
|
59
|
+
if @ec2.create == false then
|
60
|
+
puts "[Error]".red + " Breeding wasps failed"
|
61
|
+
exit false
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def down
|
66
|
+
puts "Calling off the wasp."
|
67
|
+
@ec2.terminate
|
68
|
+
end
|
69
|
+
|
70
|
+
def attack(num, conn, url, time=nil)
|
71
|
+
wasps = assemble_wasps
|
72
|
+
maximum = wasps.count * WASP::Const::DEFAULT_MAXIMUM_STINGS
|
73
|
+
if conn > maximum then
|
74
|
+
print "[WARN]".yellow + " concurrent attack exceeded maximum number of wasps.\n"
|
75
|
+
print "Currently maximum number of concurrent attack is " + "#{maximum}".green + ".\n"
|
76
|
+
print "Please breed more wasps.\n"
|
77
|
+
exit false
|
78
|
+
end
|
79
|
+
|
80
|
+
urls = url.split(';')
|
81
|
+
|
82
|
+
puts "Organizing the wasp."
|
83
|
+
|
84
|
+
stingless_wasps = _attack(wasps, num, conn, urls, time)
|
85
|
+
|
86
|
+
print "Offensive complete.\n\n"
|
87
|
+
|
88
|
+
retrive_report(stingless_wasps) if @report
|
89
|
+
|
90
|
+
mission_report(stingless_wasps)
|
91
|
+
|
92
|
+
puts "The wasp is awaiting new orders."
|
93
|
+
end
|
94
|
+
|
95
|
+
def rangeattack(to, time, url, keep=false)
|
96
|
+
wasps = assemble_wasps
|
97
|
+
maximum = wasps.count * WASP::Const::DEFAULT_MAXIMUM_STINGS
|
98
|
+
if to > maximum then
|
99
|
+
print "[WARN]".yellow + " concurrent attack exceeded maximum number of wasps.\n"
|
100
|
+
print "Currently maximum number of attack is " + "#{maximum}".green + ".\n"
|
101
|
+
print "Please breed more wasps.\n"
|
102
|
+
exit false
|
103
|
+
end
|
104
|
+
|
105
|
+
urls = url.split(';')
|
106
|
+
|
107
|
+
stingless_wasps, waves = _rangeattack(wasps, to, time, urls, keep)
|
108
|
+
print "Waves are finished.\n\n"
|
109
|
+
|
110
|
+
#retrive_report(stingless_wasps, true)
|
111
|
+
|
112
|
+
#wave_report(stingless_wasps, waves)
|
113
|
+
|
114
|
+
puts "The wasp is awaiting new orders."
|
115
|
+
end
|
116
|
+
|
117
|
+
def assemble_wasps
|
118
|
+
puts "Assemling wasps."
|
119
|
+
wasps = @ec2.get_wasps
|
120
|
+
wasps
|
121
|
+
end
|
122
|
+
|
123
|
+
def __wave_report(stingless_wasps)
|
124
|
+
puts "Wasps report:"
|
125
|
+
stingless_wasps.each do |wasp|
|
126
|
+
next if wasp[:wavereport].count == 0
|
127
|
+
|
128
|
+
puts "Wasp " + "#{wasp[:instance_id]}".yellow + " report:\n"
|
129
|
+
wasp[:wavereport].each do |w|
|
130
|
+
puts w
|
131
|
+
end
|
132
|
+
print "\n"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def partial_report(stingless_wasps, wave)
|
137
|
+
@weapon = get_weapon
|
138
|
+
wavereport = []
|
139
|
+
|
140
|
+
print "#{wave} ".yellow + "wave report:\n"
|
141
|
+
stingless_wasps.each do |wasp|
|
142
|
+
report = wasp[:wavereport]
|
143
|
+
next if report.nil?
|
144
|
+
case
|
145
|
+
when report.match(/currently not installed/) then
|
146
|
+
print "[WARN]".yellow + " Some of wasps has no weapon to attack.\n"
|
147
|
+
|
148
|
+
when report.match(/Unknown error/) then
|
149
|
+
print "[WARN]".yellow + " Some of wasps lost sight of the target.\n"
|
150
|
+
|
151
|
+
when report.match(/Requests per second/) then
|
152
|
+
wavereport.push @weapon.points(report)
|
153
|
+
|
154
|
+
else
|
155
|
+
print "[WARN]".yellow + " In wave " + "#{wave}".yellow + " Some of wasps lost completely.\n"
|
156
|
+
print "[LOG]\n"
|
157
|
+
print "#{report}\n"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
if wavereport.count < 1 then
|
162
|
+
print "This wave has no report. Target is " + "unavailable".red + ".\n\n"
|
163
|
+
else
|
164
|
+
if @compact then
|
165
|
+
@weapon.compact_summary(wavereport)
|
166
|
+
else
|
167
|
+
@weapon.summary(wavereport)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
def wave_report(stingless_wasps, waves)
|
174
|
+
wavereport = {}
|
175
|
+
i = 1
|
176
|
+
while i <= waves do
|
177
|
+
wavereport[i.to_s] = []
|
178
|
+
i += 1
|
179
|
+
end
|
180
|
+
|
181
|
+
@weapon = get_weapon
|
182
|
+
|
183
|
+
puts "Wave report:"
|
184
|
+
|
185
|
+
stingless_wasps.each do |wasp|
|
186
|
+
next if wasp[:wavereport].nil?
|
187
|
+
wave = 1
|
188
|
+
wasp[:wavereport].each do |report|
|
189
|
+
case
|
190
|
+
when report.match(/currently not installed/) then
|
191
|
+
print "[WARN]".yellow + " Some of wasps has no weapon to attack.\n"
|
192
|
+
|
193
|
+
when report.match(/Unknown error/) then
|
194
|
+
print "[WARN]".yellow + " Some of wasps lost sight of the target.\n"
|
195
|
+
|
196
|
+
when report.match(/Requests per second/) then
|
197
|
+
wavereport[wave.to_s].push(@weapon.points(report))
|
198
|
+
|
199
|
+
else
|
200
|
+
print "[WARN]".yellow + " In wave " + "#{wave}".yellow + " Some of wasps lost completely.\n"
|
201
|
+
print "[LOG]\n"
|
202
|
+
print "#{report}\n"
|
203
|
+
end
|
204
|
+
wave += 1
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
wavereport.keys.each do |report|
|
209
|
+
print "#{report} ".yellow + "wave report:\n"
|
210
|
+
if wavereport[report].count < 1 then
|
211
|
+
print "This wave has no report. Target is " + "unavailable".red + ".\n\n"
|
212
|
+
else
|
213
|
+
if @compact then
|
214
|
+
@weapon.compact_summary(wavereport[report])
|
215
|
+
else
|
216
|
+
@weapon.summary(wavereport[report])
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def mission_report(stingless_wasps)
|
223
|
+
report = []
|
224
|
+
@weapon = get_weapon
|
225
|
+
|
226
|
+
puts "Wasps report:"
|
227
|
+
stingless_wasps.each do |wasp|
|
228
|
+
next if wasp[:rawreport].nil?
|
229
|
+
|
230
|
+
case
|
231
|
+
when wasp[:rawreport].match(/currently not installed/) then
|
232
|
+
print "[WARN]".yellow + " Some of wasps has no weapon to attack.\n"
|
233
|
+
|
234
|
+
when wasp[:rawreport].match(/Unknown error/) then
|
235
|
+
print "[WARN]".yellow + " Some of wasps lost sight of the target.\n"
|
236
|
+
|
237
|
+
when wasp[:rawreport].match(/Requests per second/) then
|
238
|
+
report.push(@weapon.points(wasp[:rawreport]))
|
239
|
+
|
240
|
+
else
|
241
|
+
print "[WARN]".yellow + " Some of wasps lost completely.\n"
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
if report.empty? then
|
246
|
+
print "Nothing to report.\n\n"
|
247
|
+
else
|
248
|
+
@weapon.summary(report)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def status
|
253
|
+
wasps = @ec2.status
|
254
|
+
|
255
|
+
count = 0
|
256
|
+
if wasps.nil? == false then
|
257
|
+
wasps.each do |zone, info|
|
258
|
+
puts "Zone " + "#{zone}".green + ":"
|
259
|
+
threads = []
|
260
|
+
info[:instances].each do |ins|
|
261
|
+
threads << Thread.new(ins) { |i|
|
262
|
+
if @ec2.get_wasp_status(zone, i) == :running then
|
263
|
+
dns = @ec2.get_wasp_domain(zone, i)
|
264
|
+
print "Wasp " + "#{i}".yellow + " is ready to serve. (#{dns})\n"
|
265
|
+
count += 1
|
266
|
+
else
|
267
|
+
print "Wasp " + "#{i}".red + " is hanging around somewhere.\n"
|
268
|
+
end
|
269
|
+
}
|
270
|
+
end
|
271
|
+
threads.each { |aThread| aThread.join }
|
272
|
+
end
|
273
|
+
print "\n#{count}".green + " Wasps are ready to serve.\n"
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
def airfield
|
278
|
+
air = @ec2.get_regions
|
279
|
+
print "Available airfield:\n"
|
280
|
+
if not air.empty? then
|
281
|
+
air.each do |a|
|
282
|
+
print "#{a}".yellow + "\n"
|
283
|
+
end
|
284
|
+
else
|
285
|
+
print "No airfield is available."
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
def get_weapon
|
290
|
+
case
|
291
|
+
when @weapon_name == 'ab' then
|
292
|
+
WASP::WeaponAB.new
|
293
|
+
else
|
294
|
+
WASP::WeaponAB.new
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
def equip
|
299
|
+
wasps = assemble_wasps
|
300
|
+
threads = []
|
301
|
+
print "Wasps are checking its weapon..(This will just take a minute) "
|
302
|
+
wasps.each do |b|
|
303
|
+
threads << Thread.new(b) { |wasp|
|
304
|
+
begin
|
305
|
+
Net::SSH.start(wasp[:instance_name], wasp[:login],
|
306
|
+
:keys => ENV['HOME'] + '/.ssh/' + wasp[:key_name] + '.pem',
|
307
|
+
:verbose => :fatal) do |ssh|
|
308
|
+
weapon = get_weapon
|
309
|
+
weapon.reload if not weapon.check(ssh)
|
310
|
+
end
|
311
|
+
rescue => ex
|
312
|
+
print "[WARN]".yellow + " #{ex.message}\n"
|
313
|
+
end
|
314
|
+
}
|
315
|
+
end
|
316
|
+
|
317
|
+
threads.each { |aThread| aThread.join }
|
318
|
+
puts "OK".green
|
319
|
+
end
|
320
|
+
|
321
|
+
def _rangeattack(wasps, to, time, urls, keep=false)
|
322
|
+
count = time / WASP::Const::DEFAULT_WAVE_TIME
|
323
|
+
awave = to / count
|
324
|
+
wasp_count = wasps.count
|
325
|
+
increase = awave / wasp_count
|
326
|
+
|
327
|
+
coopwasps = []
|
328
|
+
sum = increase
|
329
|
+
|
330
|
+
count.times do
|
331
|
+
coopwasps.push(sum)
|
332
|
+
sum += increase
|
333
|
+
end
|
334
|
+
|
335
|
+
print "Get communication channel for wasps..\n"
|
336
|
+
threads = []
|
337
|
+
wasps.each do |b|
|
338
|
+
threads << Thread.new(b) { |wasp|
|
339
|
+
begin
|
340
|
+
wasp[:ssh] = Net::SSH.start(wasp[:instance_name], wasp[:login],
|
341
|
+
:keys => ENV['HOME'] + '/.ssh/' + wasp[:key_name] + '.pem')
|
342
|
+
print "Wasp " + "#{wasp[:instance_id]}".yellow + " is joining the wasp.\n"
|
343
|
+
rescue => ex
|
344
|
+
puts "[Error] ".red + "#{ex.message}"
|
345
|
+
puts "Cannot reach to " + "#{wasp[:instance_id]}".yellow
|
346
|
+
end
|
347
|
+
}
|
348
|
+
end
|
349
|
+
threads.each { |aThread| aThread.join }
|
350
|
+
|
351
|
+
wave = 1
|
352
|
+
if keep then
|
353
|
+
per_wave = to
|
354
|
+
else
|
355
|
+
per_wave = awave
|
356
|
+
end
|
357
|
+
print "Total " + "#{count}".green + " waves(per *#{per_wave}/#{WASP::Const::DEFAULT_WAVE_TIME}secs) are coming..\n"
|
358
|
+
while wave <= count do
|
359
|
+
if keep then
|
360
|
+
swarm = to
|
361
|
+
else
|
362
|
+
swarm = awave * wave
|
363
|
+
end
|
364
|
+
puts "#{wave}".yellow + " wave of swarm is coming (" + "#{swarm}".yellow + " wasps).."
|
365
|
+
threads = []
|
366
|
+
i = 0
|
367
|
+
wasps.each do |b|
|
368
|
+
url = urls[i%urls.count]
|
369
|
+
i += 1
|
370
|
+
#puts "Attack the target : #{url}"
|
371
|
+
threads << Thread.new(b) { |wasp|
|
372
|
+
begin
|
373
|
+
weapon = get_weapon
|
374
|
+
if weapon.check(wasp[:ssh]) then
|
375
|
+
if keep then
|
376
|
+
coop = to / wasp_count
|
377
|
+
else
|
378
|
+
coop = coopwasps[wave-1]
|
379
|
+
end
|
380
|
+
wasp[:wavereport] = weapon.wavefire(coop, url, wasp[:instance_id], wave, @keepalive, @without_cookie, @header)
|
381
|
+
else
|
382
|
+
puts "Wasp " + "#{wasp[:instance_id]}".yellow + " has no weapon."
|
383
|
+
end
|
384
|
+
rescue => ex
|
385
|
+
print "[WARN]".yellow + " #{ex.message}.\n"
|
386
|
+
end
|
387
|
+
}
|
388
|
+
end
|
389
|
+
threads.each { |aThread| aThread.join }
|
390
|
+
|
391
|
+
partial_report(wasps, wave)
|
392
|
+
|
393
|
+
wave += 1
|
394
|
+
if wave <= count then
|
395
|
+
print "Waiting " + "#{WASP::Const::DEFAULT_COOLDOWN}".yellow + " seconds for cooling down\n"
|
396
|
+
sleep WASP::Const::DEFAULT_COOLDOWN
|
397
|
+
else
|
398
|
+
cool = WASP::Const::DEFAULT_COOLDOWN / 5
|
399
|
+
print "Waiting " + "#{cool}".yellow + " seconds for cooling down\n"
|
400
|
+
sleep cool
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
wasps.each do |wasp|
|
405
|
+
begin
|
406
|
+
wasp[:ssh].close
|
407
|
+
rescue => ex
|
408
|
+
puts "[WARN]".yellow + "#{ex.message}"
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
return wasps, count
|
413
|
+
end
|
414
|
+
|
415
|
+
def _attack (wasps, num, conn, urls, time=nil)
|
416
|
+
coop = conn / wasps.count
|
417
|
+
num = num / wasps.count if not num.nil?
|
418
|
+
|
419
|
+
print "Get communication channel for wasps..\n"
|
420
|
+
threads = []
|
421
|
+
wasps.each do |b|
|
422
|
+
threads << Thread.new(b) { |wasp|
|
423
|
+
begin
|
424
|
+
wasp[:ssh] = Net::SSH.start(wasp[:instance_name], wasp[:login],
|
425
|
+
:keys => ENV['HOME'] + '/.ssh/' + wasp[:key_name] + '.pem')
|
426
|
+
print "Wasp " + "#{wasp[:instance_id]}".yellow + " is joining the wasp.\n"
|
427
|
+
rescue => ex
|
428
|
+
puts "[Error] ".red + "#{ex.message}"
|
429
|
+
puts "Cannot reach to " + "#{wasp[:instance_id]}".yellow
|
430
|
+
end
|
431
|
+
}
|
432
|
+
end
|
433
|
+
threads.each { |aThread| aThread.join }
|
434
|
+
|
435
|
+
threads = []
|
436
|
+
i = 0
|
437
|
+
wasps.each do |b|
|
438
|
+
url = urls[i%urls.count]
|
439
|
+
i += 1
|
440
|
+
threads << Thread.new(b) { |wasp|
|
441
|
+
begin
|
442
|
+
weapon = get_weapon
|
443
|
+
if weapon.check(wasp[:ssh]) then
|
444
|
+
print "Wasp " + "#{wasp[:instance_id]}".yellow + " is firing its stings. Ping ping!\n"
|
445
|
+
|
446
|
+
wasp[:rawreport] = weapon.fire(num, coop, url, wasp[:instance_id], @report, @keepalive, @without_cookie, time, @header)
|
447
|
+
|
448
|
+
print "Wasp " + "#{wasp[:instance_id]}".yellow + " is out of ammo.\n"
|
449
|
+
else
|
450
|
+
print "Wasp " + "#{wasp[:instance_id]}".yellow + " has no weapon.\n"
|
451
|
+
end
|
452
|
+
rescue => ex
|
453
|
+
print "[WARN]".yellow + " #{ex.message}.\n"
|
454
|
+
end
|
455
|
+
}
|
456
|
+
end
|
457
|
+
|
458
|
+
threads.each { |aThread| aThread.join }
|
459
|
+
|
460
|
+
wasps.each do |wasp|
|
461
|
+
begin
|
462
|
+
wasp[:ssh].close
|
463
|
+
rescue => ex
|
464
|
+
puts "[WARN]".yellow + "#{ex.message}"
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
wasps
|
469
|
+
end
|
470
|
+
|
471
|
+
def retrive_report (wasps, iswave=false)
|
472
|
+
threads = []
|
473
|
+
|
474
|
+
print "Retrieve report from wasps.."
|
475
|
+
wasps.each do |b|
|
476
|
+
threads << Thread.new(b) { |wasp|
|
477
|
+
if not iswave then
|
478
|
+
remotepath = "#{wasp[:instance_id]}.zip"
|
479
|
+
localpath = File.dirname(__FILE__) + "/../../report/#{wasp[:instance_id]}.zip"
|
480
|
+
else
|
481
|
+
remotepath = "#{wasp[:instance_id]}-wave.tar.gz"
|
482
|
+
localpath = File.dirname(__FILE__) + "/../../report/#{wasp[:instance_id]}-wave.tar.gz"
|
483
|
+
end
|
484
|
+
|
485
|
+
begin
|
486
|
+
Net::SSH.start(wasp[:instance_name], wasp[:login],
|
487
|
+
:keys => ENV['HOME'] + '/.ssh/' + wasp[:key_name] + '.pem',
|
488
|
+
:verbose => :fatal) do |ssh|
|
489
|
+
ssh.scp.download! remotepath, localpath
|
490
|
+
end
|
491
|
+
rescue => ex
|
492
|
+
print "[WARN]".yellow + " #{ex.message}.\n"
|
493
|
+
end
|
494
|
+
}
|
495
|
+
end
|
496
|
+
|
497
|
+
threads.each { |aThread| aThread.join }
|
498
|
+
begin
|
499
|
+
if not iswave then
|
500
|
+
report_files = File.dirname(__FILE__) + "/../../report/*.zip"
|
501
|
+
else
|
502
|
+
report_files = File.dirname(__FILE__) + "/../../report/*.tar.gz"
|
503
|
+
end
|
504
|
+
now = DateTime.now.to_s
|
505
|
+
now_dir = File.dirname(__FILE__) + "/../../report/" + now
|
506
|
+
Dir.mkdir(now_dir)
|
507
|
+
FileUtils.mv(Dir.glob(report_files), now_dir)
|
508
|
+
rescue => ex
|
509
|
+
print "[WARN]".yellow + " #{ex.message}\n"
|
510
|
+
end
|
511
|
+
puts "OK".green
|
512
|
+
end
|
513
|
+
end
|
514
|
+
end
|