rex-socket 0.1.65 → 0.1.66

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: 541410639347398150238fdd7bbc7ea008927cd838094a9b93319f3ed6793548
4
- data.tar.gz: 3478989cca0e4993334f9168a6e281dcdacc2c74b18637f59c211962defc26c9
3
+ metadata.gz: a71de87f105663943e10753e3a5b6189e28db3d0ee792b92669bda8f064d291d
4
+ data.tar.gz: bb9f07894fad3f5cbf111e2869e6213d852bf39f9f1e1cb6db8ffb01822a4041
5
5
  SHA512:
6
- metadata.gz: f9ab385de8ae209798b6a6b55d7325b8afa4c3e4a31effb96b3467ba35f48fed4b2d11b8049ab2eccb45366ceb3e7c3b65e4ef390babd4485009f22dee69e33b
7
- data.tar.gz: 67c58ea1bf7605db720034adc9d5a02d8ffc974365fdb07f0192b999e087ae5953a9fc61430e3422c8a6e05e160c960a54af2f826bb80c9e4d7551ca7da2b525
6
+ metadata.gz: 801d1877c1841113ddb0981c5f195bb89c952de7400caef780a80c40df94758a55a142e25a9853fd8a54b94b4c4c8b94bd84481811b81cdae7e0b34bb848c775
7
+ data.tar.gz: a29bf5d722df87d7c736601b6cdb5b85586ef8fa8d13d2e8e99328f1c14a8bd2e97f6398d682d4c42321e17cb793763a0050c36635151083eab998cac005a37f
@@ -158,6 +158,47 @@ class Rex::Socket::Comm::Local
158
158
  # Notify handlers of the before socket create event.
159
159
  self.instance.notify_before_socket_create(self, param)
160
160
 
161
+ # Binding to a specific interface while routing through a proxy is not
162
+ # supported. The proxy comm handles its own socket creation and ignores
163
+ # the interface option entirely, so we fail fast here rather than
164
+ # silently binding to the wrong interface.
165
+ if param.interface && !param.interface.empty? && param.proxies?
166
+ raise Rex::BindFailed.new(param.localhost, param.localport,
167
+ reason: 'Interface option is incompatible with proxy use'), caller
168
+ end
169
+
170
+ # On Windows, interface binding is handled by resolving the interface
171
+ # name to an IP address and overriding localhost before socket creation.
172
+ # No setsockopt-based interface binding is used.
173
+ if param.interface && !param.interface.empty? && Rex::Compat.is_windows
174
+ iface_ip = nil
175
+ begin
176
+ ifaddrs = ::Socket.getifaddrs.select { |ifaddr| ifaddr.name == param.interface }
177
+ iface = ifaddrs.find do |ifaddr|
178
+ if param.v6
179
+ ifaddr.addr&.ipv6?
180
+ else
181
+ ifaddr.addr&.ipv4?
182
+ end
183
+ end
184
+ iface_ip = iface&.addr&.ip_address
185
+ rescue ::SystemCallError, ::SocketError => e
186
+ raise Rex::BindFailed.new(param.localhost, param.localport,
187
+ reason: "Failed to enumerate interfaces: #{e.message}"), caller
188
+ end
189
+ if iface_ip.nil?
190
+ reason = if ifaddrs.empty?
191
+ "Interface #{param.interface} not found"
192
+ else
193
+ "Interface #{param.interface} has no #{param.v6 ? 'IPv6' : 'IPv4'} address"
194
+ end
195
+ raise Rex::BindFailed.new(param.localhost, param.localport,
196
+ reason: reason), caller
197
+ end
198
+ param = param.dup # avoid mutating the caller's instance
199
+ param.localhost = iface_ip
200
+ end
201
+
161
202
  # Create the socket
162
203
  sock = nil
163
204
  if param.v6
@@ -166,6 +207,52 @@ class Rex::Socket::Comm::Local
166
207
  sock = ::Socket.new(::Socket::AF_INET, type, proto)
167
208
  end
168
209
 
