pwn 0.4.666 → 0.4.668

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