Skip to content

Commit 18347a8

Browse files
committed
Land rapid7#7774, Fix pivoting of UDP sockets in scanners
2 parents b3e8c33 + 04a026e commit 18347a8

File tree

4 files changed

+114
-59
lines changed

4 files changed

+114
-59
lines changed

lib/msf/core/auxiliary/udp_scanner.rb

Lines changed: 62 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,41 @@ def run_batch_size
4343
datastore['BATCHSIZE'].to_i
4444
end
4545

46+
def udp_sock(ip, port)
47+
@udp_socks_mutex.synchronize do
48+
key = "#{ip}:#{port}"
49+
unless @udp_socks.key?(key)
50+
@udp_socks[key] =
51+
Rex::Socket::Udp.create({
52+
'LocalHost' => datastore['CHOST'] || nil,
53+
'LocalPort' => datastore['CPORT'] || 0,
54+
'PeerHost' => ip,
55+
'PeerPort' => port,
56+
'Context' => { 'Msf' => framework, 'MsfExploit' => self }
57+
})
58+
add_socket(@udp_socks[key])
59+
end
60+
return @udp_socks[key]
61+
end
62+
end
63+
64+
def cleanup_udp_socks
65+
@udp_socks_mutex.synchronize do
66+
@udp_socks.each do |key, sock|
67+
@udp_socks.delete(key)
68+
remove_socket(sock)
69+
sock.close
70+
end
71+
end
72+
end
73+
4674
# Start scanning a batch of IP addresses
4775
def run_batch(batch)
48-
@udp_sock = Rex::Socket::Udp.create({
49-
'LocalHost' => datastore['CHOST'] || nil,
50-
'LocalPort' => datastore['CPORT'] || 0,
51-
'Context' => { 'Msf' => framework, 'MsfExploit' => self }
52-
})
53-
add_socket(@udp_sock)
76+
@udp_socks = {}
77+
@udp_socks_mutex = Mutex.new
5478

5579
@udp_send_count = 0
80+
@interval_mutex = Mutex.new
5681

5782
# Provide a hook for pre-scanning setup
5883
scanner_prescan(batch)
@@ -95,9 +120,10 @@ def scanner_spoof_send(data, ip, port, srcip, num_packets=1)
95120
def scanner_send(data, ip, port)
96121

97122
resend_count = 0
123+
sock = nil
98124
begin
99-
100-
@udp_sock.sendto(data, ip, port, 0)
125+
sock = udp_sock(ip, port)
126+
sock.send(data, 0)
101127

102128
rescue ::Errno::ENOBUFS
103129
resend_count += 1
@@ -112,15 +138,16 @@ def scanner_send(data, ip, port)
112138

113139
retry
114140

115-
rescue ::Rex::ConnectionError
141+
rescue ::Rex::ConnectionError, ::Errno::ECONNREFUSED
116142
# This fires for host unreachable, net unreachable, and broadcast sends
117143
# We can safely ignore all of these for UDP sends
118144
end
119145

120-
@udp_send_count += 1
121-
122-
if @udp_send_count % datastore['ScannerRecvInterval'] == 0
123-
scanner_recv(0.1)
146+
@interval_mutex.synchronize do
147+
@udp_send_count += 1
148+
if @udp_send_count % datastore['ScannerRecvInterval'] == 0
149+
scanner_recv(0.1)
150+
end
124151
end
125152

126153
true
@@ -129,29 +156,38 @@ def scanner_send(data, ip, port)
129156
# Process incoming packets and dispatch to the module
130157
# Ensure a response flood doesn't trap us in a loop
131158
# Ignore packets outside of our project's scope
132-
def scanner_recv(timeout=0.1)
159+
def scanner_recv(timeout = 0.1)
133160
queue = []
134-
while (res = @udp_sock.recvfrom(65535, timeout))
161+
start = Time.now
162+
while Time.now - start < timeout do
163+
readable, _, _ = ::IO.select(@udp_socks.values, nil, nil, timeout)
164+
if readable
165+
for sock in readable
166+
res = sock.recvfrom(65535, timeout)
135167

136-
# Ignore invalid responses
137-
break if not res[1]
168+
# Ignore invalid responses
169+
break if not res[1]
138170

139-
# Ignore empty responses
140-
next if not (res[0] and res[0].length > 0)
171+
# Ignore empty responses
172+
next if not (res[0] and res[0].length > 0)
141173

142-
# Trim the IPv6-compat prefix off if needed
143-
shost = res[1].sub(/^::ffff:/, '')
174+
# Trim the IPv6-compat prefix off if needed
175+
shost = res[1].sub(/^::ffff:/, '')
144176

145-
# Ignore the response if we have a boundary
146-
next unless inside_workspace_boundary?(shost)
177+
# Ignore the response if we have a boundary
178+
next unless inside_workspace_boundary?(shost)
147179

148-
queue << [res[0], shost, res[2]]
180+
queue << [res[0], shost, res[2]]
149181

150-
if queue.length > datastore['ScannerRecvQueueLimit']
151-
break
182+
if queue.length > datastore['ScannerRecvQueueLimit']
183+
break
184+
end
185+
end
152186
end
153187
end
154188