210
+ # Apply interface binding BEFORE sock.bind() so the socket appears in netstat
211
+ # and accepts connections on the specified interface
212
+ if param.interface && !param.interface.empty?
213
+ if Rex::Compat.is_linux
214
+ begin
215
+ sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_BINDTODEVICE, param.interface)
216
+ rescue ::Errno::ENODEV, ::Errno::ENXIO
217
+ sock.close
218
+ raise Rex::BindFailed.new(param.localhost, param.localport,
219
+ reason: "Interface #{param.interface} not found"), caller
220
+ rescue ::Errno::EPERM
221
+ sock.close
222
+ raise Rex::BindFailed.new(param.localhost, param.localport,
223
+ reason: "Binding to interface #{param.interface} requires elevated privileges"), caller
224
+ rescue ::SystemCallError
225
+ sock.close
226
+ raise
227
+ end
228
+ elsif Rex::Compat.is_macosx
229
+ begin
230
+ # IP_BOUND_IF may not be defined in Ruby builds on macOS,
231
+ # so we fallback to raw value 25 which is stable across versions.
232
+ ip_bound_if = defined?(::Socket::IP_BOUND_IF) ? ::Socket::IP_BOUND_IF : 25
233
+ iface = ::Socket.getifaddrs.find { |ifaddr| ifaddr.name == param.interface }
234
+ idx = iface&.ifindex
235
+ if idx.nil?
236
+ sock.close
237
+ raise Rex::BindFailed.new(param.localhost, param.localport,
238
+ reason: "Interface #{param.interface} not found"), caller
239
+ end
240
+ sock.setsockopt(::Socket::IPPROTO_IP, ip_bound_if, [idx].pack('I'))
241
+ rescue ::SocketError, ::Errno::ENXIO
242
+ sock.close
243
+ raise Rex::BindFailed.new(param.localhost, param.localport,
244
+ reason: "Interface #{param.interface} not found"), caller
245
+ rescue ::SystemCallError
246
+ sock.close
247
+ raise
248
+ end
249
+ elsif !Rex::Compat.is_windows
250
+ sock.close
251
+ raise Rex::BindFailed.new(param.localhost, param.localport,
252
+ reason: 'Interface binding is not supported on this platform'), caller
253
+ end
254
+ end
255
+
169
256
  # Bind to a given local address and/or port if they are supplied
170
257
  if param.localport || param.localhost
171
258
  begin
@@ -81,6 +81,9 @@ class Rex::Socket::Parameters
81
81
  # retried.
82
82
  # @option hash [Fixnum] 'Timeout' The number of seconds before a connection
83
83
  # should time out
84
+ # @option hash [String] 'Interface' The network interface name to bind the socket to
85
+ # (e.g. 'eth0'). Only honoured by the local Comm; raises Rex::BindFailed if combined
86
+ # with a proxy.
84
87
  def initialize(hash = {})
85
88
  if (hash['PeerHost'])
86
89
  self.peerhost = hash['PeerHost']
@@ -203,6 +206,8 @@ class Rex::Socket::Parameters
203
206
  self.timeout = hash['Timeout'].to_i
204
207
  end
205
208
 
209
+ self.interface = hash['Interface'].to_s.strip if hash['Interface'] && !hash['Interface'].strip.empty?
210
+
206
211
  # Whether to force IPv6 addressing
207
212
  if hash['IPv6'].nil?
208
213
  # if IPv6 isn't specified and at least one host is an IPv6 address and the
@@ -486,6 +491,10 @@ class Rex::Socket::Parameters
486
491
  # @return [Array]
487
492
  attr_accessor :proxies
488
493
 
494
+ # The network interface name to bind the socket to (e.g. 'eth0'). nil means no binding.
495
+ # @return [String, nil]
496
+ attr_accessor :interface
497
+
489
498
  def proxies?
490
499
  proxies && !proxies.empty?
491
500
  end
@@ -1,5 +1,5 @@
1
1
  module Rex
2
2
  module Socket
3
- VERSION = "0.1.65"
3
+ VERSION = "0.1.66"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rex-socket
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.65
4
+ version: 0.1.66
5
5
  platform: ruby
6
6
  authors:
7
7
  - Metasploit Hackers