pwn 0.4.666 → 0.4.668

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 668626b1a3332cf0b09f04a683d7b79462724328afd3535a3d47652b120aca93
4
- data.tar.gz: 921a004b8a3bcf11031589da46b45b3e347f3cd6a7fe33854adaf3f61f2cf9ef
3
+ metadata.gz: 81bb385ebabab222a3d4da59c7eda736aaac086600e3c9710d6680428e05c023
4
+ data.tar.gz: 3227d1e76969e9b89ed2b5ce7090f8f2407288f89aa157b2af33dd4138855e38
5
5
  SHA512:
6
- metadata.gz: 9e1b117de7f4eceaf24145de0f8923394fb225356a7b79bd6024548476ba3770aa829c9cb65830a5eda93f280b79acbb6ecaf75178262ec9b6c2790ea34f37c0
7
- data.tar.gz: 03de28428d438c9394ca0254ca7f5093e5c8e6b23d4f217edb8fdc88edaa33dc0e7be5d643c2282e6d13f2a95e834e1809cb10d6530ae68658ed0b0cf285b692
6
+ metadata.gz: 262b94064b575f5200f500c4ea41e42bde4050d82f95ce8675ea3166c2ee4370126bcdb7222b3e261496e283dd04d85b80b040d939587817a8606f6e3be15559
7
+ data.tar.gz: c2cfd0bc37664e45aca472b1f4682962ad675262fc39363e6830caf05ffaf2e34b27795efdf7b7395cd383e6edd5bcb9a19518162aa7a5de87337335dce83043
data/Gemfile CHANGED
@@ -72,7 +72,7 @@ gem 'ruby-nmap', '1.0.1'
72
72
  gem 'ruby-saml', '1.15.0'
73
73
  gem 'rvm', '1.11.3.9'
74
74
  gem 'savon', '2.14.0'
75
- gem 'selenium-devtools', '0.112.0'
75
+ gem 'selenium-devtools', '0.113.0'
76
76
  gem 'serialport', '1.3.2'
77
77
  gem 'sinatra', '3.0.6'
78
78
  gem 'slack-ruby-client', '2.1.0'
data/README.md CHANGED
@@ -37,7 +37,7 @@ $ rvm use ruby-3.2.2@pwn
37
37
  $ rvm list gemsets
38
38
  $ gem install --verbose pwn
39
39
  $ pwn
40
- pwn[v0.4.666]:001 >>> PWN.help
40
+ pwn[v0.4.668]:001 >>> PWN.help
41
41
  ```
42
42
 
43
43
  [![Installing the pwn Security Automation Framework](https://raw.githubusercontent.com/0dayInc/pwn/master/documentation/pwn_install.png)](https://youtu.be/G7iLUY4FzsI)
@@ -52,7 +52,7 @@ $ rvm use ruby-3.2.2@pwn
52
52
  $ gem uninstall --all --executables pwn
53
53
  $ gem install --verbose pwn
54
54
  $ pwn
55
- pwn[v0.4.666]:001 >>> PWN.help
55
+ pwn[v0.4.668]:001 >>> PWN.help
56
56
  ```
57
57
 
58
58
 
data/bin/pwn CHANGED
@@ -297,6 +297,5 @@ begin
297
297
  prompt: prompt
298
298
  )
299
299
  rescue StandardError => e
300
- puts 1111 if Pry.config.chat_gpt
301
300
  raise e
302
301
  end
@@ -43,15 +43,33 @@ if opts.empty?
43
43
  end
44
44
 
45
45
  def csv_diff(opts = {})
46
- larger_csv = opts[:larger_csv]
47
- smaller_csv = opts[:smaller_csv]
46
+ c1_path = opts[:c1_path]
47
+ c2_path = opts[:c2_path]
48
48
  diff_path = opts[:diff_path]
49
- include_csv_headers = opts[:include_csv_headers]
50
- column_names_to_exclude = opts[:column_names_to_exclude]
49
+ no_headers = opts[:no_headers]
50
+ column_names_to_exclude = opts[:column_names_to_exclude].to_s.split(',')
51
51
 
