pwn 0.4.544 → 0.4.545
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.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +19 -13
- data/Gemfile +3 -3
- data/README.md +2 -2
- data/bin/pwn_phone +1 -1
- data/lib/pwn/plugins/baresip.rb +265 -226
- data/lib/pwn/plugins/sock.rb +3 -3
- data/lib/pwn/plugins/thread_pool.rb +4 -1
- data/lib/pwn/reports/phone.rb +12 -3
- data/lib/pwn/version.rb +1 -1
- metadata +8 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a482ad2be090b5fc318d1048fd48f6936906ec7f50ec3e5732c7fbda412f17e0
|
|
4
|
+
data.tar.gz: 8a5d6ad28f6e45bbcbffe34e29a3f558f142b054c46360a0ebabf3dd8b66b7c0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3557a95e19c60f023ef6099c58351cee88c3e98f675de6c8e3806e360a7c0eeb4a26fcd0b44a6d306829d8460ba604e6b303e1593a6d26c923ea89e60cc1be69
|
|
7
|
+
data.tar.gz: 1a58439197b28adecbfd79a67922bd39a655df2f6cc1d2a83508c7c47a58bdb77251619ba3bb702f6827c4bd146246d8e2ff9f949b970851748a112f36982660
|
data/.rubocop_todo.yml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# This configuration was generated by
|
|
2
2
|
# `rubocop --auto-gen-config`
|
|
3
|
-
# on 2022-
|
|
3
|
+
# on 2022-09-23 22:54:28 UTC using RuboCop version 1.36.0.
|
|
4
4
|
# The point is for the user to remove these configuration records
|
|
5
5
|
# one by one as the offenses are removed from the code base.
|
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
|
@@ -17,51 +17,51 @@ Layout/LineContinuationSpacing:
|
|
|
17
17
|
- 'packer/provisioners/wpscan.rb'
|
|
18
18
|
- 'vagrant/provisioners/beef.rb'
|
|
19
19
|
|
|
20
|
-
# Offense count:
|
|
20
|
+
# Offense count: 265
|
|
21
21
|
Lint/UselessAssignment:
|
|
22
22
|
Enabled: false
|
|
23
23
|
|
|
24
|
-
# Offense count:
|
|
24
|
+
# Offense count: 266
|
|
25
25
|
# Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods, CountRepeatedAttributes.
|
|
26
26
|
Metrics/AbcSize:
|
|
27
27
|
Max: 328
|
|
28
28
|
|
|
29
|
-
# Offense count:
|
|
29
|
+
# Offense count: 71
|
|
30
30
|
# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, AllowedMethods, AllowedPatterns, IgnoredMethods.
|
|
31
31
|
# AllowedMethods: refine
|
|
32
32
|
Metrics/BlockLength:
|
|
33
33
|
Max: 196
|
|
34
34
|
|
|
35
|
-
# Offense count:
|
|
35
|
+
# Offense count: 46
|
|
36
36
|
# Configuration parameters: CountBlocks.
|
|
37
37
|
Metrics/BlockNesting:
|
|
38
38
|
Max: 5
|
|
39
39
|
|
|
40
|
-
# Offense count:
|
|
40
|
+
# Offense count: 96
|
|
41
41
|
# Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods.
|
|
42
42
|
Metrics/CyclomaticComplexity:
|
|
43
43
|
Max: 231
|
|
44
44
|
|
|
45
|
-
# Offense count:
|
|
45
|
+
# Offense count: 484
|
|
46
46
|
# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, AllowedMethods, AllowedPatterns, IgnoredMethods.
|
|
47
47
|
Metrics/MethodLength:
|
|
48
48
|
Max: 466
|
|
49
49
|
|
|
50
|
-
# Offense count:
|
|
50
|
+
# Offense count: 48
|
|
51
51
|
# Configuration parameters: CountComments, CountAsOne.
|
|
52
52
|
Metrics/ModuleLength:
|
|
53
53
|
Max: 1186
|
|
54
54
|
|
|
55
|
-
# Offense count:
|
|
55
|
+
# Offense count: 88
|
|
56
56
|
# Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods.
|
|
57
57
|
Metrics/PerceivedComplexity:
|
|
58
58
|
Max: 51
|
|
59
59
|
|
|
60
|
-
# Offense count:
|
|
60
|
+
# Offense count: 164
|
|
61
61
|
Style/ClassVars:
|
|
62
62
|
Enabled: false
|
|
63
63
|
|
|
64
|
-
# Offense count:
|
|
64
|
+
# Offense count: 285
|
|
65
65
|
# This cop supports safe autocorrection (--autocorrect).
|
|
66
66
|
# Configuration parameters: EnforcedStyle, SingleLineConditionsOnly, IncludeTernaryExpressions.
|
|
67
67
|
# SupportedStyles: assign_to_condition, assign_inside_condition
|
|
@@ -74,6 +74,12 @@ Style/ExplicitBlockArgument:
|
|
|
74
74
|
Exclude:
|
|
75
75
|
- 'lib/pwn/plugins/nmap_it.rb'
|
|
76
76
|
|
|
77
|
+
# Offense count: 1
|
|
78
|
+
# This cop supports safe autocorrection (--autocorrect).
|
|
79
|
+
Style/IfUnlessModifier:
|
|
80
|
+
Exclude:
|
|
81
|
+
- 'lib/pwn/plugins/baresip.rb'
|
|
82
|
+
|
|
77
83
|
# Offense count: 1
|
|
78
84
|
# This cop supports safe autocorrection (--autocorrect).
|
|
79
85
|
Style/RedundantBegin:
|
|
@@ -87,12 +93,12 @@ Style/RedundantCondition:
|
|
|
87
93
|
- 'bin/pwn_simple_http_server'
|
|
88
94
|
- 'lib/pwn/plugins/packet.rb'
|
|
89
95
|
|
|
90
|
-
# Offense count:
|
|
96
|
+
# Offense count: 45
|
|
91
97
|
# This cop supports unsafe autocorrection (--autocorrect-all).
|
|
92
98
|
Style/SlicingWithRange:
|
|
93
99
|
Enabled: false
|
|
94
100
|
|
|
95
|
-
# Offense count:
|
|
101
|
+
# Offense count: 577
|
|
96
102
|
# This cop supports safe autocorrection (--autocorrect).
|
|
97
103
|
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns, IgnoredPatterns.
|
|
98
104
|
# URISchemes: http, https
|
data/Gemfile
CHANGED
|
@@ -57,12 +57,12 @@ gem 'rbvmomi', '3.0.0'
|
|
|
57
57
|
gem 'rdoc', '6.4.0'
|
|
58
58
|
gem 'rest-client', '2.1.0'
|
|
59
59
|
gem 'rex', '2.0.13'
|
|
60
|
-
gem 'rmagick', '4.
|
|
60
|
+
gem 'rmagick', '4.3.0'
|
|
61
61
|
gem 'rspec', '3.11.0'
|
|
62
62
|
gem 'rtesseract', '3.1.2'
|
|
63
63
|
gem 'rubocop', '1.36.0'
|
|
64
64
|
gem 'rubocop-rake', '0.6.0'
|
|
65
|
-
gem 'rubocop-rspec', '2.13.
|
|
65
|
+
gem 'rubocop-rspec', '2.13.2'
|
|
66
66
|
gem 'ruby-audio', '1.6.1'
|
|
67
67
|
gem 'ruby-nmap', '0.10.0'
|
|
68
68
|
gem 'ruby-saml', '1.14.0'
|
|
@@ -70,7 +70,7 @@ gem 'rvm', '1.11.3.9'
|
|
|
70
70
|
gem 'savon', '2.13.1'
|
|
71
71
|
gem 'selenium-devtools', '0.105.0'
|
|
72
72
|
gem 'serialport', '1.3.2'
|
|
73
|
-
gem 'sinatra', '
|
|
73
|
+
gem 'sinatra', '3.0.1'
|
|
74
74
|
gem 'slack-ruby-client', '1.1.0'
|
|
75
75
|
gem 'socksify', '1.7.1'
|
|
76
76
|
gem 'spreadsheet', '1.3.0'
|
data/README.md
CHANGED
|
@@ -37,7 +37,7 @@ $ rvm use ruby-3.1.2@pwn
|
|
|
37
37
|
$ rvm list gemsets
|
|
38
38
|
$ gem install --verbose pwn
|
|
39
39
|
$ pwn
|
|
40
|
-
pwn[v0.4.
|
|
40
|
+
pwn[v0.4.545]:001 >>> PWN.help
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
[](https://youtu.be/G7iLUY4FzsI)
|
|
@@ -52,7 +52,7 @@ $ rvm use ruby-3.1.2@pwn
|
|
|
52
52
|
$ gem uninstall --all --executables pwn
|
|
53
53
|
$ gem install --verbose pwn
|
|
54
54
|
$ pwn
|
|
55
|
-
pwn[v0.4.
|
|
55
|
+
pwn[v0.4.545]:001 >>> PWN.help
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
|
data/bin/pwn_phone
CHANGED
|
@@ -22,7 +22,7 @@ OptionParser.new do |options|
|
|
|
22
22
|
opts[:seconds_to_record] = s
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
options.on('-TTHREADS', '--max-threads=THREADS', '<Optional # Calls to Run Simultaneously (Defaults to 3
|
|
25
|
+
options.on('-TTHREADS', '--max-threads=THREADS', '<Optional # Calls to Run Simultaneously (Defaults to 3)>') do |t|
|
|
26
26
|
opts[:max_threads] = t
|
|
27
27
|
end
|
|
28
28
|
|
data/lib/pwn/plugins/baresip.rb
CHANGED
|
@@ -17,7 +17,7 @@ module PWN
|
|
|
17
17
|
private_class_method def self.baresip_http_call(opts = {})
|
|
18
18
|
baresip_obj = opts[:baresip_obj]
|
|
19
19
|
cmd = opts[:cmd]
|
|
20
|
-
http_listen_ip_port =
|
|
20
|
+
http_listen_ip_port = opts[:http_listen_ip_port]
|
|
21
21
|
baresip_url = "http://#{http_listen_ip_port}"
|
|
22
22
|
|
|
23
23
|
max_conn_attempts = 30
|
|
@@ -37,7 +37,7 @@ module PWN
|
|
|
37
37
|
)
|
|
38
38
|
|
|
39
39
|
Nokogiri::HTML.parse(response)
|
|
40
|
-
rescue Errno::ECONNREFUSED
|
|
40
|
+
rescue Errno::ECONNREFUSED => e
|
|
41
41
|
raise e if conn_attempt > max_conn_attempts
|
|
42
42
|
|
|
43
43
|
sleep 1
|
|
@@ -58,6 +58,7 @@ module PWN
|
|
|
58
58
|
# baresip_bin: 'Optional path of baresip binary (Defaults to /usr/bin/baresip)',
|
|
59
59
|
# config_root: 'Optional dir of baresip config (Defaults to ~/.baresip)',
|
|
60
60
|
# session_root: 'Optional dir of baresip session (Defaults to Dir.pwd)',
|
|
61
|
+
# screenlog_path: 'Optional path of screenlog file (Defaults to screenlog.txt)',
|
|
61
62
|
# screen_session: 'Optional name of screen session (Defaults baresip)'
|
|
62
63
|
# )
|
|
63
64
|
|
|
@@ -69,8 +70,6 @@ module PWN
|
|
|
69
70
|
)
|
|
70
71
|
baresip_bin ||= '/usr/bin/baresip'
|
|
71
72
|
|
|
72
|
-
baresip_obj = {}
|
|
73
|
-
|
|
74
73
|
session_root = opts[:session_root] if Dir.exist?(opts[:session_root].to_s)
|
|
75
74
|
|
|
76
75
|
config_root = opts[:config_root] if Dir.exist?(
|
|
@@ -84,7 +83,34 @@ module PWN
|
|
|
84
83
|
|
|
85
84
|
raise "no http_listen value found in #{config}." if http_list_entry.empty?
|
|
86
85
|
|
|
87
|
-
|
|
86
|
+
# Update http_listen value in respective config with random available port
|
|
87
|
+
random_port = -1
|
|
88
|
+
port_in_use = true
|
|
89
|
+
while port_in_use
|
|
90
|
+
random_port = Random.rand(1024..65_535)
|
|
91
|
+
port_in_use = PWN::Plugins::Sock.check_port_in_use(port: random_port)
|
|
92
|
+
end
|
|
93
|
+
http_listen_ip_port = "127.0.0.1:#{random_port}"
|
|
94
|
+
|
|
95
|
+
updated_config_content = ''
|
|
96
|
+
File.read(config).each_line do |line|
|
|
97
|
+
this_config_line = line
|
|
98
|
+
# Only allow one call per thread.
|
|
99
|
+
this_config_line = "call_max_calls 1\n" if line.match?(/^call_max_calls\s.+$/)
|
|
100
|
+
|
|
101
|
+
# One random HTTP listener / thread
|
|
102
|
+
if line.match?(/^http_listen\s.+$/)
|
|
103
|
+
this_config_line = "http_listen #{http_listen_ip_port}\n"
|
|
104
|
+
end
|
|
105
|
+
updated_config_content = "#{updated_config_content}#{this_config_line}"
|
|
106
|
+
end
|
|
107
|
+
File.write(config, updated_config_content)
|
|
108
|
+
|
|
109
|
+
baresip_obj = {}
|
|
110
|
+
|
|
111
|
+
screenlog_path = opts[:screenlog_path]
|
|
112
|
+
screenlog_path ||= "#{session_root}/screenlog.txt"
|
|
113
|
+
baresip_obj[:screenlog_path] = screenlog_path
|
|
88
114
|
|
|
89
115
|
screen_session = opts[:screen_session]
|
|
90
116
|
screen_session ||= 'baresip'
|
|
@@ -94,9 +120,6 @@ module PWN
|
|
|
94
120
|
baresip_obj[:session_root] = session_root
|
|
95
121
|
baresip_obj[:screen_session] = screen_session
|
|
96
122
|
|
|
97
|
-
screenlog_file = 'screenlog.txt'
|
|
98
|
-
baresip_obj[:screenlog_file] = screenlog_file
|
|
99
|
-
|
|
100
123
|
# Prefer running baresip in detached screen vs --daemon mode
|
|
101
124
|
# Since sndfile doesn't produce .wav files in --daemon mode
|
|
102
125
|
system(
|
|
@@ -105,7 +128,7 @@ module PWN
|
|
|
105
128
|
'xterm',
|
|
106
129
|
'-L',
|
|
107
130
|
'-Logfile',
|
|
108
|
-
|
|
131
|
+
screenlog_path,
|
|
109
132
|
'-S',
|
|
110
133
|
screen_session,
|
|
111
134
|
'-d',
|
|
@@ -116,7 +139,8 @@ module PWN
|
|
|
116
139
|
'-e',
|
|
117
140
|
'/insmod httpd',
|
|
118
141
|
'-e',
|
|
119
|
-
'/insmod sndfile'
|
|
142
|
+
'/insmod sndfile',
|
|
143
|
+
'-v'
|
|
120
144
|
)
|
|
121
145
|
|
|
122
146
|
baresip_obj[:session_thread] = init_session_thread(
|
|
@@ -155,17 +179,15 @@ module PWN
|
|
|
155
179
|
baresip_obj = opts[:baresip_obj]
|
|
156
180
|
|
|
157
181
|
session_root = baresip_obj[:session_root]
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
screenlog = "#{session_root}/#{screenlog_file}"
|
|
182
|
+
screenlog_path = baresip_obj[:screenlog_path]
|
|
161
183
|
|
|
162
184
|
# Spin up a baresip_obj session_thread
|
|
163
185
|
Thread.new do
|
|
164
186
|
loop do
|
|
165
|
-
next unless File.exist?(
|
|
187
|
+
next unless File.exist?(screenlog_path)
|
|
166
188
|
|
|
167
|
-
# Continuously consume contents of
|
|
168
|
-
@session_data = File.readlines(
|
|
189
|
+
# Continuously consume contents of screenlog_path
|
|
190
|
+
@session_data = File.readlines(screenlog_path)
|
|
169
191
|
@session_data.delete_if do |line|
|
|
170
192
|
line.include?('ua: using best effort AF: af=AF_INET')
|
|
171
193
|
end
|
|
@@ -203,10 +225,12 @@ module PWN
|
|
|
203
225
|
|
|
204
226
|
public_class_method def self.baresip_exec(opts = {})
|
|
205
227
|
baresip_obj = opts[:baresip_obj]
|
|
228
|
+
http_listen_ip_port = baresip_obj[:http_listen_ip_port]
|
|
206
229
|
cmd = opts[:cmd]
|
|
207
230
|
|
|
208
231
|
baresip_http_call(
|
|
209
232
|
baresip_obj: baresip_obj,
|
|
233
|
+
http_listen_ip_port: http_listen_ip_port,
|
|
210
234
|
cmd: cmd
|
|
211
235
|
)
|
|
212
236
|
rescue StandardError => e
|
|
@@ -227,15 +251,11 @@ module PWN
|
|
|
227
251
|
|
|
228
252
|
session_thread.terminate
|
|
229
253
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
screen_session,
|
|
235
|
-
'quit'
|
|
254
|
+
puts "STOPPING #{baresip_obj[:screen_session]}"
|
|
255
|
+
cmd_resp = baresip_exec(
|
|
256
|
+
baresip_obj: baresip_obj,
|
|
257
|
+
cmd: "/quit\r\n"
|
|
236
258
|
)
|
|
237
|
-
|
|
238
|
-
baresip_obj = nil
|
|
239
259
|
rescue StandardError => e
|
|
240
260
|
raise e
|
|
241
261
|
end
|
|
@@ -372,15 +392,11 @@ module PWN
|
|
|
372
392
|
|
|
373
393
|
# Supported Method Parameters::
|
|
374
394
|
# PWN::Plugins::BareSIP.recon(
|
|
375
|
-
# baresip_obj: 'Required - baresip_obj returned from #start method',
|
|
376
|
-
# target_num: 'Required - target destination to recon (i.e. phone number)',
|
|
377
|
-
# 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])',
|
|
378
|
-
# seconds_to_record: 'Optional - Seconds to Record (Defaults to 60)',
|
|
379
|
-
# sox_bin: 'Optional - Path to SoX Binary, the Swiss Army knife of Audio (Defaults to /usr/bin/sox)'
|
|
380
395
|
# )
|
|
381
396
|
|
|
382
|
-
public_class_method def self.
|
|
397
|
+
public_class_method def self.dial_target_in_list(opts = {})
|
|
383
398
|
baresip_bin = opts[:baresip_bin]
|
|
399
|
+
target_num = opts[:target_num]
|
|
384
400
|
|
|
385
401
|
config_root = opts[:config_root] if Dir.exist?(
|
|
386
402
|
opts[:config_root].to_s
|
|
@@ -389,15 +405,16 @@ module PWN
|
|
|
389
405
|
|
|
390
406
|
session_root = opts[:session_root]
|
|
391
407
|
session_root ||= Dir.pwd
|
|
392
|
-
|
|
408
|
+
|
|
393
409
|
randomize = opts[:randomize]
|
|
394
410
|
src_num_rules = opts[:src_num_rules]
|
|
395
|
-
|
|
396
|
-
max_threads = 3 if max_threads.zero?
|
|
411
|
+
|
|
397
412
|
seconds_to_record = opts[:seconds_to_record].to_i
|
|
398
413
|
seconds_to_record = 60 if seconds_to_record.zero?
|
|
414
|
+
|
|
399
415
|
sox_bin = opts[:sox_bin] if File.exist?(opts[:sox_bin].to_s)
|
|
400
416
|
sox_bin ||= '/usr/bin/sox'
|
|
417
|
+
|
|
401
418
|
waveform_bin = 'waveform'
|
|
402
419
|
|
|
403
420
|
# Intialize empty baresip obj for ensure block below
|
|
@@ -410,222 +427,243 @@ module PWN
|
|
|
410
427
|
cayan = "\e[36m"
|
|
411
428
|
end_of_color = "\e[0m"
|
|
412
429
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
430
|
+
config_root_for_target_num = "#{config_root}-#{target_num}"
|
|
431
|
+
FileUtils.cp_r(config_root, config_root_for_target_num)
|
|
432
|
+
src_num = apply_src_num_rules(
|
|
433
|
+
config_root: config_root_for_target_num,
|
|
434
|
+
target_num: target_num,
|
|
435
|
+
src_num_rules: src_num_rules
|
|
416
436
|
)
|
|
417
437
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
#
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
call_info_hash = {}
|
|
438
|
+
call_resp_hash = {}
|
|
439
|
+
|
|
440
|
+
call_started = Time.now.strftime('%Y-%m-%d_%H.%M.%S')
|
|
441
|
+
|
|
442
|
+
call_resp_hash[:call_started] = call_started
|
|
443
|
+
call_resp_hash[:src_num] = src_num
|
|
444
|
+
call_resp_hash[:src_num_rules] = src_num_rules
|
|
445
|
+
call_resp_hash[:target_num] = target_num
|
|
446
|
+
target_num_root = "#{session_root}/#{target_num}-#{call_started}"
|
|
447
|
+
Dir.mkdir(target_num_root)
|
|
448
|
+
|
|
449
|
+
screenlog_path = "#{target_num_root}/screenlog-#{target_num}.txt"
|
|
450
|
+
screen_session = "#{File.basename($PROGRAM_NAME)}-#{target_num}"
|
|
451
|
+
|
|
452
|
+
# Start baresip in detached screen to support commands over HTTP
|
|
453
|
+
# and call recording to wav files
|
|
454
|
+
baresip_obj = start(
|
|
455
|
+
src_num: src_num,
|
|
456
|
+
baresip_bin: baresip_bin,
|
|
457
|
+
config_root: config_root_for_target_num,
|
|
458
|
+
session_root: session_root,
|
|
459
|
+
screenlog_path: screenlog_path,
|
|
460
|
+
screen_session: screen_session
|
|
461
|
+
)
|
|
444
462
|
|
|
445
|
-
|
|
463
|
+
# session_root = baresip_obj[:session_root]
|
|
464
|
+
config_root = baresip_obj[:config_root]
|
|
465
|
+
config = "#{config_root}/config"
|
|
446
466
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
call_info_hash[:src_num_rules] = src_num_rules
|
|
450
|
-
call_info_hash[:target_num] = target_num
|
|
467
|
+
puts "#{green}#{call_started} >>>#{end_of_color}"
|
|
468
|
+
puts "#{yellow}dialing #{target_num}#{end_of_color}"
|
|
451
469
|
|
|
452
|
-
|
|
470
|
+
cmd_resp = baresip_exec(
|
|
471
|
+
baresip_obj: baresip_obj,
|
|
472
|
+
cmd: "/dial #{target_num}\r\n"
|
|
473
|
+
)
|
|
474
|
+
puts "/dial #{target_num} RESP:"
|
|
475
|
+
puts cmd_resp.xpath('//pre').text
|
|
453
476
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
477
|
+
cmd_resp = baresip_exec(
|
|
478
|
+
baresip_obj: baresip_obj,
|
|
479
|
+
cmd: "/listcalls\r\n"
|
|
480
|
+
)
|
|
481
|
+
puts '/listcalls RESP:'
|
|
482
|
+
puts cmd_resp.xpath('//pre').text
|
|
483
|
+
|
|
484
|
+
puts red
|
|
485
|
+
# Conditions to hangup when less than seconds_to_record
|
|
486
|
+
terminated = 'terminated (duration:'
|
|
487
|
+
unavail = '503 Service Unavailable'
|
|
488
|
+
not_found = 'session closed: 404 Not Found'
|
|
489
|
+
|
|
490
|
+
reason = 'recording limit reached'
|
|
491
|
+
seconds_recorded = 0
|
|
492
|
+
seconds_to_record.downto(1).each do |countdown|
|
|
493
|
+
seconds_recorded += 1
|
|
494
|
+
print "#{seconds_to_record}s to record - remaining: #{format('%-9.9s', countdown)}"
|
|
495
|
+
print "\r"
|
|
496
|
+
|
|
497
|
+
if dump_session_data.select { |s| s.include?(terminated) }.length.positive?
|
|
498
|
+
reason = 'call terminated by other party'
|
|
499
|
+
break
|
|
500
|
+
end
|
|
457
501
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
baresip_bin: baresip_bin,
|
|
463
|
-
session_root: target_num_root,
|
|
464
|
-
screen_session: "#{File.basename($PROGRAM_NAME)}-#{target_num}"
|
|
465
|
-
)
|
|
502
|
+
if dump_session_data.select { |s| s.include?(unavail) }.length.positive?
|
|
503
|
+
reason = 'SIP 503 (service unavailable)'
|
|
504
|
+
break
|
|
505
|
+
end
|
|
466
506
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
507
|
+
if dump_session_data.select { |s| s.include?(not_found) }.length.positive?
|
|
508
|
+
reason = 'SIP 404 (not found)'
|
|
509
|
+
break
|
|
510
|
+
end
|
|
470
511
|
|
|
471
|
-
|
|
472
|
-
|
|
512
|
+
sleep 1
|
|
513
|
+
end
|
|
514
|
+
call_resp_hash[:seconds_recorded] = seconds_recorded
|
|
515
|
+
puts end_of_color
|
|
516
|
+
|
|
517
|
+
call_stopped = Time.now.strftime('%Y-%m-%d_%H.%M.%S')
|
|
518
|
+
puts "\n#{green}#{call_stopped} >>> #{reason} #{target_num}#{end_of_color}"
|
|
519
|
+
call_resp_hash[:call_stopped] = call_stopped
|
|
520
|
+
call_resp_hash[:reason] = reason
|
|
521
|
+
puts "call termination reason: #{reason}"
|
|
522
|
+
|
|
523
|
+
stop(baresip_obj: baresip_obj)
|
|
524
|
+
FileUtils.rm_rf(config_root_for_target_num)
|
|
525
|
+
|
|
526
|
+
absolute_recording = ''
|
|
527
|
+
relative_recording = ''
|
|
528
|
+
Dir.glob("#{session_root}/dump-*#{target_num}*.wav").each do |path|
|
|
529
|
+
wav = File.basename(path)
|
|
530
|
+
File.delete(path) if wav.match(/^dump-.+#{target_num}.+-enc\.wav$/)
|
|
531
|
+
next unless wav.match(/^dump-.+#{target_num}.+-dec.wav/)
|
|
532
|
+
|
|
533
|
+
FileUtils.mv(path, target_num_root)
|
|
534
|
+
absolute_recording = "#{target_num_root}/#{wav}"
|
|
535
|
+
relative_recording = "#{target_num}-#{call_started}/#{wav}"
|
|
536
|
+
end
|
|
473
537
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
538
|
+
screenlog_file = File.basename(screenlog_path)
|
|
539
|
+
relative_screenlog = "#{target_num}-#{call_started}/#{screenlog_file}"
|
|
540
|
+
|
|
541
|
+
call_resp_hash[:screenlog] = relative_screenlog
|
|
542
|
+
call_resp_hash[:recording] = '--'
|
|
543
|
+
call_resp_hash[:waveform] = '--'
|
|
544
|
+
call_resp_hash[:spectrogram] = '--'
|
|
545
|
+
|
|
546
|
+
unless absolute_recording.empty?
|
|
547
|
+
puts cayan
|
|
548
|
+
|
|
549
|
+
call_resp_hash[:recording] = relative_recording
|
|
550
|
+
|
|
551
|
+
absolute_spectrogram = "#{absolute_recording}-spectrogram.png"
|
|
552
|
+
relative_spectrogram = "#{relative_recording}-spectrogram.png"
|
|
553
|
+
print "Generating Audio Spectrogram for #{absolute_recording}..."
|
|
554
|
+
system(
|
|
555
|
+
sox_bin,
|
|
556
|
+
'--show-progress',
|
|
557
|
+
'--type',
|
|
558
|
+
'sndfile',
|
|
559
|
+
'--encoding',
|
|
560
|
+
'signed-integer',
|
|
561
|
+
'--bits',
|
|
562
|
+
'16',
|
|
563
|
+
'--endian',
|
|
564
|
+
'little',
|
|
565
|
+
'--channels',
|
|
566
|
+
'1',
|
|
567
|
+
'--rate',
|
|
568
|
+
'8000',
|
|
569
|
+
absolute_recording,
|
|
570
|
+
'-n',
|
|
571
|
+
'spectrogram',
|
|
572
|
+
'-o',
|
|
573
|
+
absolute_spectrogram,
|
|
574
|
+
'-d',
|
|
575
|
+
seconds_to_record.to_s
|
|
477
576
|
)
|
|
478
|
-
puts
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
577
|
+
puts 'complete.'
|
|
578
|
+
call_resp_hash[:spectrogram] = relative_spectrogram
|
|
579
|
+
|
|
580
|
+
absolute_waveform = "#{absolute_recording}-waveform.png"
|
|
581
|
+
relative_waveform = "#{relative_recording}-waveform.png"
|
|
582
|
+
print "Generating Audio Waveform for #{absolute_recording}..."
|
|
583
|
+
system(
|
|
584
|
+
waveform_bin,
|
|
585
|
+
'--method',
|
|
586
|
+
'peak',
|
|
587
|
+
'--color',
|
|
588
|
+
'#FF0000',
|
|
589
|
+
'--background',
|
|
590
|
+
'#000000',
|
|
591
|
+
'--force',
|
|
592
|
+
absolute_recording,
|
|
593
|
+
absolute_waveform
|
|
484
594
|
)
|
|
485
|
-
puts '
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
# Conditions to proceed less than seconds_to_record
|
|
490
|
-
terminated = 'terminated (duration:'
|
|
491
|
-
unavail = '503 Service Unavailable'
|
|
492
|
-
not_found = 'session closed: 404 Not Found'
|
|
493
|
-
|
|
494
|
-
reason = 'recording limit reached'
|
|
495
|
-
seconds_recorded = 0
|
|
496
|
-
seconds_to_record.downto(1).each do |countdown|
|
|
497
|
-
seconds_recorded += 1
|
|
498
|
-
print "#{seconds_to_record}s to record - remaining: #{format('%-9.9s', countdown)}"
|
|
499
|
-
print "\r"
|
|
500
|
-
|
|
501
|
-
if dump_session_data.select { |s| s.include?(terminated) }.length.positive?
|
|
502
|
-
reason = 'call terminated by other party'
|
|
503
|
-
break
|
|
504
|
-
end
|
|
595
|
+
puts 'complete.'
|
|
596
|
+
call_resp_hash[:waveform] = relative_waveform
|
|
597
|
+
puts end_of_color
|
|
598
|
+
end
|
|
505
599
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
600
|
+
call_resp_hash
|
|
601
|
+
rescue StandardError => e
|
|
602
|
+
raise e
|
|
603
|
+
end
|
|
510
604
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
end
|
|
605
|
+
# Supported Method Parameters::
|
|
606
|
+
# PWN::Plugins::BareSIP.recon(
|
|
607
|
+
# )
|
|
515
608
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
609
|
+
public_class_method def self.recon(opts = {})
|
|
610
|
+
baresip_bin = opts[:baresip_bin]
|
|
611
|
+
config_root = opts[:config_root] if Dir.exist?(
|
|
612
|
+
opts[:config_root].to_s
|
|
613
|
+
)
|
|
614
|
+
config_root ||= "#{Dir.home}/.baresip"
|
|
615
|
+
session_root = opts[:session_root]
|
|
616
|
+
session_root ||= Dir.pwd
|
|
617
|
+
target_file = opts[:target_file]
|
|
618
|
+
randomize = opts[:randomize]
|
|
619
|
+
src_num_rules = opts[:src_num_rules]
|
|
620
|
+
max_threads = opts[:max_threads].to_i
|
|
621
|
+
max_threads = 3 if max_threads.zero?
|
|
622
|
+
seconds_to_record = opts[:seconds_to_record].to_i
|
|
623
|
+
seconds_to_record = 60 if seconds_to_record.zero?
|
|
624
|
+
sox_bin = opts[:sox_bin] if File.exist?(opts[:sox_bin].to_s)
|
|
625
|
+
sox_bin ||= '/usr/bin/sox'
|
|
520
626
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
recording = ''
|
|
527
|
-
# BUGFIX: Sometimes recordings aren't generated
|
|
528
|
-
# Perhaps because previous session wasn't stopped properly?
|
|
529
|
-
Dir.entries(target_num_root).each do |entry|
|
|
530
|
-
recording = entry if entry.match(/^dump-.+-dec.wav/)
|
|
531
|
-
File.delete(entry) if entry.match(/^dump-.+-enc\.wav$/) && File.exist?(entry)
|
|
532
|
-
end
|
|
627
|
+
target_range = parse_target_file(
|
|
628
|
+
target_file: target_file,
|
|
629
|
+
randomize: randomize
|
|
630
|
+
)
|
|
533
631
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
'--rate',
|
|
559
|
-
'8000',
|
|
560
|
-
recording,
|
|
561
|
-
'-n',
|
|
562
|
-
'spectrogram',
|
|
563
|
-
'-o',
|
|
564
|
-
spectrogram,
|
|
565
|
-
'-d',
|
|
566
|
-
seconds_to_record.to_s
|
|
567
|
-
)
|
|
568
|
-
puts 'complete.'
|
|
569
|
-
call_info_hash[:spectrogram] = "#{target_num}-#{call_started}/#{spectrogram}"
|
|
570
|
-
|
|
571
|
-
waveform = "#{recording}-waveform.png"
|
|
572
|
-
print "Generating Audio Waveform for #{recording}..."
|
|
573
|
-
system(
|
|
574
|
-
waveform_bin,
|
|
575
|
-
'--method',
|
|
576
|
-
'peak',
|
|
577
|
-
'--color',
|
|
578
|
-
'#FF0000',
|
|
579
|
-
'--background',
|
|
580
|
-
'#000000',
|
|
581
|
-
'--force',
|
|
582
|
-
recording,
|
|
583
|
-
waveform
|
|
584
|
-
)
|
|
585
|
-
puts 'complete.'
|
|
586
|
-
call_info_hash[:waveform] = "#{target_num}-#{call_started}/#{waveform}"
|
|
587
|
-
puts end_of_color
|
|
588
|
-
end
|
|
632
|
+
results_hash = {
|
|
633
|
+
session_started: Time.now.strftime('%Y-%m-%d_%H.%M.%S'),
|
|
634
|
+
data: []
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
# Change to session_root _before_ executing threads
|
|
638
|
+
Dir.chdir(session_root)
|
|
639
|
+
|
|
640
|
+
# Multi-thread calls!
|
|
641
|
+
mutex = Mutex.new
|
|
642
|
+
PWN::Plugins::ThreadPool.fill(
|
|
643
|
+
enumerable_array: target_range,
|
|
644
|
+
max_threads: max_threads
|
|
645
|
+
) do |target_num|
|
|
646
|
+
call_resp_hash = dial_target_in_list(
|
|
647
|
+
baresip_bin: baresip_bin,
|
|
648
|
+
target_num: target_num,
|
|
649
|
+
config_root: config_root,
|
|
650
|
+
session_root: session_root,
|
|
651
|
+
randomize: randomize,
|
|
652
|
+
src_num_rules: src_num_rules,
|
|
653
|
+
seconds_to_record: seconds_to_record,
|
|
654
|
+
sox_bin: sox_bin
|
|
655
|
+
)
|
|
589
656
|
|
|
590
657
|
# Push Call Results to results_hash[:data]
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
# end
|
|
594
|
-
results_hash[:data].push(call_info_hash)
|
|
595
|
-
|
|
596
|
-
puts "call termination reason: #{reason}"
|
|
597
|
-
# Stop call to cd into next session root
|
|
598
|
-
stop(baresip_obj: baresip_obj)
|
|
599
|
-
|
|
600
|
-
seconds_to_delay_between_pool_of_calls = Random.rand(1..9)
|
|
601
|
-
seconds_to_delay_between_pool_of_calls.downto(1).each do |countdown|
|
|
602
|
-
print "#{red}ZZZ #{seconds_to_delay_between_pool_of_calls}s until next pool of calls: #{format('%-9.9s', countdown)}#{end_of_color}"
|
|
603
|
-
print "\r"
|
|
604
|
-
sleep 1
|
|
658
|
+
mutex.synchronize do
|
|
659
|
+
results_hash[:data].push(call_resp_hash)
|
|
605
660
|
end
|
|
606
|
-
puts "\n#{green}waking up for next call \\o/#{end_of_color}"
|
|
607
|
-
puts "\n\n\n"
|
|
608
661
|
end
|
|
609
662
|
results_hash[:session_ended] = Time.now.strftime('%Y-%m-%d_%H.%M.%S')
|
|
610
663
|
|
|
611
664
|
results_hash
|
|
612
665
|
rescue StandardError => e
|
|
613
666
|
raise e
|
|
614
|
-
ensure
|
|
615
|
-
stop(baresip_obj: baresip_obj) unless baresip_obj.empty?
|
|
616
|
-
end
|
|
617
|
-
|
|
618
|
-
# Supported Method Parameters::
|
|
619
|
-
# PWN::Plugins::BareSIP.killall
|
|
620
|
-
|
|
621
|
-
public_class_method def self.killall
|
|
622
|
-
system(
|
|
623
|
-
'killall',
|
|
624
|
-
'-15',
|
|
625
|
-
'baresip'
|
|
626
|
-
)
|
|
627
|
-
rescue StandardError => e
|
|
628
|
-
raise e
|
|
629
667
|
end
|
|
630
668
|
|
|
631
669
|
# Author(s):: 0day Inc. <request.pentest@0dayinc.com>
|
|
@@ -641,8 +679,11 @@ module PWN
|
|
|
641
679
|
public_class_method def self.help
|
|
642
680
|
puts "USAGE:
|
|
643
681
|
baresip_obj = #{self}.start(
|
|
644
|
-
|
|
682
|
+
src_num: 'Optional source phone number displayed',
|
|
683
|
+
baresip_bin: 'Optional path of baresip binary (Defaults to /usr/bin/baresip)',
|
|
645
684
|
config_root: 'Optional dir of baresip config (Defaults to ~/.baresip)',
|
|
685
|
+
session_root: 'Optional dir of baresip session (Defaults to Dir.pwd)',
|
|
686
|
+
screenlog_path: 'Optional path of screenlog file (Defaults to screenlog.txt)',
|
|
646
687
|
screen_session: 'Optional name of screen session (Defaults baresip)'
|
|
647
688
|
)
|
|
648
689
|
|
|
@@ -657,8 +698,6 @@ module PWN
|
|
|
657
698
|
screen_session: 'Required - screen session to stop'
|
|
658
699
|
)
|
|
659
700
|
|
|
660
|
-
#{self}.killall
|
|
661
|
-
|
|
662
701
|
#{self}.authors
|
|
663
702
|
"
|
|
664
703
|
end
|
data/lib/pwn/plugins/sock.rb
CHANGED
|
@@ -48,20 +48,20 @@ module PWN
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
# Supported Method Parameters::
|
|
51
|
-
# PWN::Plugins::Sock.
|
|
51
|
+
# PWN::Plugins::Sock.check_port_in_use(
|
|
52
52
|
# port: 'required - target port',
|
|
53
53
|
# server_ip: 'optional - target host or ip to check (Defaults to 127.0.0.1)',
|
|
54
54
|
# protocol: 'optional - :tcp || :udp (defaults to tcp)'
|
|
55
55
|
# )
|
|
56
56
|
|
|
57
|
-
public_class_method def self.
|
|
57
|
+
public_class_method def self.check_port_in_use(opts = {})
|
|
58
58
|
server_ip = opts[:server_ip]
|
|
59
59
|
server_ip ||= '127.0.0.1'
|
|
60
60
|
port = opts[:port]
|
|
61
61
|
protocol = opts[:protocol]
|
|
62
62
|
protocol ||= :tcp
|
|
63
63
|
|
|
64
|
-
ct =
|
|
64
|
+
ct = 1
|
|
65
65
|
s = Socket.tcp(server_ip, port, connect_timeout: ct) if protocol == :tcp
|
|
66
66
|
s = Socket.udp(server_ip, port, connect_timeout: ct) if protocol == :udp
|
|
67
67
|
s.close
|
|
@@ -14,8 +14,11 @@ module PWN
|
|
|
14
14
|
#
|
|
15
15
|
# Example:
|
|
16
16
|
# arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
17
|
+
# mutex = Mutex.new
|
|
17
18
|
# PWN::Plugins::ThreadPool.fill(enumerable_array: arr, max_threads: 9) do |integer|
|
|
18
|
-
#
|
|
19
|
+
# mutex.synchronize do
|
|
20
|
+
# puts integer
|
|
21
|
+
# end
|
|
19
22
|
# end
|
|
20
23
|
|
|
21
24
|
public_class_method def self.fill(opts = {})
|
data/lib/pwn/reports/phone.rb
CHANGED
|
@@ -104,9 +104,10 @@ module PWN
|
|
|
104
104
|
<a class="toggle-vis" data-column="5" href="#">Seconds Recorded</a> |
|
|
105
105
|
<a class="toggle-vis" data-column="6" href="#">Call Stopped</a>
|
|
106
106
|
<a class="toggle-vis" data-column="7" href="#">Reason</a>
|
|
107
|
-
<a class="toggle-vis" data-column="8" href="#">
|
|
108
|
-
<a class="toggle-vis" data-column="9" href="#">
|
|
109
|
-
<a class="toggle-vis" data-column="10" href="#">
|
|
107
|
+
<a class="toggle-vis" data-column="8" href="#">Screenlog</a> |
|
|
108
|
+
<a class="toggle-vis" data-column="9" href="#">Recording</a> |
|
|
109
|
+
<a class="toggle-vis" data-column="10" href="#">Spectrogram</a> |
|
|
110
|
+
<a class="toggle-vis" data-column="11" href="#">Waveform</a>
|
|
110
111
|
</div>
|
|
111
112
|
<br /><br />
|
|
112
113
|
|
|
@@ -122,6 +123,7 @@ module PWN
|
|
|
122
123
|
<th>Seconds Recorded</th>
|
|
123
124
|
<th>Call Stopped</th>
|
|
124
125
|
<th>Reason Stopped</th>
|
|
126
|
+
<th>Screenlog</th>
|
|
125
127
|
<th>Recording</th>
|
|
126
128
|
<th>Spectrogram</th>
|
|
127
129
|
<th>Waveform</th>
|
|
@@ -198,6 +200,13 @@ module PWN
|
|
|
198
200
|
"data": "reason",
|
|
199
201
|
"render": $.fn.dataTable.render.text()
|
|
200
202
|
},
|
|
203
|
+
{
|
|
204
|
+
"data": "screenlog",
|
|
205
|
+
"render": function (data, type, row, meta) {
|
|
206
|
+
var screenlog = htmlEntityEncode(data);
|
|
207
|
+
return '<a href="' + screenlog +'" target="_blank">' + screenlog + '</a>';
|
|
208
|
+
}
|
|
209
|
+
},
|
|
201
210
|
{
|
|
202
211
|
"data": "recording",
|
|
203
212
|
"render": function (data, type, row, meta) {
|
data/lib/pwn/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pwn
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.4.
|
|
4
|
+
version: 0.4.545
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- 0day Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2022-09-
|
|
11
|
+
date: 2022-09-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -646,14 +646,14 @@ dependencies:
|
|
|
646
646
|
requirements:
|
|
647
647
|
- - '='
|
|
648
648
|
- !ruby/object:Gem::Version
|
|
649
|
-
version: 4.
|
|
649
|
+
version: 4.3.0
|
|
650
650
|
type: :runtime
|
|
651
651
|
prerelease: false
|
|
652
652
|
version_requirements: !ruby/object:Gem::Requirement
|
|
653
653
|
requirements:
|
|
654
654
|
- - '='
|
|
655
655
|
- !ruby/object:Gem::Version
|
|
656
|
-
version: 4.
|
|
656
|
+
version: 4.3.0
|
|
657
657
|
- !ruby/object:Gem::Dependency
|
|
658
658
|
name: rspec
|
|
659
659
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -716,14 +716,14 @@ dependencies:
|
|
|
716
716
|
requirements:
|
|
717
717
|
- - '='
|
|
718
718
|
- !ruby/object:Gem::Version
|
|
719
|
-
version: 2.13.
|
|
719
|
+
version: 2.13.2
|
|
720
720
|
type: :runtime
|
|
721
721
|
prerelease: false
|
|
722
722
|
version_requirements: !ruby/object:Gem::Requirement
|
|
723
723
|
requirements:
|
|
724
724
|
- - '='
|
|
725
725
|
- !ruby/object:Gem::Version
|
|
726
|
-
version: 2.13.
|
|
726
|
+
version: 2.13.2
|
|
727
727
|
- !ruby/object:Gem::Dependency
|
|
728
728
|
name: ruby-audio
|
|
729
729
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -828,14 +828,14 @@ dependencies:
|
|
|
828
828
|
requirements:
|
|
829
829
|
- - '='
|
|
830
830
|
- !ruby/object:Gem::Version
|
|
831
|
-
version:
|
|
831
|
+
version: 3.0.1
|
|
832
832
|
type: :runtime
|
|
833
833
|
prerelease: false
|
|
834
834
|
version_requirements: !ruby/object:Gem::Requirement
|
|
835
835
|
requirements:
|
|
836
836
|
- - '='
|
|
837
837
|
- !ruby/object:Gem::Version
|
|
838
|
-
version:
|
|
838
|
+
version: 3.0.1
|
|
839
839
|
- !ruby/object:Gem::Dependency
|
|
840
840
|
name: slack-ruby-client
|
|
841
841
|
requirement: !ruby/object:Gem::Requirement
|