189+
cleanup_udp_socks
190+
155191
queue.each do |q|
156192
scanner_process(*q)
157193
end

lib/rex/post/meterpreter/channels/datagram.rb

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,21 @@ def type?
3333
'udp'
3434
end
3535

36-
def recvfrom_nonblock(length,flags = nil)
37-
return [super(length, flags)[0], super(length, flags)[0]]
36+
def recvfrom_nonblock(length, flags = 0)
37+
data = super(length, flags)[0]
38+
sockaddr = super(length, flags)[0]
39+
[data, sockaddr]
3840
end
3941

40-
def send(buf, flags, saddr)
41-
channel.send(buf, flags, saddr)
42+
#
43+
# This should work just like a UDPSocket.send method
44+
#
45+
# send(mesg, flags, host, port) => numbytes_sent click to toggle source
46+
# send(mesg, flags, sockaddr_to) => numbytes_sent
47+
# send(mesg, flags) => numbytes_sent
48+
#
49+
def send(buf, flags, a = nil, b = nil)
50+
channel.send(buf, flags, a, b)
4251
end
4352
end
4453

@@ -53,17 +62,12 @@ def dio_write_handler(packet, data)
5362
)
5463

5564
if peerhost && peerport
56-
# Maxlen here is 65507, to ensure we dont overflow, we need to write twice
57-
# If the other side has a full 64k, handle by splitting up the datagram and
58-
# writing multiple times along with the sockaddr. Consumers calling recvfrom
59-
# repeatedly will buffer up all the pieces.
60-
while data.length > 65507
61-
rsock.syswrite(data[0..65506])
62-
rsock.syswrite(Rex::Socket.to_sockaddr(peerhost,peerport))
63-
data = data - data[0..65506]
64-
end
65-
rsock.syswrite(data)
66-
rsock.syswrite(Rex::Socket.to_sockaddr(peerhost,peerport))
65+
# A datagram can be maximum 65507 bytes, truncate longer messages
66+
rsock.syswrite(data[0..65506])
67+
68+
# We write the data and sockaddr data to the local socket, the pop it
69+
# back in recvfrom_nonblock.
70+
rsock.syswrite(Rex::Socket.to_sockaddr(peerhost, peerport))
6771
return true
6872
else
6973
return false

lib/rex/post/meterpreter/extensions/stdapi/net/socket_subsystem/udp_channel.rb

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -77,24 +77,41 @@ def initialize(client, cid, type, flags)
7777
end
7878

7979
#
80-
# This function is called by Rex::Socket::Udp.sendto and writes data to a specified
81-
# remote peer host/port via the remote end of the channel.
80+
# This function is called by Rex::Socket::Udp.sendto and writes data to a
81+
# specified remote peer host/port via the remote end of the channel.
8282
#
83-
def send(buf, flags, saddr)
84-
_af, peerhost, peerport = Rex::Socket.from_sockaddr(saddr)
83+
# This should work just like a UDPSocket.send method
84+
#
85+
# send(mesg, flags, host, port) => numbytes_sent click to toggle source
86+
# send(mesg, flags, sockaddr_to) => numbytes_sent
87+
# send(mesg, flags) => numbytes_sent
88+
#
89+
def send(buf, flags, a = nil, b = nil)
90+
host = nil
91+
port = nil
8592

86-
addends = [
87-
{
88-
'type' => TLV_TYPE_PEER_HOST,
89-
'value' => peerhost
90-
},
91-
{
92-
'type' => TLV_TYPE_PEER_PORT,
93-
'value' => peerport
94-
}
95-
]
93+
if a && b.nil?
94+
_, host, port = Rex::Socket.from_sockaddr(a)
95+
elsif a && b
96+
host = a
97+
port = b
98+
end
99+
100+
addends = nil
101+
if host && port
102+
addends = [
103+
{
104+
'type' => TLV_TYPE_PEER_HOST,
105+
'value' => host
106+
},
107+
{
108+
'type' => TLV_TYPE_PEER_PORT,
109+
'value' => port
110+
}
111+
]
112+
end
96113

97-
return _write(buf, buf.length, addends)
114+
_write(buf, buf.length, addends)
98115
end
99116

100117
end

modules/auxiliary/scanner/misc/rosewill_rxs3211_passwords.rb

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
class MetasploitModule < Msf::Auxiliary
1010

11-
include Msf::Exploit::Remote::Tcp
11+
include Msf::Exploit::Remote::Udp
1212
include Msf::Auxiliary::Report
1313
include Msf::Auxiliary::Scanner
1414

@@ -42,8 +42,6 @@ def run_host(ip)
4242
password = nil
4343

4444
begin
45-
# Create an unbound UDP socket if no CHOST is specified, otherwise
46-
# create a UDP socket bound to CHOST (in order to avail of pivoting)
4745
udp_sock = Rex::Socket::Udp.create( {
4846
'LocalHost' => datastore['CHOST'] || nil,
4947
'PeerHost' => ip,

0 commit comments

Comments
 (0)