52
- # TODO: Exclude the column values for diff consideration
53
- # while including the columns in the diff...figure out what
54
- # to do with the columns' values.
52
+ csv1 = CSV.read(c1_path)
53
+ csv2 = CSV.read(c2_path)
54
+
55
+ if csv1.length >= csv2.length
56
+ larger_csv = csv1
57
+ larger_csv_orig = CSV.read(c1_path)
58
+
59
+ smaller_csv = csv2
60
+ smaller_csv_orig = CSV.read(c2_path)
61
+ end
62
+
63
+ if csv2.length >= csv1.length
64
+ larger_csv = csv2
65
+ larger_csv_orig = CSV.read(c2_path)
66
+
67
+ smaller_csv = csv1
68
+ smaller_csv_orig = CSV.read(c1_path)
69
+ end
70
+
71
+ # Exclude the column values for diff to ensure the same rows
72
+ # with for example different timestamps aren't included.
55
73
  columns_index_arr = []
56
74
  column_names_to_exclude&.each do |column_name|
57
75
  column_index = smaller_csv.first.find_index(column_name)
@@ -72,44 +90,46 @@ def csv_diff(opts = {})
72
90
  end
73
91
  end
74
92
 
75
- csv_headers = larger_csv.first.join(',')
93
+ # Write diff with redacted columns (to find differences we care about)
76
94
  File.open(diff_path, 'w') do |f|
77
- f.puts csv_headers if include_csv_headers
78
95
  larger_csv.each do |line_arr|
79
96
  line = line_arr.join(',')
80
97
  f.puts line unless smaller_csv.include?(line_arr)
81
98
  end
82
99
  end
100
+ diff_csv = CSV.read(diff_path)
101
+
102
+ # Write diff again with all columns.
103
+ csv_headers_orig = larger_csv_orig.first.join(',')
104
+ File.open(diff_path, 'w') do |f|
105
+ if no_headers
106
+ larger_csv_orig.each do |line_arr|
107
+ line = line_arr.join(',')
108
+ f.puts line if diff_csv.include?(line_arr)
109
+ end
110
+ else
111
+ f.puts csv_headers_orig
112
+ larger_csv_orig(1..-1).each do |line_arr|
113
+ line = line_arr.join(',')
114
+ f.puts line if diff_csv.include?(line_arr)
115
+ end
116
+ end
117
+ end
83
118
  end
84
119
 
85
120
  c1_path = opts[:c1_path]
86
- csv1 = CSV.read(c1_path)
87
-
88
121
  c2_path = opts[:c2_path]
89
- csv2 = CSV.read(c2_path)
90
-
91
122
  diff_path = opts[:diff_path]
123
+ column_names_to_exclude = opts[:column_names_to_exclude]
92
124
 
93
- column_names_to_exclude = opts[:column_names_to_exclude].to_s.split(',')
94
-
95
- include_csv_headers = false if opts[:no_headers]
96
- include_csv_headers ||= true
125
+ no_headers = true if opts[:no_headers]
126
+ no_headers ||= false
97
127
 
98
128
  # Compare which two is larger
99
- if csv1.length >= csv2.length
100
- csv_diff(
101
- larger_csv: csv1,
102
- smaller_csv: csv2,
103
- diff_path: diff_path,
104
- include_csv_headers: include_csv_headers,
105
- column_names_to_exclude: column_names_to_exclude
106
- )
107
- else
108
- csv_diff(
109
- larger_csv: csv2,
110
- smaller_csv: csv1,
111
- diff_path: diff_path,
112
- include_csv_headers: include_csv_headers,
113
- column_names_to_exclude: column_names_to_exclude
114
- )
115
- end
129
+ csv_diff(
130
+ c1_path: c1_path,
131
+ c2_path: c2_path,
132
+ diff_path: diff_path,
133
+ no_headers: no_headers,
134
+ column_names_to_exclude: column_names_to_exclude
135
+ )
@@ -20,13 +20,9 @@ OptionParser.new do |options|
20
20
  opts[:output_results] = o
21
21
  end
22
22
 
23
- options.on('-pPROXY', '--proxy=PROXY', '<Optional - HTTP or Socks Proxy>') do |p|
23
+ options.on('-pPROXY', '--proxy=PROXY', '<Optional - HTTP or Socks Proxy || :tor>') do |p|
24
24
  opts[:proxy] = p
25
25
  end
