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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 768cf09e183ef82a282e1173e076723617fa5d1d3ef6d159930c5f080e62267c
4
- data.tar.gz: 9cfe878cd33e159f58803d2012150d20f2d9a934222e71e674618b553ef3f4ed
3
+ metadata.gz: 8f5d0dfb0bb9e3a72914fe37c326aa979f75a6f845208337af3f21ff9b36313c
4
+ data.tar.gz: 63faf272cb65e2f42da0ff023390da3437e225dfce065314f05b0560b76949bd
5
5
  SHA512:
6
- metadata.gz: 3135f51b48c61967266a771006fd0b432061f2c2717025695f9843cde59b40d7abf60c4bd7d92f957b9f574700cce4cdc0997e9707b5f2026eab1afd8a879696
7
- data.tar.gz: c2874c8b85019ae4af581bd1d00e046b46d9c82b34b8032c13cc88547966706cde53775f80c856e5f4658ed186adc3aa836bf626f7c530e208ee1e46cc7c2493
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 && !local && !peer
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 (local && local.length == 16) || (peer && peer.length == 16)
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 local && local.length == 4
143
- if local == "\x00\x00\x00\x00"
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:' + Rex::Socket.getaddress(param.localhost, true)
141
+ param.localhost = '::ffff:' + param.localhost
149
142
  end
150
143
  end
151
144
 
152
- if peer && peer.length == 4
153
- if peer == "\x00\x00\x00\x00"
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:' + Rex::Socket.getaddress(param.peerhost, true)
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, intead allowing
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
- klass = Rex::Socket::SctpServer
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
- ip = Rex::Socket.getaddress(param.peerhost)
266
- port = param.peerport
267
-
268
- if param.proxies
269
- chain = param.proxies.dup
270
- chain.push(['host',param.peerhost,param.peerport])
271
- ip = chain[0][1]
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 chain.size > 1
330
- chain.each_with_index {
331
- |proxy, i|
332
- next_hop = chain[i + 1]
333
- if next_hop
334
- proxy(sock, proxy[0], next_hop[1], next_hop[2])
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(param.bare? == false and param.ssl)
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
- resp = Rex::Proto::Http::Response.new
444
- resp.update_cmd_parts(ret.split(/\r?\n/)[0])
427
+ if (match = ret.match(/HTTP\/.+?\s+(\d+)\s?.+?\r?\n?$/))
428
+ status_code = match[1].to_i
429
+ end
445
430
 
446
- if resp.code != 200
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
- supports_ipv6 = false
451
- setup = [4,1,port.to_i].pack('CCn') + Rex::Socket.resolv_nbo(host, supports_ipv6) + Rex::Text.rand_text_alpha(rand(8)+1) + "\x00"
452
- size = sock.put(setup)
453
- if size != setup.length
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
- begin
458
- ret = sock.get_once(8, 30)
459
- rescue IOError
460
- raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a response from the proxy"), caller
461
- end
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
- if ret.nil? || ret.length < 8
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
- if Rex::Socket.is_ipv4?(host)
481
- accepts_ipv6 = false
482
- addr = Rex::Socket.resolv_nbo(host, accepts_ipv6)
483
- setup = [5,1,0,1].pack('C4') + addr + [port.to_i].pack('n')
484
- elsif Rex::Socket.support_ipv6? && Rex::Socket.is_ipv6?(host)
485
- # IPv6 stuff all untested
486
- accepts_ipv6 = true
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
- 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
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
- begin
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
- if response.nil? || response.length < 10
508
- raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a complete response from the proxy"), caller
509
- end
510
- if response[1,1] != "\x00"
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 server and localhost
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
@@ -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, i.e. {[['type', 'host', 'port']]}
15
+ # @return [Array<URI>] The array of proxies
15
16
  def self.parse(value)
16
- value.to_s.strip.split(',').map { |a| a.strip }.map { |a| a.split(':').map { |b| b.strip } }
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
@@ -1,5 +1,5 @@
1
1
  module Rex
2
2
  module Socket
3
- VERSION = "0.1.61"
3
+ VERSION = "0.1.62"
4
4
  end
5
5
  end
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.61
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-04-11 00:00:00.000000000 Z
11
+ date: 2025-05-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake