rex-socket 0.1.61 → 0.1.62
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/lib/rex/socket/comm/local.rb +123 -98
- data/lib/rex/socket/parameters.rb +8 -1
- data/lib/rex/socket/proxies.rb +31 -2
- data/lib/rex/socket/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: 8f5d0dfb0bb9e3a72914fe37c326aa979f75a6f845208337af3f21ff9b36313c
|
4
|
+
data.tar.gz: 63faf272cb65e2f42da0ff023390da3437e225dfce065314f05b0560b76949bd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6586170be5a1241d17dd818afefc55e981330e7369e984e69be0c1e9a7b5db68185f0b2a73d4176ecdbdcd5e81c48ea9cfc6c8ae5ec04009024f75e3ace90939
|
7
|
+
data.tar.gz: b277f06f584d268172a7aa30f408a415a9a4136519d5aba34d98e0c7ce5c9f54cc915aed182ca461a1baa29d459ef790dbf0d29270ece45eabf3f0e15414c7d8
|
@@ -122,40 +122,31 @@ class Rex::Socket::Comm::Local
|
|
122
122
|
# Creates a socket using the supplied Parameter instance.
|
123
123
|
#
|
124
124
|
def self.create_by_type(param, type, proto = 0)
|
125
|
-
|
126
125
|
# Detect IPv6 addresses and enable IPv6 accordingly
|
127
126
|
if Rex::Socket.support_ipv6?
|
128
|
-
|
129
|
-
local = Rex::Socket.resolv_nbo(param.localhost) if param.localhost
|
130
|
-
peer = Rex::Socket.resolv_nbo(param.peerhost) if param.peerhost
|
131
|
-
|
132
127
|
# Enable IPv6 dual-bind mode for unbound UDP sockets on Linux
|
133
|
-
if type == ::Socket::SOCK_DGRAM && Rex::Compat.is_linux && !
|
128
|
+
if type == ::Socket::SOCK_DGRAM && Rex::Compat.is_linux && !param.localhost && !param.peerhost
|
134
129
|
param.v6 = true
|
135
130
|
|
136
131
|
# Check if either of the addresses is 16 octets long
|
137
|
-
elsif (
|
132
|
+
elsif (param.localhost && Rex::Socket.is_ipv6?(param.localhost)) || (param.peerhost && Rex::Socket.is_ipv6?(param.peerhost))
|
138
133
|
param.v6 = true
|
139
134
|
end
|
140
135
|
|
141
136
|
if param.v6
|
142
|
-
if
|
143
|
-
if
|
137
|
+
if param.localhost && Rex::Socket.is_ipv4?(param.localhost)
|
138
|
+
if Rex::Socket.addr_atoi(param.localhost) == 0
|
144
139
|
param.localhost = '::'
|
145
|
-
elsif local == "\x7f\x00\x00\x01"
|
146
|
-
param.localhost = '::1'
|
147
140
|
else
|
148
|
-
param.localhost = '::ffff:' +
|
141
|
+
param.localhost = '::ffff:' + param.localhost
|
149
142
|
end
|
150
143
|
end
|
151
144
|
|
152
|
-
if
|
153
|
-
if
|
145
|
+
if param.peerhost && Rex::Socket.is_ipv4?(param.peerhost)
|
146
|
+
if Rex::Socket.addr_atoi(param.peerhost) == 0
|
154
147
|
param.peerhost = '::'
|
155
|
-
elsif peer == "\x7f\x00\x00\x01"
|
156
|
-
param.peerhost = '::1'
|
157
148
|
else
|
158
|
-
param.peerhost = '::ffff:' +
|
149
|
+
param.peerhost = '::ffff:' + param.peerhost
|
159
150
|
end
|
160
151
|
end
|
161
152
|
end
|
@@ -179,7 +170,7 @@ class Rex::Socket::Comm::Local
|
|
179
170
|
if param.localport || param.localhost
|
180
171
|
begin
|
181
172
|
|
182
|
-
# SO_REUSEADDR has undesired semantics on Windows,
|
173
|
+
# SO_REUSEADDR has undesired semantics on Windows, instead allowing
|
183
174
|
# sockets to be stolen without warning from other unprotected
|
184
175
|
# processes.
|
185
176
|
unless Rex::Compat.is_windows
|
@@ -210,7 +201,7 @@ class Rex::Socket::Comm::Local
|
|
210
201
|
klass = Rex::Socket::SslTcpServer
|
211
202
|
end
|
212
203
|
elsif param.proto == 'sctp'
|
213
|
-
|
204
|
+
klass = Rex::Socket::SctpServer
|
214
205
|
else
|
215
206
|
raise Rex::BindFailed.new(param.localhost, param.localport), caller
|
216
207
|
end
|
@@ -220,8 +211,6 @@ class Rex::Socket::Comm::Local
|
|
220
211
|
end
|
221
212
|
# Otherwise, if we're creating a client...
|
222
213
|
else
|
223
|
-
chain = []
|
224
|
-
|
225
214
|
# If we were supplied with host information
|
226
215
|
if param.peerhost
|
227
216
|
|
@@ -262,14 +251,13 @@ class Rex::Socket::Comm::Local
|
|
262
251
|
end
|
263
252
|
|
264
253
|
ip6_scope_idx = 0
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
port = chain[0][2].to_i
|
254
|
+
|
255
|
+
if param.proxies?
|
256
|
+
ip = param.proxies.first.host
|
257
|
+
port = param.proxies.first.port
|
258
|
+
else
|
259
|
+
ip = Rex::Socket.getaddress(param.peerhost)
|
260
|
+
port = param.peerport
|
273
261
|
end
|
274
262
|
|
275
263
|
begin
|
@@ -326,24 +314,20 @@ class Rex::Socket::Comm::Local
|
|
326
314
|
end
|
327
315
|
end
|
328
316
|
|
329
|
-
if
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
end
|
336
|
-
}
|
317
|
+
if param.proxies?
|
318
|
+
param.proxies.each_cons(2) do |current_proxy, next_proxy|
|
319
|
+
proxy(sock, current_proxy.scheme, next_proxy.host, next_proxy.port)
|
320
|
+
end
|
321
|
+
current_proxy = param.proxies.last
|
322
|
+
proxy(sock, current_proxy.scheme, param.peerhost, param.peerport)
|
337
323
|
end
|
338
324
|
|
339
325
|
# Now extend the socket with SSL and perform the handshake
|
340
|
-
if
|
326
|
+
if !param.bare? && param.ssl
|
341
327
|
klass = Rex::Socket::SslTcp
|
342
328
|
sock.extend(klass)
|
343
329
|
sock.initsock(param)
|
344
330
|
end
|
345
|
-
|
346
|
-
|
347
331
|
end
|
348
332
|
|
349
333
|
# Notify handlers that a socket has been created.
|
@@ -440,76 +424,49 @@ class Rex::Socket::Comm::Local
|
|
440
424
|
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a response from the proxy"), caller
|
441
425
|
end
|
442
426
|
|
443
|
-
|
444
|
-
|
427
|
+
if (match = ret.match(/HTTP\/.+?\s+(\d+)\s?.+?\r?\n?$/))
|
428
|
+
status_code = match[1].to_i
|
429
|
+
end
|
445
430
|
|
446
|
-
if
|
431
|
+
if status_code != 200
|
447
432
|
raise Rex::ConnectionProxyError.new(host, port, type, "The proxy returned a non-OK response"), caller
|
448
433
|
end
|
449
434
|
when Rex::Socket::Proxies::ProxyType::SOCKS4
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to send the entire request to the proxy"), caller
|
455
|
-
end
|
435
|
+
if !Rex::Socket.is_ipv4?(host)
|
436
|
+
if !Rex::Socket.is_name?(host)
|
437
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "The SOCKS4 target host must be an IPv4 address or a hostname"), caller
|
438
|
+
end
|
456
439
|
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
440
|
+
begin
|
441
|
+
address = Rex::Socket.getaddress(host, false)
|
442
|
+
rescue ::SocketError
|
443
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "The SOCKS4 target '#{host}' could not be resolved to an IP address"), caller
|
444
|
+
end
|
462
445
|
|
463
|
-
|
464
|
-
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a complete response from the proxy"), caller
|
465
|
-
end
|
466
|
-
if ret[1,1] != "\x5a"
|
467
|
-
raise Rex::ConnectionProxyError.new(host, port, type, "Proxy responded with error code #{ret[0,1].unpack("C")[0]}"), caller
|
468
|
-
end
|
469
|
-
when Rex::Socket::Proxies::ProxyType::SOCKS5
|
470
|
-
auth_methods = [5,1,0].pack('CCC')
|
471
|
-
size = sock.put(auth_methods)
|
472
|
-
if size != auth_methods.length
|
473
|
-
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to send the entire request to the proxy"), caller
|
474
|
-
end
|
475
|
-
ret = sock.get_once(2,30)
|
476
|
-
if ret[1,1] == "\xff"
|
477
|
-
raise Rex::ConnectionProxyError.new(host, port, type, "The proxy requires authentication"), caller
|
446
|
+
host = address
|
478
447
|
end
|
479
448
|
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
addr = Rex::Socket.resolv_nbo(host, accepts_ipv6)
|
488
|
-
setup = [5,1,0,4].pack('C4') + addr + [port.to_i].pack('n')
|
489
|
-
else
|
490
|
-
# Then it must be a domain name.
|
491
|
-
# Unfortunately, it looks like the host has always been
|
492
|
-
# resolved by the time it gets here, so this code never runs.
|
493
|
-
setup = [5,1,0,3].pack('C4') + [host.length].pack('C') + host + [port.to_i].pack('n')
|
494
|
-
end
|
449
|
+
self.proxy_socks4a(sock, type, host, port)
|
450
|
+
when Rex::Socket::Proxies::ProxyType::SOCKS5
|
451
|
+
# follow the unofficial convention where SOCKS5 handles the resolution locally (which leaks DNS)
|
452
|
+
if !Rex::Socket.is_ip_addr?(host)
|
453
|
+
if !Rex::Socket.is_name?(host)
|
454
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "The SOCKS5 target host must be an IP address or a hostname"), caller
|
455
|
+
end
|
495
456
|
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
457
|
+
begin
|
458
|
+
address = Rex::Socket.getaddress(host, Rex::Socket.support_ipv6?)
|
459
|
+
rescue ::SocketError
|
460
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "The SOCKS5 target '#{host}' could not be resolved to an IP address"), caller
|
461
|
+
end
|
500
462
|
|
501
|
-
|
502
|
-
response = sock.get_once(10, 30)
|
503
|
-
rescue IOError
|
504
|
-
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a response from the proxy"), caller
|
463
|
+
host = address
|
505
464
|
end
|
506
465
|
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
raise Rex::ConnectionProxyError.new(host, port, type, "Proxy responded with error code #{response[1,1].unpack("C")[0]}"), caller
|
512
|
-
end
|
466
|
+
self.proxy_socks5h(sock, type, host, port)
|
467
|
+
when Rex::Socket::Proxies::ProxyType::SOCKS5H
|
468
|
+
# follow the unofficial convention where SOCKS5H has the proxy server resolve the hostname to and IP address
|
469
|
+
self.proxy_socks5h(sock, type, host, port)
|
513
470
|
else
|
514
471
|
raise RuntimeError, "The proxy type specified is not valid", caller
|
515
472
|
end
|
@@ -531,4 +488,72 @@ class Rex::Socket::Comm::Local
|
|
531
488
|
def self.each_event_handler(handler) # :nodoc:
|
532
489
|
self.instance.each_event_handler(handler)
|
533
490
|
end
|
491
|
+
|
492
|
+
private
|
493
|
+
|
494
|
+
def self.proxy_socks4a(sock, type, host, port)
|
495
|
+
setup = [4,1,port.to_i].pack('CCn') + Rex::Socket.resolv_nbo(host, false) + Rex::Text.rand_text_alpha(rand(8)+1) + "\x00"
|
496
|
+
size = sock.put(setup)
|
497
|
+
if size != setup.length
|
498
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to send the entire request to the proxy"), caller
|
499
|
+
end
|
500
|
+
|
501
|
+
begin
|
502
|
+
ret = sock.get_once(8, 30)
|
503
|
+
rescue IOError
|
504
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a response from the proxy"), caller
|
505
|
+
end
|
506
|
+
|
507
|
+
if ret.nil? || ret.length < 8
|
508
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a complete response from the proxy"), caller
|
509
|
+
end
|
510
|
+
if ret[1,1] != "\x5a"
|
511
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Proxy responded with error code #{ret[0,1].unpack("C")[0]}"), caller
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
def self.proxy_socks5h(sock, type, host, port)
|
516
|
+
auth_methods = [5,1,0].pack('CCC')
|
517
|
+
size = sock.put(auth_methods)
|
518
|
+
if size != auth_methods.length
|
519
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to send the entire request to the proxy"), caller(1)
|
520
|
+
end
|
521
|
+
ret = sock.get_once(2,30)
|
522
|
+
if ret[1,1] == "\xff"
|
523
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "The proxy requires authentication"), caller(1)
|
524
|
+
end
|
525
|
+
|
526
|
+
if Rex::Socket.is_ipv4?(host)
|
527
|
+
accepts_ipv6 = false
|
528
|
+
addr = Rex::Socket.resolv_nbo(host, accepts_ipv6)
|
529
|
+
setup = [5,1,0,1].pack('C4') + addr + [port.to_i].pack('n')
|
530
|
+
elsif Rex::Socket.is_ipv6?(host)
|
531
|
+
raise Rex::RuntimeError.new('Rex::Socket does not support IPv6') unless Rex::Socket.support_ipv6?
|
532
|
+
|
533
|
+
accepts_ipv6 = true
|
534
|
+
addr = Rex::Socket.resolv_nbo(host, accepts_ipv6)
|
535
|
+
setup = [5,1,0,4].pack('C4') + addr + [port.to_i].pack('n')
|
536
|
+
else
|
537
|
+
# Then it must be a domain name.
|
538
|
+
setup = [5,1,0,3].pack('C4') + [host.length].pack('C') + host + [port.to_i].pack('n')
|
539
|
+
end
|
540
|
+
|
541
|
+
size = sock.put(setup)
|
542
|
+
if size != setup.length
|
543
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to send the entire request to the proxy"), caller(1)
|
544
|
+
end
|
545
|
+
|
546
|
+
begin
|
547
|
+
response = sock.get_once(10, 30)
|
548
|
+
rescue IOError
|
549
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a response from the proxy"), caller(1)
|
550
|
+
end
|
551
|
+
|
552
|
+
if response.nil? || response.length < 10
|
553
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a complete response from the proxy"), caller(1)
|
554
|
+
end
|
555
|
+
if response[1,1] != "\x00"
|
556
|
+
raise Rex::ConnectionProxyError.new(host, port, type, "Proxy responded with error code #{response[1,1].unpack("C")[0]}"), caller(1)
|
557
|
+
end
|
558
|
+
end
|
534
559
|
end
|
@@ -323,6 +323,7 @@ class Rex::Socket::Parameters
|
|
323
323
|
# The local host. Equivalent to the LocalHost parameter hash key.
|
324
324
|
# @return [String]
|
325
325
|
attr_writer :localhost
|
326
|
+
|
326
327
|
def localhost
|
327
328
|
return @localhost if @localhost
|
328
329
|
|
@@ -365,7 +366,9 @@ class Rex::Socket::Parameters
|
|
365
366
|
best_comm = nil
|
366
367
|
# If no comm was explicitly specified, try to use the comm that is best fit
|
367
368
|
# to handle the provided host based on the current routing table.
|
368
|
-
if
|
369
|
+
if proxies?
|
370
|
+
best_comm = Rex::Socket::Comm::Local
|
371
|
+
elsif server && localhost
|
369
372
|
best_comm = Rex::Socket::SwitchBoard.best_comm(localhost)
|
370
373
|
elsif peerhost
|
371
374
|
best_comm = Rex::Socket::SwitchBoard.best_comm(peerhost)
|
@@ -483,6 +486,10 @@ class Rex::Socket::Parameters
|
|
483
486
|
# @return [Array]
|
484
487
|
attr_accessor :proxies
|
485
488
|
|
489
|
+
def proxies?
|
490
|
+
proxies && !proxies.empty?
|
491
|
+
end
|
492
|
+
|
486
493
|
alias peeraddr peerhost
|
487
494
|
alias localaddr localhost
|
488
495
|
end
|
data/lib/rex/socket/proxies.rb
CHANGED
@@ -8,12 +8,41 @@ module Rex
|
|
8
8
|
HTTP = 'http'
|
9
9
|
SOCKS4 = 'socks4'
|
10
10
|
SOCKS5 = 'socks5'
|
11
|
+
SOCKS5H = 'socks5h'
|
11
12
|
end
|
12
13
|
|
13
14
|
# @param [String,nil] value A proxy chain of format {type:host:port[,type:host:port][...]}
|
14
|
-
# @return [Array] The array of proxies
|
15
|
+
# @return [Array<URI>] The array of proxies
|
15
16
|
def self.parse(value)
|
16
|
-
|
17
|
+
proxies = []
|
18
|
+
value.to_s.strip.split(',').each do |proxy|
|
19
|
+
proxy = proxy.strip
|
20
|
+
|
21
|
+
# replace the first : with :// so it can be parsed as a URI
|
22
|
+
# URIs will offer more flexibility long term, but we'll keep backwards compatibility for now by treating : as ://
|
23
|
+
proxy = proxy.sub(/\A(\w+):(\w+)/, '\1://\2')
|
24
|
+
uri = URI(proxy)
|
25
|
+
|
26
|
+
unless supported_types.include?(uri.scheme)
|
27
|
+
raise Rex::RuntimeError.new("Unsupported proxy scheme: #{uri.scheme}")
|
28
|
+
end
|
29
|
+
|
30
|
+
if uri.host.nil? || uri.host.empty?
|
31
|
+
raise Rex::RuntimeError.new("A proxy URI must include a valid host.")
|
32
|
+
end
|
33
|
+
|
34
|
+
if uri.port.nil? && uri.scheme.start_with?('socks')
|
35
|
+
uri.port = 1080
|
36
|
+
end
|
37
|
+
|
38
|
+
if uri.port.nil? || uri.port.zero?
|
39
|
+
raise Rex::RuntimeError.new("A proxy URI must include a valid port.")
|
40
|
+
end
|
41
|
+
|
42
|
+
proxies << uri
|
43
|
+
end
|
44
|
+
|
45
|
+
proxies
|
17
46
|
end
|
18
47
|
|
19
48
|
def self.supported_types
|
data/lib/rex/socket/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rex-socket
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.62
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Metasploit Hackers
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-05-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|