26
-
27
- options.on('-T', '--[no-]with-tor', '<Optional - Proxy w/ TOR (Defaults to false)>') do |w|
28
- opts[:with_tor] = w
29
- end
30
26
  end.parse!
31
27
 
32
28
  if opts.empty?
@@ -37,13 +33,13 @@ end
37
33
  registrant_filter = opts[:registrant_filter].to_s.strip.chomp.scrub
38
34
  output_results = opts[:output_results].to_s.strip.chomp.scrub
39
35
  proxy = opts[:proxy].to_s.scrub.strip.chomp unless opts[:proxy].nil?
40
- with_tor = opts[:with_tor]
41
36
 
42
37
  begin
43
- if proxy != '' && with_tor
44
- browser_obj = PWN::Plugins::TransparentBrowser.open(browser_type: :headless, proxy: proxy, with_tor: true)
45
- elsif proxy != '' && with_tor.nil?
46
- browser_obj = PWN::Plugins::TransparentBrowser.open(browser_type: :headless, proxy: proxy)
38
+ if proxy
39
+ browser_obj = PWN::Plugins::TransparentBrowser.open(
40
+ browser_type: :headless,
41
+ proxy: proxy
42
+ )
47
43
  else
48
44
  browser_obj = PWN::Plugins::TransparentBrowser.open(browser_type: :headless)
49
45
  end
@@ -77,12 +77,10 @@ begin
77
77
  )
78
78
 
79
79
  scan_status = scan_status_resp[:status]
80
- # break if scan_status != 'initializing' && scan_status != 'pending' && scan_status != 'running' && scan_status != 'publishing'
80
+
81
81
  break if scan_status == 'completed'
82
82
  end
83
83
 
84
- # raise "Scan status reached an unexpected condition: #{scan_status}. Re-verify the scan config for, '#{scan_name}' and try again." unless scan_status == 'completed'
85
-
86
84
  puts 'scan complete.'
87
85
 
88
86
  print "Exporting results to #{path_to_export}..."
@@ -14,13 +14,9 @@ OptionParser.new do |options|
14
14
  opts[:regex] = r
15
15
  end
16
16
 
17
- options.on('-pPROXY', '--proxy=PROXY', '<Optional - HTTP or Socks Proxy>') do |p|
17
+ options.on('-pPROXY', '--proxy=PROXY', '<Optional - HTTP or Socks Proxy || :tor>') do |p|
18
18
  opts[:proxy] = p
19
19
  end
20
-
21
- options.on('-T', '--[no-]with-tor', '<Optional - Proxy w/ TOR (Defaults to false)>') do |w|
22
- opts[:with_tor] = w
23
- end
24
20
  end.parse!
25
21
 
26
22
  if opts.empty?
@@ -29,13 +25,11 @@ if opts.empty?
29
25
  end
30
26
 
31
27
  proxy = opts[:proxy]
32
- with_tor = opts[:with_tor]
33
28
  regex = opts[:regex]
34
29
 
35
30
  browser_obj = PWN::WWW::Pastebin.open(
36
31
  browser_type: :headless,
37
- proxy: proxy,
38
- with_tor: with_tor
32
+ proxy: proxy
39
33
  )
40
34
 
41
35
  begin
@@ -38,13 +38,9 @@ OptionParser.new do |options|
38
38
  opts[:mfa] = f
39
39
  end
40
40
 
41
- options.on('-pPROXY', '--proxy=PROXY', '<Optional - HTTP or Socks Proxy>') do |p|
41
+ options.on('-pPROXY', '--proxy=PROXY', '<Optional - HTTP or Socks Proxy || :tor>') do |p|
42
42
  opts[:proxy] = p
43
43
  end
44
-
45
- options.on('-T', '--[no-]with-tor', '<Optional - Proxy w/ TOR (Defaults to false)>') do |w|
46
- opts[:with_tor] = w
47
- end
48
44
  end.parse!
49
45
 
50
46
  if opts.empty?
@@ -88,7 +84,6 @@ else
88
84
  end
89
85
 
90
86
  proxy = opts[:proxy].to_s.scrub.strip.chomp unless opts[:proxy].nil?
91
- with_tor = opts[:with_tor]
92
87
 
93
88
  begin
94
89
  def get_web_cache_deception(opts = {})
