pwn 0.5.154 → 0.5.156
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/README.md +3 -3
- data/bin/pwn_sast +1 -1
- data/lib/pwn/plugins/ip_info.rb +53 -19
- data/lib/pwn/plugins/thread_pool.rb +41 -19
- data/lib/pwn/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 47f160c73c391cf48bd0a847d0031d342ef79b77575e86872153105905072141
|
4
|
+
data.tar.gz: 7c71dc97fd8e8e4e99584ab8bbb97363d46a2edd6786f2bb8e4ee9bd7c0b16cb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 55c9643d11b525ef375396f51354e2356363567de4ca1b9e0d9ea9281ff2af58698bd819a38f1e8b0c7212b03012403257c5e4622ae16e7ee018a36e1d66c7e1
|
7
|
+
data.tar.gz: c67b6fe7fbf5966d2630c4a0e99f254e58e6b09a6b15cad13a79897f9ab42c0f2efb7147547c70b4663e0f830af672d72859abc50e64e54b51ad999c4a33220b
|
data/README.md
CHANGED
@@ -37,7 +37,7 @@ $ cd /opt/pwn
|
|
37
37
|
$ ./install.sh
|
38
38
|
$ ./install.sh ruby-gem
|
39
39
|
$ pwn
|
40
|
-
pwn[v0.5.
|
40
|
+
pwn[v0.5.156]:001 >>> PWN.help
|
41
41
|
```
|
42
42
|
|
43
43
|
[](https://youtu.be/G7iLUY4FzsI)
|
@@ -52,7 +52,7 @@ $ rvm use ruby-3.3.1@pwn
|
|
52
52
|
$ gem uninstall --all --executables pwn
|
53
53
|
$ gem install --verbose pwn
|
54
54
|
$ pwn
|
55
|
-
pwn[v0.5.
|
55
|
+
pwn[v0.5.156]:001 >>> PWN.help
|
56
56
|
```
|
57
57
|
|
58
58
|
If you're using a multi-user install of RVM do:
|
@@ -62,7 +62,7 @@ $ rvm use ruby-3.3.1@pwn
|
|
62
62
|
$ rvmsudo gem uninstall --all --executables pwn
|
63
63
|
$ rvmsudo gem install --verbose pwn
|
64
64
|
$ pwn
|
65
|
-
pwn[v0.5.
|
65
|
+
pwn[v0.5.156]:001 >>> PWN.help
|
66
66
|
```
|
67
67
|
|
68
68
|
PWN periodically upgrades to the latest version of Ruby which is reflected in `/opt/pwn/.ruby-version`. The easiest way to upgrade to the latest version of Ruby from a previous PWN installation is to run the following script:
|
data/bin/pwn_sast
CHANGED
data/lib/pwn/plugins/ip_info.rb
CHANGED
@@ -46,12 +46,30 @@ module PWN
|
|
46
46
|
raise e
|
47
47
|
end
|
48
48
|
|
49
|
+
# Supported Method Parameters::
|
50
|
+
# is_rfc1918 = PWN::Plugins::IPInfo.check_rfc1918(
|
51
|
+
# ip: 'required - IP to check'
|
52
|
+
# )
|
53
|
+
public_class_method def self.check_rfc1918(opts = {})
|
54
|
+
ip = opts[:ip].to_s.scrub.strip.chomp
|
55
|
+
ip_obj = IPAddress.valid?(ip) ? IPAddress.parse(ip) : nil
|
56
|
+
|
57
|
+
rfc1918_ranges = [
|
58
|
+
IPAddress('10.0.0.0/8'), # 10.0.0.0 - 10.255.255.255
|
59
|
+
IPAddress('172.16.0.0/12'), # 172.16.0.0 - 172.31.255.255
|
60
|
+
IPAddress('192.168.0.0/16') # 192.168.0.0 - 192.168.255.255
|
61
|
+
]
|
62
|
+
|
63
|
+
rfc1918_ranges.any? { |range| range.include?(ip_obj) }
|
64
|
+
end
|
65
|
+
|
49
66
|
# Supported Method Parameters::
|
50
67
|
# ip_info_struc = PWN::Plugins::IPInfo.get(
|
51
68
|
# target: 'required - IP or Host to lookup',
|
52
69
|
# proxy: 'optional - use a proxy',
|
53
70
|
# tls_port: 'optional port to check cert for Domain Name (default: 443). Will not execute if proxy parameter is set.',
|
54
|
-
# skip_api: 'optional - skip the API call'
|
71
|
+
# skip_api: 'optional - skip the API call',
|
72
|
+
# dns_server: 'optional - DNS server to use for lookup (default: your default DNS server)'
|
55
73
|
# )
|
56
74
|
|
57
75
|
public_class_method def self.get(opts = {})
|
@@ -63,19 +81,29 @@ module PWN
|
|
63
81
|
ip_info_resp = []
|
64
82
|
ip_resp_hash = {}
|
65
83
|
is_ip = IPAddress.valid?(target)
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
84
|
+
hostname = '' if is_ip
|
85
|
+
|
86
|
+
unless is_ip
|
87
|
+
begin
|
88
|
+
hostname = target
|
89
|
+
dns_server = opts[:dns_server]
|
90
|
+
dns_resolver = Resolv::DNS.new(nameserver: [dns_server]) if dns_server
|
91
|
+
dns_resolver ||= Resolv::DNS.new
|
92
|
+
target = dns_resolver.getaddress(target).to_s
|
93
|
+
rescue Resolv::ResolvError
|
94
|
+
target = nil
|
95
|
+
end
|
72
96
|
end
|
73
97
|
|
74
98
|
ip_resp_hash = ip_info_rest_call(ip: target, proxy: proxy) unless skip_api
|
99
|
+
is_rfc1918 = check_rfc1918(ip: target)
|
75
100
|
ip_resp_hash[:ip] = target
|
101
|
+
ip_resp_hash[:is_rfc1918] = is_rfc1918
|
102
|
+
ip_resp_hash[:hostname] = hostname
|
103
|
+
|
76
104
|
ip_info_resp.push(ip_resp_hash) unless target.nil?
|
77
105
|
|
78
|
-
if proxy.nil?
|
106
|
+
if proxy.nil?
|
79
107
|
ip_info_resp.each do |ip_resp|
|
80
108
|
tls_port_avail = PWN::Plugins::Sock.check_port_in_use(
|
81
109
|
server_ip: target,
|
@@ -128,7 +156,7 @@ module PWN
|
|
128
156
|
# PWN::Plugins::IPInfo.bruteforce_subdomains(
|
129
157
|
# parent_domain: 'required - Parent Domain to brute force',
|
130
158
|
# dictionary: 'required - Dictionary to use for subdomain brute force',
|
131
|
-
# max_threads: 'optional - Maximum number of threads to use (default:
|
159
|
+
# max_threads: 'optional - Maximum number of threads to use (default: 9)',
|
132
160
|
# proxy: 'optional - use a proxy',
|
133
161
|
# tls_port: 'optional port to check cert for Domain Name (default: 443). Will not execute if proxy parameter is set.',
|
134
162
|
# results_file: 'optional - File to write results to (default: /tmp/parent_domain-timestamp-pwn_bruteforce_subdomains.txt)'
|
@@ -141,15 +169,14 @@ module PWN
|
|
141
169
|
dictionary = opts[:dictionary] ||= default_dictionary
|
142
170
|
raise "ERROR: Dictionary file not found: #{dictionary}" unless File.exist?(dictionary)
|
143
171
|
|
144
|
-
max_threads = opts[:max_threads]
|
145
|
-
max_threads = 8 unless max_threads.positive?
|
172
|
+
max_threads = opts[:max_threads]
|
146
173
|
|
147
174
|
proxy = opts[:proxy]
|
148
175
|
tls_port = opts[:tls_port]
|
149
176
|
timestamp = Time.now.strftime('%Y-%m-%d_%H.%M.%S')
|
150
177
|
results_file = opts[:results_file] ||= "/tmp/SUBS.#{parent_domain}-#{timestamp}-pwn_bruteforce_subdomains.txt"
|
151
178
|
|
152
|
-
File.write(results_file,
|
179
|
+
File.write(results_file, "[\n")
|
153
180
|
|
154
181
|
# Break up dictonary file into sublines and process each subline in a thread
|
155
182
|
dict_lines = File.readlines(dictionary).shuffle
|
@@ -158,17 +185,16 @@ module PWN
|
|
158
185
|
enumerable_array: dict_lines,
|
159
186
|
max_threads: max_threads
|
160
187
|
) do |subline|
|
188
|
+
print '.'
|
161
189
|
subdomain = subline.to_s.scrub.strip.chomp
|
162
190
|
target = parent_domain if subdomain.empty?
|
163
|
-
target = "#{subdomain}.#{parent_domain}"
|
191
|
+
target = "#{subdomain}.#{parent_domain}" unless subdomain.empty?
|
164
192
|
ip_info_resp = get(
|
165
193
|
target: target,
|
166
194
|
proxy: proxy,
|
167
195
|
tls_port: tls_port,
|
168
196
|
skip_api: true
|
169
197
|
)
|
170
|
-
puts "SUBD: #{target} RESP: #{ip_info_resp}" if ip_info_resp.empty?
|
171
|
-
puts "SUBD: #{target} RESP:\n#{ip_info_resp}" if ip_info_resp.any?
|
172
198
|
|
173
199
|
mutex.synchronize do
|
174
200
|
File.open(results_file, 'a') do |file|
|
@@ -185,8 +211,11 @@ module PWN
|
|
185
211
|
raise e
|
186
212
|
ensure
|
187
213
|
# Strip trailing comma and close JSON array
|
188
|
-
File.readlines(results_file)
|
189
|
-
|
214
|
+
final_results = File.readlines(results_file)
|
215
|
+
# Strip trailing comma from last line
|
216
|
+
last_line = final_results[-1][0..-2]
|
217
|
+
final_results[-1] = last_line
|
218
|
+
File.write(results_file, "#{final_results.join}\n]")
|
190
219
|
end
|
191
220
|
|
192
221
|
# Author(s):: 0day Inc. <support@0dayinc.com>
|
@@ -201,17 +230,22 @@ module PWN
|
|
201
230
|
|
202
231
|
public_class_method def self.help
|
203
232
|
puts "USAGE:
|
233
|
+
is_rfc1918 = #{self}.check_rfc1918(
|
234
|
+
ip: 'required - IP to check'
|
235
|
+
)
|
236
|
+
|
204
237
|
ip_info_struc = #{self}.get(
|
205
238
|
target: 'required - IP or Host to lookup',
|
206
239
|
proxy: 'optional - use a proxy',
|
207
240
|
tls_port: 'optional port to check cert for Domain Name (default: 443). Will not execute if proxy parameter is set.',
|
208
|
-
skip_api: 'optional - skip the API call'
|
241
|
+
skip_api: 'optional - skip the API call',
|
242
|
+
dns_server: 'optional - DNS server to use for lookup (default: your default DNS server)'
|
209
243
|
)
|
210
244
|
|
211
245
|
#{self}.bruteforce_subdomains(
|
212
246
|
parent_domain: 'required - Parent Domain to brute force',
|
213
247
|
dictionary: 'required - Dictionary to use for subdomain brute force',
|
214
|
-
max_threads: 'optional - Maximum number of threads to use (default:
|
248
|
+
max_threads: 'optional - Maximum number of threads to use (default: 9)',
|
215
249
|
proxy: 'optional - use a proxy',
|
216
250
|
tls_port: 'optional port to check cert for Domain Name (default: 443). Will not execute if proxy parameter is set.',
|
217
251
|
results_file: 'optional - File to write results to (default: /tmp/parent_domain-timestamp-pwn_bruteforce_subdomains.txt)'
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'concurrent-ruby'
|
4
|
+
|
3
5
|
module PWN
|
4
6
|
module Plugins
|
5
7
|
# This plugin makes the creation of a thread pool much simpler.
|
@@ -27,34 +29,54 @@ module PWN
|
|
27
29
|
detach = opts[:detach] ||= false
|
28
30
|
|
29
31
|
puts "Initiating Thread Pool of #{max_threads} Worker Threads...."
|
30
|
-
|
31
|
-
threads = Array.new(max_threads) do
|
32
|
-
Thread.new do
|
33
|
-
until (this_thread = queue.pop) == :POOL_EXHAUSTED
|
34
|
-
yield this_thread
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
enumerable_array.uniq.sort.each do |this_thread|
|
40
|
-
queue << this_thread
|
41
|
-
end
|
32
|
+
pool = Concurrent::FixedThreadPool.new(max_threads)
|
42
33
|
|
43
|
-
|
44
|
-
|
34
|
+
enumerable_array.each do |this_thread|
|
35
|
+
pool.post do
|
36
|
+
yield this_thread
|
37
|
+
end
|
45
38
|
end
|
46
39
|
|
47
|
-
|
48
|
-
|
49
|
-
else
|
50
|
-
threads.each(&:join)
|
51
|
-
end
|
40
|
+
pool.shutdown
|
41
|
+
pool.wait_for_termination unless detach
|
52
42
|
rescue Interrupt
|
53
43
|
puts "\nGoodbye."
|
54
44
|
rescue StandardError => e
|
45
|
+
puts e.backtrace
|
55
46
|
raise e
|
56
47
|
end
|
57
48
|
|
49
|
+
# public_class_method def self.fill(opts = {})
|
50
|
+
# enumerable_array = opts[:enumerable_array]
|
51
|
+
# max_threads = opts[:max_threads].to_i
|
52
|
+
# max_threads = 9 if max_threads.zero?
|
53
|
+
# detach = opts[:detach] ||= false
|
54
|
+
|
55
|
+
# puts "Initiating Thread Pool of #{max_threads} Worker Threads...."
|
56
|
+
# queue = SizedQueue.new(max_threads)
|
57
|
+
# threads = Array.new(max_threads) do
|
58
|
+
# Thread.new do
|
59
|
+
# until (this_thread = queue.pop) == :POOL_EXHAUSTED
|
60
|
+
# yield this_thread
|
61
|
+
# end
|
62
|
+
# end
|
63
|
+
# end
|
64
|
+
|
65
|
+
# enumerable_array.uniq.each do |this_thread|
|
66
|
+
# queue << this_thread
|
67
|
+
# end
|
68
|
+
|
69
|
+
# max_threads.times do
|
70
|
+
# queue << :POOL_EXHAUSTED
|
71
|
+
# end
|
72
|
+
|
73
|
+
# threads.each(&:join) unless detach
|
74
|
+
# rescue Interrupt
|
75
|
+
# puts "\nGoodbye."
|
76
|
+
# rescue StandardError => e
|
77
|
+
# raise e
|
78
|
+
# end
|
79
|
+
|
58
80
|
# Author(s):: 0day Inc. <support@0dayinc.com>
|
59
81
|
|
60
82
|
public_class_method def self.authors
|
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.5.
|
4
|
+
version: 0.5.156
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- 0day Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-06-
|
11
|
+
date: 2024-06-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|