pwn 0.4.512 → 0.4.515

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +17 -11
  3. data/Gemfile +10 -9
  4. data/README.md +2 -2
  5. data/bin/pwn_android_war_dialer +2 -1
  6. data/bin/pwn_fuzz_net_app_proto +4 -1
  7. data/bin/pwn_phone +124 -0
  8. data/bin/pwn_sast +7 -2
  9. data/lib/pwn/plugins/baresip.rb +632 -0
  10. data/lib/pwn/plugins/serial.rb +1 -1
  11. data/lib/pwn/plugins/sock.rb +32 -0
  12. data/lib/pwn/plugins/thread_pool.rb +19 -5
  13. data/lib/pwn/plugins.rb +1 -0
  14. data/lib/pwn/reports/phone.rb +294 -0
  15. data/lib/pwn/reports/sast.rb +4 -4
  16. data/lib/pwn/reports.rb +1 -0
  17. data/lib/pwn/sast/amqp_connect_as_guest.rb +7 -5
  18. data/lib/pwn/sast/apache_file_system_util_api.rb +7 -5
  19. data/lib/pwn/sast/aws.rb +7 -5
  20. data/lib/pwn/sast/banned_function_calls_c.rb +7 -5
  21. data/lib/pwn/sast/base64.rb +7 -5
  22. data/lib/pwn/sast/beef_hook.rb +7 -5
  23. data/lib/pwn/sast/cmd_execution_java.rb +7 -5
  24. data/lib/pwn/sast/cmd_execution_python.rb +7 -5
  25. data/lib/pwn/sast/cmd_execution_ruby.rb +7 -5
  26. data/lib/pwn/sast/cmd_execution_scala.rb +7 -5
  27. data/lib/pwn/sast/csrf.rb +7 -5
  28. data/lib/pwn/sast/deserial_java.rb +7 -5
  29. data/lib/pwn/sast/emoticon.rb +7 -5
  30. data/lib/pwn/sast/eval.rb +7 -5
  31. data/lib/pwn/sast/factory.rb +7 -5
  32. data/lib/pwn/sast/http_authorization_header.rb +7 -5
  33. data/lib/pwn/sast/inner_html.rb +7 -5
  34. data/lib/pwn/sast/keystore.rb +7 -5
  35. data/lib/pwn/sast/location_hash.rb +7 -5
  36. data/lib/pwn/sast/log4j.rb +7 -5
  37. data/lib/pwn/sast/logger.rb +7 -5
  38. data/lib/pwn/sast/outer_html.rb +7 -5
  39. data/lib/pwn/sast/password.rb +7 -5
  40. data/lib/pwn/sast/pom_version.rb +12 -8
  41. data/lib/pwn/sast/port.rb +7 -5
  42. data/lib/pwn/sast/private_key.rb +7 -5
  43. data/lib/pwn/sast/redirect.rb +7 -5
  44. data/lib/pwn/sast/redos.rb +7 -5
  45. data/lib/pwn/sast/shell.rb +7 -5
  46. data/lib/pwn/sast/signature.rb +7 -5
  47. data/lib/pwn/sast/sql.rb +7 -5
  48. data/lib/pwn/sast/ssl.rb +7 -5
  49. data/lib/pwn/sast/sudo.rb +7 -5
  50. data/lib/pwn/sast/task_tag.rb +7 -5
  51. data/lib/pwn/sast/throw_errors.rb +7 -5
  52. data/lib/pwn/sast/token.rb +7 -5
  53. data/lib/pwn/sast/version.rb +7 -5
  54. data/lib/pwn/sast/window_location_hash.rb +7 -5
  55. data/lib/pwn/version.rb +1 -1
  56. data/spec/lib/pwn/reports/phone_spec.rb +15 -0
  57. data/spec/lib/pwn/sast/amqp_connect_as_guest_spec.rb +3 -3
  58. data/spec/lib/pwn/sast/apache_file_system_util_api_spec.rb +3 -3
  59. data/spec/lib/pwn/sast/aws_spec.rb +3 -3
  60. data/spec/lib/pwn/sast/banned_function_calls_c_spec.rb +3 -3
  61. data/spec/lib/pwn/sast/base64_spec.rb +3 -3
  62. data/spec/lib/pwn/sast/beef_hook_spec.rb +3 -3
  63. data/spec/lib/pwn/sast/cmd_execution_java_spec.rb +3 -3
  64. data/spec/lib/pwn/sast/cmd_execution_python_spec.rb +3 -3
  65. data/spec/lib/pwn/sast/cmd_execution_ruby_spec.rb +3 -3
  66. data/spec/lib/pwn/sast/cmd_execution_scala_spec.rb +3 -3
  67. data/spec/lib/pwn/sast/csrf_spec.rb +3 -3
  68. data/spec/lib/pwn/sast/deserial_java_spec.rb +3 -3
  69. data/spec/lib/pwn/sast/emoticon_spec.rb +3 -3
  70. data/spec/lib/pwn/sast/eval_spec.rb +3 -3
  71. data/spec/lib/pwn/sast/factory_spec.rb +3 -3
  72. data/spec/lib/pwn/sast/http_authorization_header_spec.rb +3 -3
  73. data/spec/lib/pwn/sast/inner_html_spec.rb +3 -3
  74. data/spec/lib/pwn/sast/keystore_spec.rb +3 -3
  75. data/spec/lib/pwn/sast/location_hash_spec.rb +3 -3
  76. data/spec/lib/pwn/sast/log4j_spec.rb +3 -3
  77. data/spec/lib/pwn/sast/logger_spec.rb +3 -3
  78. data/spec/lib/pwn/sast/password_spec.rb +3 -3
  79. data/spec/lib/pwn/sast/pom_version_spec.rb +3 -3
  80. data/spec/lib/pwn/sast/port_spec.rb +3 -3
  81. data/spec/lib/pwn/sast/private_key_spec.rb +3 -3
  82. data/spec/lib/pwn/sast/redirect_spec.rb +3 -3
  83. data/spec/lib/pwn/sast/redos_spec.rb +3 -3
  84. data/spec/lib/pwn/sast/shell_spec.rb +3 -3
  85. data/spec/lib/pwn/sast/signature_spec.rb +3 -3
  86. data/spec/lib/pwn/sast/sql_spec.rb +3 -3
  87. data/spec/lib/pwn/sast/ssl_spec.rb +3 -3
  88. data/spec/lib/pwn/sast/sudo_spec.rb +3 -3
  89. data/spec/lib/pwn/sast/task_tag_spec.rb +3 -3
  90. data/spec/lib/pwn/sast/throw_errors_spec.rb +3 -3
  91. data/spec/lib/pwn/sast/token_spec.rb +3 -3
  92. data/spec/lib/pwn/sast/version_spec.rb +3 -3
  93. data/spec/lib/pwn/sast/window_location_hash_spec.rb +3 -3
  94. metadata +40 -21