@@ -157,18 +152,10 @@ begin
157
152
 
158
153
  puts "#{@green}Leveraging PWN::WWW::#{pwn_www_mod_str} to Obtain a Post AuhN State...#{@end_of_color}"
159
154
  if proxy
160
- if with_tor
161
- browser_obj = pwn_www_mod.open(
162
- browser_type: :chrome,
163
- proxy: proxy,
164
- with_tor: true
165
- )
166
- else
167
- browser_obj = pwn_www_mod.open(
168
- browser_type: :chrome,
169
- proxy: proxy
170
- )
171
- end
155
+ browser_obj = pwn_www_mod.open(
156
+ browser_type: :chrome,
157
+ proxy: proxy
158
+ )
172
159
  else
173
160
  browser_obj = pwn_www_mod.open(browser_type: :chrome)
174
161
  end
@@ -181,18 +168,10 @@ begin
181
168
  )
182
169
  puts "#{@green}complete.#{@end_of_color}\n\n\n"
183
170
  elsif pwn_www_mod_str == '' && proxy
184
- if with_tor
185
- browser_obj = PWN::Plugins::TransparentBrowser.open(
186
- browser_type: :chrome,
187
- proxy: proxy,
188
- with_tor: true
189
- )
190
- else
191
- browser_obj = PWN::Plugins::TransparentBrowser.open(
192
- browser_type: :chrome,
193
- proxy: proxy
194
- )
195
- end
171
+ browser_obj = PWN::Plugins::TransparentBrowser.open(
172
+ browser_type: :chrome,
173
+ proxy: proxy
174
+ )
196
175
  else
197
176
  browser_obj = PWN::Plugins::TransparentBrowser.open(browser_type: :chrome)
198
177
  end
data/bin/pwn_www_checkip CHANGED
@@ -10,28 +10,24 @@ OptionParser.new do |options|
10
10
  #{$PROGRAM_NAME} [opts]
11
11
  "
12
12
 
13
- options.on('-pPROXY', '--proxy=PROXY', '<Optional - HTTP or Socks Proxy>') do |p|
13
+ options.on('-pPROXY', '--proxy=PROXY', '<Optional - HTTP or Socks Proxy || :tor>') do |p|
14
14
  opts[:proxy] = p
15
15
  end
16
16
 
17
- options.on('-T', '--[no-]with-tor', '<Optional - Proxy w/ TOR (Defaults to false)>') do |w|
18
- opts[:with_tor] = w
19
- end
20
-
21
17
  options.on('-i', '--[no-]ipinfo', '<Optional - Return Details about Public IP Returned from CheckIP>') do |i|
22
18
  opts[:ipinfo] = i
23
19
  end
24
20
  end.parse!
25
21
 
26
22
  proxy = opts[:proxy].to_s.scrub.strip.chomp unless opts[:proxy].nil?
27
- with_tor = opts[:with_tor]
28
23
  ipinfo = opts[:ipinfo]
29
24
 
30
25
  begin
31
- if proxy != '' && with_tor
32
- browser_obj = PWN::Plugins::TransparentBrowser.open(browser_type: :rest, proxy: proxy, with_tor: true)::Request
33
- elsif proxy != '' && with_tor.nil?
34
- browser_obj = PWN::Plugins::TransparentBrowser.open(browser_type: :rest, proxy: proxy)::Request
26
+ if proxy
27
+ browser_obj = PWN::Plugins::TransparentBrowser.open(
28
+ browser_type: :rest,
29
+ proxy: proxy
30
+ )::Request
35
31
  else
36
32
  browser_obj = PWN::Plugins::TransparentBrowser.open(browser_type: :rest)::Request
37
33
  end
@@ -26,14 +26,10 @@ OptionParser.new do |options|
26
26
  opts[:browser_type] = b
27
27
  end
28
28
 
29
- options.on('-pPROXY', '--proxy=PROXY', '<Optional - HTTP or Socks Proxy>') do |p|
29
+ options.on('-pPROXY', '--proxy=PROXY', '<Optional - HTTP or Socks Proxy || :tor>') do |p|
30
30
  opts[:proxy] = p
31
31
  end
32
32
 
