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 +4 -4
- data/lib/rex/socket/comm/local.rb +87 -0
- data/lib/rex/socket/parameters.rb +9 -0
- data/lib/rex/socket/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a71de87f105663943e10753e3a5b6189e28db3d0ee792b92669bda8f064d291d
|
|
4
|
+
data.tar.gz: bb9f07894fad3f5cbf111e2869e6213d852bf39f9f1e1cb6db8ffb01822a4041
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
data/lib/rex/socket/version.rb
CHANGED