@@ -0,0 +1,632 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'nokogiri'
4
+
5
+ module PWN
6
+ module Plugins
7
+ # This plugin is used for interacting w/ baresip over a screen session.
8
+ module BareSIP
9
+ @@logger = PWN::Plugins::PWNLogger.create
10
+ @session_data = []
11
+ # Supported Method Parameters::
12
+ # baresip_http_call(
13
+ # http_method: 'optional HTTP method (defaults to GET)
14
+ # cmd: 'required rest call to make per the schema',
15
+ # )
16
+
17
+ private_class_method def self.baresip_http_call(opts = {})
18
+ baresip_obj = opts[:baresip_obj]
19
+ cmd = opts[:cmd]
20
+ http_listen_ip_port = baresip_obj[:http_listen_ip_port]
21
+ baresip_url = "http://#{http_listen_ip_port}"
22
+
23
+ max_conn_attempts = 30
24
+ conn_attempt = 0
25
+
26
+ begin
27
+ conn_attempt += 1
28
+
29
+ rest_client = PWN::Plugins::TransparentBrowser.open(
30
+ browser_type: :rest
31
+ )::Request
32
+
33
+ response = rest_client.execute(
34
+ method: :get,
35
+ url: "#{baresip_url}/?#{cmd}",
36
+ verify_ssl: false
37
+ )
38
+
39
+ Nokogiri::HTML.parse(response)
40
+ rescue Errno::ECONNREFUSED
41
+ raise e if conn_attempt > max_conn_attempts
42
+
43
+ sleep 1
44
+ retry
45
+ end
46
+ rescue StandardError => e
47
+ case e.message
48
+ when '400 Bad Request', '404 Resource Not Found'
49
+ "#{e.message}: #{e.response}"
50
+ else
51
+ raise e
52
+ end
53
+ end
54
+
55
+ # Supported Method Parameters::
56
+ # baresip_obj = PWN::Plugins::BareSIP.start(
57
+ # src_num: 'Optional source phone number displayed',
58
+ # baresip_bin: 'Optional path of baresip binary (Defaults to /usr/bin/baresip)',
59
+ # config_root: 'Optional dir of baresip config (Defaults to ~/.baresip)',
60
+ # session_root: 'Optional dir of baresip session (Defaults to Dir.pwd)',
61
+ # screen_session: 'Optional name of screen session (Defaults baresip)'
62
+ # )
63
+
64
+ public_class_method def self.start(opts = {})
65
+ src_num = opts[:src_num]
66
+
67
+ baresip_bin = opts[:baresip_bin] if File.exist?(
68
+ opts[:baresip_bin].to_s
69
+ )
70
+ baresip_bin ||= '/usr/bin/baresip'
71
+
72
+ baresip_obj = {}
73
+
74
+ session_root = opts[:session_root] if Dir.exist?(opts[:session_root].to_s)
75
+
76
+ config_root = opts[:config_root] if Dir.exist?(
77
+ opts[:config_root].to_s
78
+ )
79
+ config_root ||= "#{Dir.home}/.baresip"
80
+
81
+ config = "#{config_root}/config"
82
+ config_lines = File.readlines(config)
83
+ http_list_entry = config_lines.grep(/^http_listen\s.+$/)
84
+
85
+ raise "no http_listen value found in #{config}." if http_list_entry.empty?
86
+
87
+ http_listen_ip_port = http_list_entry.last.split.grep(/:/).last
88
+
89
+ screen_session = opts[:screen_session]
90
+ screen_session ||= 'baresip'
91
+
92
+ baresip_obj[:config_root] = config_root
93
+ baresip_obj[:http_listen_ip_port] = http_listen_ip_port
94
+ baresip_obj[:session_root] = session_root
95
+ baresip_obj[:screen_session] = screen_session
96
+
97
+ screenlog_file = 'screenlog.txt'
98
+ baresip_obj[:screenlog_file] = screenlog_file
99
+
100
+ # Prefer running baresip in detached screen vs --daemon mode
101
+ # Since sndfile doesn't produce .wav files in --daemon mode
102
+ system(
103
+ 'screen',
104
+ '-T',
105
+ 'xterm',
106
+ '-L',
107
+ '-Logfile',
108
+ screenlog_file,
109
+ '-S',
110
+ screen_session,
111
+ '-d',
112
+ '-m',
113
+ baresip_bin,
114
+ '-f',
115
+ config_root,
116
+ '-e',
117
+ '/insmod httpd',
118
+ '-e',
119
+ '/insmod sndfile'
120
+ )
121
+
122
+ baresip_obj[:session_thread] = init_session_thread(
123
+ baresip_obj: baresip_obj
124
+ )
125
+
126
+ ok = 'registered successfully'
127
+ gone = 'account: No SIP accounts found'
128
+ forb = '403 Forbidden'
129
+
130
+ # TODO: Make this faster.
131
+ print 'Starting baresip...'
132
+ loop do
133
+ break if @session_data.select { |s| s.include?(ok) }.length.positive?
134
+
135
+ next unless dump_session_data.select { |s| s.include?(gone) }.length.positive?
136
+ next unless dump_session_data.select { |s| s.include?(forb) }.length.positive?
137
+
138
+ error = gone if dump_session_data.select { |s| s.include?(gone) }.length.positive?
139
+ error = forbid if dump_session_data.select { |s| s.include?(forb) }.length.positive?
140
+ raise "Something happened when attempting to start baresip: #{error}"
141
+ end
142
+ puts 'ready.'
143
+
144
+ baresip_obj
145
+ rescue StandardError => e
146
+ raise e
147
+ end
148
+
149
+ # Supported Method Parameters::
150
+ # session_thread = init_session_thread(
151
+ # serial_conn: 'required - SerialPort.new object'
152
+ # )
153
+
154
+ private_class_method def self.init_session_thread(opts = {})
155
+ baresip_obj = opts[:baresip_obj]
156
+
157
+ session_root = baresip_obj[:session_root]
158
+ screenlog_file = baresip_obj[:screenlog_file]
159
+
160
+ screenlog = "#{session_root}/#{screenlog_file}"
161
+
162
+ # Spin up a baresip_obj session_thread
163
+ Thread.new do
164
+ loop do
165
+ next unless File.exist?(screenlog)
166
+
167
+ # Continuously consume contents of screenlog.0
168
+ @session_data = File.readlines(screenlog)
169
+ @session_data.delete_if do |line|
170
+ line.include?('ua: using best effort AF: af=AF_INET')
171
+ end
172
+ end
173
+ end
174
+ rescue StandardError => e
175
+ session_thread&.terminate
176
+
177
+ raise e
178
+ end
179
+
180
+ # Supported Method Parameters::
181
+ # session_data = PWN::Plugins::BareSIP.dump_session_data
182
+
183
+ public_class_method def self.dump_session_data
184
+ @session_data
185
+ rescue StandardError => e
186
+ raise e
187
+ end
188
+
189
+ # Supported Method Parameters::
190
+ # session_data = PWN::Plugins::BareSIP.flush_session_data
191
+
192
+ public_class_method def self.flush_session_data
193
+ @session_data.clear
194
+ rescue StandardError => e
195
+ raise e
196
+ end
197
+
198
+ # Supported Method Parameters::
199
+ # cmd_resp = PWN::Plugins::BareSIP.baresip_exec(
200
+ # baresip_obj: 'Required - baresip obj returned from #start method',
201
+ # cmd: 'Required - command to send to baresip HTTP daemon'
202
+ # )
203
+
204
+ public_class_method def self.baresip_exec(opts = {})
205
+ baresip_obj = opts[:baresip_obj]
206
+ cmd = opts[:cmd]
207
+
208
+ baresip_http_call(
209
+ baresip_obj: baresip_obj,
210
+ cmd: cmd
211
+ )
212
+ rescue StandardError => e
213
+ raise e
214
+ end
215
+
216
+ # Supported Method Parameters::
217
+ # PWN::Plugins::BareSIP.stop(
218
+ # screen_session: 'Required - screen session to stop'
219
+ # )
220
+
221
+ public_class_method def self.stop(opts = {})
222
+ baresip_obj = opts[:baresip_obj]
223
+ session_thread = baresip_obj[:session_thread]
224
+ screen_session = baresip_obj[:screen_session]
225
+
226
+ flush_session_data
227
+
228
+ session_thread.terminate
229
+
230
+ system(
231
+ 'screen',
232
+ '-X',
233
+ '-S',
234
+ screen_session,
235
+ 'quit'
236
+ )
237
+
238
+ baresip_obj = nil
239
+ rescue StandardError => e
240
+ raise e
241
+ end
242
+
243
+ # Supported Method Parameters::
244
+ # PWN::Plugins::BareSIP.parse_target_file(
245
+ # target_file: 'Required - txt file containing phone numbers to dial',
246
+ # randomize: 'Optional - randomize list of phone numbers to dial (Defaults to false)'
247
+ # )
248
+
249
+ public_class_method def self.parse_target_file(opts = {})
250
+ target_file = opts[:target_file]
251
+ randomize = opts[:randomize]
252
+
253
+ # Parse entries from target_file and build out our target range
254
+ target_lines = File.readlines(target_file)
255
+ target_range = []
256
+ print 'Initializing targets...'
257
+ target_lines.each do |target_line|
258
+ next if target_line.match?(/^#.*$/)
259
+
260
+ target_line.scrub.strip.chomp.delete('(').delete(')').delete('.').delete('+')
261
+ if target_line.include?('-')
262
+ split_range = target_line.split('-')
263
+ if split_range.length == 2
264
+ from_num = split_range.first.to_i
265
+ to_num = split_range.last.to_i
266
+
267
+ (from_num..to_num).each do |number_in_range|
268
+ target_range.push(number_in_range)
269
+ end
270
+ else
271
+ target_line.scrub.strip.chomp.delete('-')
272
+ end
273
+ else
274
+ target_range.push(target_line.to_i)
275
+ end
276
+ end
277
+ puts 'complete.'
278
+
279
+ # Randomize targets if applicable
280
+ target_range.shuffle! if randomize
281
+
282
+ target_range
283
+ rescue StandardError => e
284
+ raise e
285
+ end
286
+
287
+ # Supported Method Parameters::
288
+ # PWN::Plugins::BareSIP.apply_src_num_rules(
289
+ # target_num: 'Required - destination number to derive source number',
290
+ # src_num_rules: 'Optional - Comma-delimited list of rules for src_num format (i.e. self, same_country, same_area, and/or same_prefix [Defaults to random src_num w/ same length as target_num])'
291
+ # )
292
+
293
+ public_class_method def self.apply_src_num_rules(opts = {})
294
+ src_num_rules = opts[:src_num_rules]
295
+ target_num = opts[:target_num]
296
+
297
+ src_num_rules_arr = []
298
+ if src_num_rules
299
+ src_num_rules_arr = src_num_rules.delete("\s").split(',').map(
300
+ &:to_sym
301
+ )
302
+ end
303
+
304
+ case target_num.to_s.length
305
+ when 10
306
+ # area+prefix+suffix
307
+ country = ''
308
+ when 11
309
+ # 1 digit country+area+prefix+suffix
310
+ country = format('%0.1s', Random.rand(1..9))
311
+ country = target_num.to_s.chars.first if src_num_rules_arr.include?(
312
+ :same_country
313
+ )
314
+ when 12
315
+ # 2 digit country+area+prefix+suffix
316
+ country = format('%0.2s', Random.rand(1..99))
317
+ country = target_num.to_s.chars[0..1].join if src_num_rules_arr.include?(
318
+ :same_country
319
+ )
320
+ when 13
321
+ # 3 digit country+area+prefix+suffix
322
+ country = format('%0.3s', Random.rand(1..999))
323
+ country = target_num.to_s.chars[0..2].join if src_num_rules_arr.include?(
324
+ :same_country
325
+ )
326
+ when 14
327
+ # 4 digit country+area+prefix+suffix
328
+ country = format('%0.4s', Random.rand(1..9999))
329
+ country = target_num.to_s.chars[0..3].join if src_num_rules_arr.include?(
330
+ :same_country
331
+ )
332
+ else
333
+ raise "Target # should be 10-14 digits. Length is: #{target_num.to_s.length}"
334
+ end
335
+
336
+ area = format('%0.3s', Random.rand(200..999))
337
+ area = target_num.to_s.chars[-10..-8].join if src_num_rules_arr.include?(
338
+ :same_area
339
+ )
340
+
341
+ prefix = format('%0.3s', Random.rand(200..999))
342
+ prefix = target_num.to_s.chars[-7..-5].join if src_num_rules_arr.include?(
343
+ :same_prefix
344
+ )
345
+ suffix = format('%0.4s', Random.rand(0..9999))
346
+ src_num = "#{country}#{area}#{prefix}#{suffix}"
347
+ src_num = target_num if src_num_rules_arr.include?(:self)
348
+
349
+ # TODO: Update ~/.baresip/accounts to apply source number
350
+ # config_root = baresip_obj[:config_root]
351
+ # config = "#{config_root}/config"
352
+
353
+ src_num
354
+ rescue StandardError => e
355
+ raise e
356
+ end
357
+
358
+ # Supported Method Parameters::
359
+ # PWN::Plugins::BareSIP.recon(
360
+ # baresip_obj: 'Required - baresip_obj returned from #start method',
361
+ # target_num: 'Required - target destination to recon (i.e. phone number)',
362
+ # src_num_rules: 'Optional - Comma-delimited list of rules for src_num format (i.e. self, same_country, same_area, and/or same_prefix [Defaults to random src_num w/ same length as target_num])',
363
+ # seconds_to_record: 'Optional - Seconds to Record (Defaults to 60)',
364
+ # sox_bin: 'Optional - Path to SoX Binary, the Swiss Army knife of Audio (Defaults to /usr/bin/sox)'
365
+ # )
366
+
367
+ public_class_method def self.recon(opts = {})
368
+ baresip_bin = opts[:baresip_bin]
369
+ session_root = opts[:session_root]
370
+ session_root ||= Dir.pwd
371
+ target_file = opts[:target_file]
372
+ randomize = opts[:randomize]
373
+ src_num_rules = opts[:src_num_rules]
374
+ max_threads = opts[:max_threads].to_i
375
+ max_threads = 3 if max_threads.zero?
376
+ seconds_to_record = opts[:seconds_to_record].to_i
377
+ seconds_to_record = 60 if seconds_to_record.zero?
378
+ sox_bin = opts[:sox_bin] if File.exist?(opts[:sox_bin].to_s)
379
+ sox_bin ||= '/usr/bin/sox'
380
+ waveform_bin = 'waveform'
381
+
382
+ # Intialize empty baresip obj for ensure block below
383
+ baresip_obj = {}
384
+
385
+ # Colors!
386
+ red = "\e[31m"
387
+ green = "\e[32m"
388
+ yellow = "\e[33m"
389
+ cayan = "\e[36m"
390
+ end_of_color = "\e[0m"
391
+
392
+ target_range = parse_target_file(
393
+ target_file: target_file,
394
+ randomize: randomize
395
+ )
396
+
397
+ results_hash = {
398
+ session_started: Time.now.strftime('%Y-%m-%d_%H.%M.%S'),
399
+ data: []
400
+ }
401
+
402
+ # TODO: Multi-thread!
403
+ # mutex = Mutex.new
404
+ # PWN::Plugins::ThreadPool.fill(
405
+ # enumerable_array: target_range,
406
+ # max_threads: max_threads
407
+ # ) do |target_num|
408
+ target_range.each_with_index do |target_num, index|
409
+ call_count = index + 1
410
+ puts "Call #{call_count} of #{target_range.length}..."
411
+
412
+ # Change to session_root _before_ starting to ensure
413
+ # wav files are stored in the proper location
414
+ Dir.chdir(session_root)
415
+
416
+ src_num = apply_src_num_rules(
417
+ target_num: target_num,
418
+ src_num_rules: src_num_rules
419
+ )
420
+
421
+ call_info_hash = {}
422
+
423
+ call_started = Time.now.strftime('%Y-%m-%d_%H.%M.%S')
424
+
425
+ call_info_hash[:call_started] = call_started
426
+ call_info_hash[:src_num] = src_num
427
+ call_info_hash[:src_num_rules] = src_num_rules
428
+ call_info_hash[:target_num] = target_num
429
+
430
+ target_num_root = "#{session_root}/#{target_num}-#{call_started}"
431
+
432
+ # TODO: Determine what to do w/ existing Directory
433
+ Dir.mkdir(target_num_root)
434
+ Dir.chdir(target_num_root)
435
+
436
+ # Start baresip in detached screen to support commands over HTTP
437
+ # and call recording to wav files
438
+ baresip_obj = start(
439
+ src_num: src_num,
440
+ baresip_bin: baresip_bin,
441
+ session_root: target_num_root,
442
+ screen_session: "#{File.basename($PROGRAM_NAME)}-#{target_num}"
443
+ )
444
+
445
+ # session_root = baresip_obj[:session_root]
446
+ config_root = baresip_obj[:config_root]
447
+ config = "#{config_root}/config"
448
+
449
+ puts "#{green}#{call_started} >>>#{end_of_color}"
450
+ puts "#{yellow}dialing #{target_num}#{end_of_color}"
451
+
452
+ cmd_resp = baresip_exec(
453
+ baresip_obj: baresip_obj,
454
+ cmd: "/dial #{target_num}\r\n"
455
+ )
456
+ puts "/dial #{target_num} RESP:"
457
+ puts cmd_resp.xpath('//pre').text
458
+
459
+ cmd_resp = baresip_exec(
460
+ baresip_obj: baresip_obj,
461
+ cmd: "/listcalls\r\n"
462
+ )
463
+ puts '/listcalls RESP:'
464
+ puts cmd_resp.xpath('//pre').text
465
+
466
+ puts red
467
+ # Conditions to proceed less than seconds_to_record
468
+ terminated = 'terminated (duration:'
469
+ unavail = '503 Service Unavailable'
470
+ not_found = 'session closed: 404 Not Found'
471
+
472
+ reason = 'recording limit reached'
473
+ seconds_recorded = 0
474
+ seconds_to_record.downto(1).each do |countdown|
475
+ seconds_recorded += 1
476
+ print "#{seconds_to_record}s to record - remaining: #{format('%-9.9s', countdown)}"
477
+ print "\r"
478
+
479
+ if dump_session_data.select { |s| s.include?(terminated) }.length.positive?
480
+ reason = 'call terminated by other party'
481
+ break
482
+ end
483
+
484
+ if dump_session_data.select { |s| s.include?(unavail) }.length.positive?
485
+ reason = 'SIP 503 (service unavailable)'
486
+ break
487
+ end
488
+
489
+ if dump_session_data.select { |s| s.include?(not_found) }.length.positive?
490
+ reason = 'SIP 404 (not found)'
491
+ break
492
+ end
493
+
494
+ sleep 1
495
+ end
496
+ call_info_hash[:seconds_recorded] = seconds_recorded
497
+ puts end_of_color
498
+
499
+ call_stopped = Time.now.strftime('%Y-%m-%d_%H.%M.%S')
500
+ puts "\n#{green}#{call_stopped} >>> #{reason} #{target_num}#{end_of_color}"
501
+ call_info_hash[:call_stopped] = call_stopped
502
+ call_info_hash[:reason] = reason
503
+
504
+ recording = ''
505
+ # BUGFIX: Sometimes recordings aren't generated
506
+ # Perhaps because previous session wasn't stopped properly?
507
+ Dir.entries(target_num_root).each do |entry|
508
+ recording = entry if entry.match(/^dump-.+-dec.wav/)
509
+ File.delete(entry) if entry.match(/^dump-.+-enc\.wav$/) && File.exist?(entry)
510
+ end
511
+
512
+ call_info_hash[:recording] = '--'
513
+ call_info_hash[:waveform] = '--'
514
+ call_info_hash[:spectrogram] = '--'
515
+
516
+ unless recording.empty?
517
+ puts cayan
518
+
519
+ call_info_hash[:recording] = "#{target_num}-#{call_started}/#{recording}"
520
+
521
+ spectrogram = "#{recording}-spectrogram.png"
522
+ print "Generating Audio Spectrogram for #{recording}..."
523
+ system(
524
+ sox_bin,
525
+ recording,
526
+ '-n',
527
+ 'spectrogram',
528
+ '-o',
529
+ spectrogram,
530
+ '-d',
531
+ seconds_to_record.to_s
532
+ )
533
+ puts 'complete.'
534
+ call_info_hash[:spectrogram] = "#{target_num}-#{call_started}/#{spectrogram}"
535
+
536
+ waveform = "#{recording}-waveform.png"
537
+ print "Generating Audio Waveform for #{recording}..."
538
+ system(
539
+ waveform_bin,
540
+ '--method',
541
+ 'peak',
542
+ '--color',
543
+ '#FF0000',
544
+ '--background',
545
+ '#000000',
546
+ '--force',
547
+ recording,
548
+ waveform
549
+ )
550
+ puts 'complete.'
551
+ call_info_hash[:waveform] = "#{target_num}-#{call_started}/#{waveform}"
552
+ puts end_of_color
553
+ end
554
+
555
+ # Push Call Results to results_hash[:data]
556
+ # mutex.synchronize do
557
+ # results_hash[:data].push(call_info_hash)
558
+ # end
559
+ results_hash[:data].push(call_info_hash)
560
+
561
+ puts "call termination reason: #{reason}"
562
+ # Stop call to cd into next session root
563
+ stop(baresip_obj: baresip_obj)
564
+
565
+ seconds_to_delay_between_pool_of_calls = Random.rand(1..9)
566
+ seconds_to_delay_between_pool_of_calls.downto(1).each do |countdown|
567
+ print "#{red}ZZZ #{seconds_to_delay_between_pool_of_calls}s until next pool of calls: #{format('%-9.9s', countdown)}#{end_of_color}"
568
+ print "\r"
569
+ sleep 1
570
+ end
571
+ puts "\n#{green}waking up for next call \\o/#{end_of_color}"
572
+ puts "\n\n\n"
573
+ end
574
+ results_hash[:session_ended] = Time.now.strftime('%Y-%m-%d_%H.%M.%S')
575
+
576
+ results_hash
577
+ rescue StandardError => e
578
+ raise e
579
+ ensure
580
+ stop(baresip_obj: baresip_obj) unless baresip_obj.empty?
581
+ end
582
+
583
+ # Supported Method Parameters::
584
+ # PWN::Plugins::BareSIP.killall
585
+
586
+ public_class_method def self.killall
587
+ system(
588
+ 'killall',
589
+ '-15',
590
+ 'baresip'
591
+ )
592
+ rescue StandardError => e
593
+ raise e
594
+ end
595
+
596
+ # Author(s):: 0day Inc. <request.pentest@0dayinc.com>
597
+
598
+ public_class_method def self.authors
599
+ "AUTHOR(S):
600
+ 0day Inc. <request.pentest@0dayinc.com>
601
+ "
602
+ end
603
+
604
+ # Display Usage for this Module
605
+
606
+ public_class_method def self.help
607
+ puts "USAGE:
608
+ baresip_obj = #{self}.start(
609
+ baresip_bin: 'Optional path of baresip binary (Defaults to /usr/bin/baresip)'
610
+ config_root: 'Optional dir of baresip config (Defaults to ~/.baresip)',
611
+ screen_session: 'Optional name of screen session (Defaults baresip)'
612
+ )
613
+
614
+ session_data_arr = #{self}.dump_session_data
615
+
616
+ cmd_resp = #{self}.baresip_exec(
617
+ baresip_obj: 'Required - baresip obj returned from #start method',
618
+ cmd: 'Required - command to send to baresip HTTP daemon'
619
+ )
620
+
621
+ stopped_bool = #{self}.stop(
622
+ screen_session: 'Required - screen session to stop'
623
+ )
624
+
625
+ #{self}.killall
626
+
627
+ #{self}.authors
628
+ "
629
+ end
630
+ end
631
+ end
632
+ end
@@ -213,7 +213,7 @@ module PWN
213
213
  cmd_response_arr.map(&:strip)