33
- options.on('-T', '--[no-]with-tor', '<Optional - Proxy w/ TOR (Defaults to false)>') do |w|
34
- opts[:with_tor] = w
35
- end
36
-
37
33
  options.on('-S', '--[no-]spider-fqdn', '<Optional - Spider Target FQDN>') do |s|
38
34
  opts[:spider] = s
39
35
  end
@@ -56,7 +52,6 @@ else
56
52
  end
57
53
 
58
54
  proxy = opts[:proxy].to_s.scrub.strip.chomp unless opts[:proxy].nil?
59
- with_tor = opts[:with_tor]
60
55
 
61
56
  if opts[:spider]
62
57
  spider = true
@@ -109,18 +104,10 @@ begin
109
104
  end
110
105
 
111
106
  if proxy
112
- if with_tor
113
- browser_obj = PWN::Plugins::TransparentBrowser.open(
114
- browser_type: browser_type,
115
- proxy: proxy,
116
- with_tor: true
117
- )
118
- else
119
- browser_obj = PWN::Plugins::TransparentBrowser.open(
120
- browser_type: browser_type,
121
- proxy: proxy
122
- )
123
- end
107
+ browser_obj = PWN::Plugins::TransparentBrowser.open(
108
+ browser_type: browser_type,
109
+ proxy: proxy
110
+ )
124
111
  else
125
112
  browser_obj = PWN::Plugins::TransparentBrowser.open(browser_type: browser_type)
126
113
  end
@@ -80,9 +80,10 @@ module PWN
80
80
 
81
81
  response
82
82
  rescue RestClient::ExceptionWithResponse => e
83
- puts "#{base_nessus_cloud_api_uri}/#{rest_call}"
84
- puts params
85
- puts http_body
83
+ puts "URI: #{base_nessus_cloud_api_uri}/#{rest_call}"
84
+ puts "Params: #{params.inspect}"
85
+ puts "HTTP POST Body: #{http_body}"
86
+
86
87
  raise e
87
88
  rescue StandardError, SystemExit, Interrupt => e
88
89
  raise e
@@ -71,7 +71,7 @@ module PWN
71
71
  end
72
72
 
73
73
  # Supported Method Parameters::
74
- # PWN::Plugins::OwaspZap.start(
74
+ # zap_obj = PWN::Plugins::OwaspZap.start(
75
75
  # api_key: 'required - api key for API authorization',
76
76
  # zap_bin_path: 'optional - path to zap.sh file'
77
77
  # headless: 'optional - run zap headless if set to true',
@@ -467,17 +467,12 @@ module PWN
467
467
 
468
468
  # Supported Method Parameters::
469
469
  # PWN::Plugins::OwaspZap.stop(
470
- # :zap_obj => 'required - zap_obj returned from #open method'
470
+ # :zap_obj => 'required - zap_obj returned from #start method'
471
471
  # )
472
472
 
473
473
  public_class_method def self.stop(opts = {})
474
474
  zap_obj = opts[:zap_obj]
475
- unless zap_obj.nil?
476
- pid = zap_obj[:pid]
477
- # File.unlink(zap_obj[:stdout_log]) if File.exist?(zap_obj[:stdout_log])
478
-
479
- Process.kill('TERM', pid)
480
- end
475
+ Process.kill('TERM', zab_obj[:pid]) unless zap_obj.nil?
481
476
  rescue StandardError => e
482
477
  raise e
483
478
  end
@@ -538,7 +533,7 @@ module PWN
538
533
  )
539
534
 
540
535
  #{self}.stop(
541
- zap_obj: 'required - zap_obj returned from #open method'
536
+ zap_obj: 'required - zap_obj returned from #start method'
542
537
  )
543
538
 
544
539
  #{self}.authors
