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.
@@ -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