214
214
  rescue StandardError => e
215
215
  # Flush Responses for Next Request
216
- flush_session_data(serial_obj: serial_obj)
216
+ flush_session_data
217
217
 
218
218
  raise e
219
219
  end
@@ -47,6 +47,32 @@ module PWN
47
47
  raise e
48
48
  end
49
49
 
50
+ # Supported Method Parameters::
51
+ # PWN::Plugins::Sock.check_port_availability(
52
+ # port: 'required - target port',
53
+ # server_ip: 'optional - target host or ip to check (Defaults to 127.0.0.1)',
54
+ # protocol: 'optional - :tcp || :udp (defaults to tcp)'
55
+ # )
56
+
57
+ public_class_method def self.check_port_availability(opts = {})
58
+ server_ip = opts[:server_ip]
59
+ server_ip ||= '127.0.0.1'
60
+ port = opts[:port]
61
+ protocol = opts[:protocol]
62
+ protocol ||= :tcp
63
+
64
+ ct = 0.1
65
+ s = Socket.tcp(server_ip, port, connect_timeout: ct) if protocol == :tcp
66
+ s = Socket.udp(server_ip, port, connect_timeout: ct) if protocol == :udp
67
+ s.close
68
+
69
+ true
70
+ rescue Errno::ECONNREFUSED,
71
+ Errno::EHOSTUNREACH,
72
+ Errno::ETIMEDOUT
73
+ false
74
+ end
75
+
50
76
  # Supported Method Parameters::
51
77
  # PWN::Plugins::Sock.listen(
52
78
  # server_ip: 'required - target host or ip to listen',
@@ -137,6 +163,12 @@ module PWN
137
163
  tls: 'optional - boolean connect to target socket using TLS (defaults to false)'
138
164
  )
139
165
 
166
+ #{self}.check_port_availability(
167
+ port: 'required - target port',
168
+ server_ip: 'optional - target host or ip to check (Defaults to 127.0.0.1)',
169
+ protocol: 'optional - :tcp || :udp (defaults to tcp)'
170
+ )
171
+
140
172
  #{self}.listen(
141
173
  server_ip: 'required - target host or ip to listen',
142
174
  port: 'required - target port',