@@ -0,0 +1,221 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pty'
4
+
5
+ module PWN
6
+ module Plugins
7
+ # This plugin processes images into readable text
8
+ module Tor
9
+ # Supported Method Parameters::
10
+ # tor_ctrl_cmd(
11
+ # tor_obj: 'required - tor_obj returned from #start method'
12
+ # cmd: 'required - Tor control command to execute',
13
+ # response_timeout: 'optional - float in seconds to timeout (default: 3.0)'
14
+ # )
15
+
16
+ private_class_method def self.tor_control_cmd(opts = {})
17
+ tor_obj = opts[:tor_obj]
18
+ cmd = opts[:cmd]
19
+ response_timeout = opts[:response_timeout]
20
+ response_timeout ||= 3.0
21
+
22
+ ctrl_ip = tor_obj[:ip]
23
+ ctrl_port = tor_obj[:ctrl_port]
24
+ cookie_authn = tor_obj[:cookie_authn]
25
+
26
+ sock_obj = PWN::Plugins::Sock.connect(
27
+ target: ctrl_ip,
28
+ port: ctrl_port
29
+ )
30
+
31
+ cmd_hist_arr = []
32
+ cmd_hash = { cmd: "AUTHENTICATE #{cookie_authn}\r\n" }
33
+ sock_obj.write(cmd_hash[:cmd])
34
+ does_respond = sock_obj.wait_readable(response_timeout)
35
+ if does_respond
36
+ response = sock_obj.readline.chomp
37
+ cmd_hash[:resp] = response
38
+ cmd_hist_arr.push(cmd_hash)
39
+ if response == '250 OK'
40
+ cmd_hash = { cmd: "#{cmd}\r\n" }
41
+ sock_obj.write(cmd_hash[:cmd])
42
+ does_respond = sock_obj.wait_readable(response_timeout)
43
+ if does_respond
44
+ response = sock_obj.readline.chomp
45
+ cmd_hash[:resp] = response
46
+ cmd_hist_arr.push(cmd_hash)
47
+ if response == '250 OK'
48
+ cmd_hash = { cmd: "QUIT\r\n" }
49
+ sock_obj.write(cmd_hash[:cmd])
50
+ does_respond = sock_obj.wait_readable(response_timeout)
51
+ if does_respond
52
+ response = sock_obj.readline.chomp
53
+ else
54
+ response = '900 NO CMD RESPONSE'
55
+ end
56
+ end
57
+ else
58
+ response = '900 NO CMD RESPONSE'
59
+ end
60
+ cmd_hash[:resp] = response
61
+ cmd_hist_arr.push(cmd_hash)
62
+ end
63
+ else
64
+ response = '700 NO AUTHENTICATE RESPONSE'
65
+ cmd_hash[:resp] = response
66
+ cmd_hist_arr.push(cmd_hash)
67
+ end
68
+ sock_obj = PWN::Plugins::Sock.disconnect(sock_obj: sock_obj)
69
+
70
+ cmd_hist_arr
71
+ rescue StandardError => e
72
+ stop(tor_obj: tor_obj)
73
+ end
74
+
75
+ # Supported Method Parameters::
76
+ # tor_obj = PWN::Plugins::Tor.start(
77
+ # ip: 'optional - IP address to listen (default: 127.0.0.1)',
78
+ # port: 'optional - socks port to listen (default: 1024-65535)',
79
+ # ctrl_port: 'optional - tor control port to listen (default: 1024-65535)',
80
+ # data_dir: 'optional - directory to keep tor session data (default: /tmp/tor_pwn-TIMESTAMP)'
81
+ # )
82
+
83
+ public_class_method def self.start(opts = {})
84
+ ip = opts[:ip]
85
+ ip ||= '127.0.0.1'
86
+ port = opts[:port].to_i
87
+ port = PWN::Plugins::Sock.get_random_unused_port if port.zero?
88
+ ctrl_port = opts[:ctrl_port].to_i
89
+ if ctrl_port.zero?
90
+ loop do
91
+ ctrl_port = PWN::Plugins::Sock.get_random_unused_port
92
+ break if ctrl_port != port
93
+ end
94
+ end
95
+ timestamp = Time.now.strftime('%Y-%m-%d_%H-%M-%S.%N%z')
96
+ data_dir = opts[:data_dir]
97
+ data_dir ||= "/tmp/tor_pwn-#{timestamp}"
98
+ FileUtils.mkdir_p(data_dir)
99
+
100
+ socks_proxy = "#{ip}:#{port}"
101
+ pid_file = "#{data_dir}/tor.pid"
102
+ cookie_authn_file = "#{data_dir}/control_auth_cookie"
103
+ session_log_path = "#{data_dir}/stdout-session.log"
104
+ session_log = File.new(session_log_path, 'w')
105
+ session_log.sync = true
106
+ session_log.fsync
107
+
108
+ fork_pid = Process.fork do
109
+ pty = PTY.spawn(
110
+ 'tor',
111
+ 'DataDirectory',
112
+ data_dir,
113
+ 'SocksPort',
114
+ socks_proxy,
115
+ 'ControlPort',
116
+ ctrl_port.to_s,
117
+ 'CookieAuthentication',
118
+ '1'
119
+ ) do |stdout, _stdin, pid|
120
+ File.write(pid_file, pid)
121
+ stdout.each do |line|
122
+ session_log.puts line
123
+ end
124
+ end
125
+ rescue StandardError => e
126
+ puts 'Tor exiting with errors...'
127
+ FileUtils.rm_rf(data_dir)
128
+ raise e
129
+ end
130
+ Process.detach(fork_pid)
131
+
132
+ loop do
133
+ pid_ready = File.exist?(pid_file)
134
+ cookie_authn_ready = File.exist?(cookie_authn_file)
135
+ sleep 0.1
136
+ break if pid_ready && cookie_authn_ready
137
+ end
138
+
139
+ cookie_authn = `hexdump -e '32/1 "%02x"' #{cookie_authn_file}`
140
+ tor_obj = {
141
+ parent_pid: fork_pid,
142
+ child_pid: File.read(pid_file).to_i,
143
+ ip: ip,
144
+ port: port,
145
+ ctrl_port: ctrl_port,
146
+ data_dir: data_dir,
147
+ cookie_authn: cookie_authn
148
+ }
149
+ rescue StandardError, SystemExit => e
150
+ stop(tor_obj) unless tor_obj.nil?
151
+ raise e
152
+ end
153
+
154
+ # Supported Method Parameters::
155
+ # PWN::Plugins::Tor.switch_exit_node(
156
+ # tor_obj: 'required - tor_obj returned from #start method',
157
+ # response_timeout: 'optional - float in seconds to timeout (default: 3.0)'
158
+ # )
159
+
160
+ public_class_method def self.switch_exit_node(opts = {})
161
+ tor_obj = opts[:tor_obj]
162
+ response_timeout = opts[:response_timeout]
163
+ tor_control_cmd(
164
+ tor_obj: tor_obj,
165
+ cmd: 'SIGNAL NEWNYM',
166
+ response_timeout: response_timeout
167
+ )
168
+ rescue StandardError => e
169
+ raise e
170
+ end
171
+
172
+ # Supported Method Parameters::
173
+ # PWN::Plugins::Tor.stop(
174
+ # tor_obj: 'required - tor_obj returned from #start method'
175
+ # )
176
+
177
+ public_class_method def self.stop(opts = {})
178
+ tor_obj = opts[:tor_obj]
179
+ unless tor_obj.nil?
180
+ FileUtils.rm_rf(tor_obj[:data_dir])
181
+ Process.kill('TERM', tor_obj[:child_pid])
182
+ Process.kill('TERM', tor_obj[:parent_pid])
183
+ end
184
+ rescue StandardError => e
185
+ raise e
186
+ end
187
+
188
+ # Author(s):: 0day Inc. <request.pentest@0dayinc.com>
189
+
190
+ public_class_method def self.authors
191
+ "AUTHOR(S):
192
+ 0day Inc. <request.pentest@0dayinc.com>
193
+ "
194
+ end
195
+
196
+ # Display Usage for this Module
197
+
198
+ public_class_method def self.help
199
+ puts "USAGE:
200
+ tor_obj = #{self}.start(
201
+ ip: 'optional - IP address to listen (default: 127.0.0.1)',
202
+ port: 'optional - socks port to listen (default: 9050)',
203
+ ctrl_port: 'optional - tor control port to listen (default: 9051)',
204
+ data_dir: 'optional - directory to keep tor session data (default: /tmp/tor_pwn-TIMESTAMP)'
205
+ )
206
+
207
+ #{self}.switch_exit_node(
208
+ tor_obj: 'required - tor_obj returned from #start method',
209
+ response_timeout: 'optional - float in seconds to timeout (default: 3.0)'
210
+ )
211
+
212
+ #{self}.stop(
213
+ tor_obj: 'required - tor_obj returned from #start method'
214
+ )
215
+
216
+ #{self}.authors
217
+ "
218
+ end
219
+ end
220
+ end
221
+ end