Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ source 'https://rubygems.org'
# spec.add_runtime_dependency '<name>', [<version requirements>]
gemspec name: 'metasploit-framework'

# Temporary: pull in the rex-socket #recvfrom stdlib alignment + #timed_recvfrom
# until a release is cut. Revert this commit once rex-socket is published.
gem 'rex-socket', git: 'https://github.com/zeroSteiner/rex-socket', branch: 'fix/udp/recvfrom-api'

# separate from test as simplecov is not run on travis-ci
group :coverage do
# code coverage for tests
Expand Down
13 changes: 10 additions & 3 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ GIT
with_env (= 1.1.0)
xml-simple (~> 1.1.9)

GIT
remote: https://github.com/zeroSteiner/rex-socket
revision: 1c1e60bb77abd786ec0c3b35e735a6a8f58a057c
branch: fix/udp/recvfrom-api
specs:
rex-socket (0.1.68)
dnsruby
rex-core

PATH
remote: .
specs:
Expand Down Expand Up @@ -550,9 +559,6 @@ GEM
metasm
rex-core
rex-text
rex-socket (0.1.65)
dnsruby
rex-core
rex-sslscan (0.1.13)
rex-core
rex-socket
Expand Down Expand Up @@ -717,6 +723,7 @@ DEPENDENCIES
pry-byebug
rake
redcarpet
rex-socket!
rspec-rails
rspec-rerun
rubocop (= 1.75.7)
Expand Down
3 changes: 2 additions & 1 deletion lib/metasploit/framework/login_scanner/snmp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,8 @@ def process_logins(opts = {})
def recv_wrapper(sock, max_size, timeout)
res = nil
if protocol == 'udp'
res = sock.recvfrom(max_size, timeout)
data, addr = sock.timed_recvfrom(max_size, timeout)
res = [data, addr[3], addr[1]] if addr
elsif protocol == 'tcp'
ready = ::IO.select([sock], nil, nil, timeout)
if ready
Expand Down
6 changes: 3 additions & 3 deletions lib/msf/core/auxiliary/udp_scanner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ def scanner_recv(timeout = 0.1)
readable, _, _ = ::IO.select(@udp_sockets.values, nil, nil, timeout)
if readable
for sock in readable
res = sock.recvfrom(65535, timeout)
res = sock.recvfrom(65535)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to have removed the timeout here, compared to the file below, where resp, _saddr, _sport = ping_sock.recvfrom(65535, timeout) was changed to resp, = ping_sock.timed_recvfrom(65535, timeout). Why was the timeout removed here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's right, because it's not necessary here. The 3 lines preceeding this one ensure that the socket is already in a readable state. Then the length field is a maximum length, not a total length, so it'll return immediately even if there is less data than 65535 bytes. Between these two controls, the #recvfrom call here should not be blocking and it's not necessary to apply a timeout to it.


# Ignore invalid responses
break if not res[1]
Expand All @@ -182,12 +182,12 @@ def scanner_recv(timeout = 0.1)
next if not (res[0] and res[0].length > 0)

# Trim the IPv6-compat prefix off if needed
shost = res[1].sub(/^::ffff:/, '')
shost = res[1][3].sub(/^::ffff:/, '')

# Ignore the response if we have a boundary
next unless inside_workspace_boundary?(shost)

queue << [res[0], shost, res[2]]
queue << [res[0], shost, res[1][1]]

if queue.length > datastore['ScannerRecvQueueLimit']
break
Expand Down
2 changes: 1 addition & 1 deletion lib/msf/core/exploit/remote/mssql.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def mssql_ping(timeout=5)
})

ping_sock.put("\x02")
resp, _saddr, _sport = ping_sock.recvfrom(65535, timeout)
resp, = ping_sock.timed_recvfrom(65535, timeout)
ping_sock.close

return data if not resp
Expand Down
24 changes: 10 additions & 14 deletions lib/msf/core/exploit/remote/wdbrpc_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def wdbrpc_client_connect
wdbrpc_client_send_disconnect()

udp_sock.sendto(wdbrpc_request_connect(rhost), rhost, rport, 0)
res,src = udp_sock.recvfrom(65535, 5)
res, = udp_sock.timed_recvfrom(65535, 5)
if not res
print_error("No response to TARGET_CONNECT (WDB4)")
return
Expand Down Expand Up @@ -84,7 +84,7 @@ def wdbrpc_client_connect2
wdbrpc_client_send_disconnect()

udp_sock.sendto(wdbrpc_request_connect2(rhost), rhost, rport, 0)
res,src = udp_sock.recvfrom(65535, 5)
res, = udp_sock.timed_recvfrom(65535, 5)
if not res
print_error("No response to TARGET_CONNECT2")
return
Expand Down Expand Up @@ -115,12 +115,8 @@ def wdbrpc_client_memread(offset, length, params=0)

begin
udp_sock.sendto(pkt, rhost, rport, 0)
res,src = udp_sock.recvfrom(65535, 0.5)
if not res and src
raise RuntimeError, "no reply"
end

if res.length <= 48
res, = udp_sock.timed_recvfrom(65535, 0.5)
if res.nil? || res.length <= 48
raise RuntimeError, "short read"
end

Expand All @@ -142,9 +138,9 @@ def wdbrpc_client_memwrite(offset, buffer, params=0)
res = nil

udp_sock.sendto(pkt, rhost, rport, 0)
res,src = udp_sock.recvfrom(65535, 5.0)
res, = udp_sock.timed_recvfrom(65535, 5.0)

if not res and src
unless res
raise RuntimeError, "no reply"
end
res[-4,4].unpack("N")[0]
Expand All @@ -157,9 +153,9 @@ def wdbrpc_client_memscan(offset, depth, buffer, params=0)
res = nil

udp_sock.sendto(pkt, rhost, rport, 0)
res,src = udp_sock.recvfrom(65535, 5.0)
res, = udp_sock.timed_recvfrom(65535, 5.0)

if not res and src
unless res
raise RuntimeError, "no reply"
end
p res
Expand All @@ -173,7 +169,7 @@ def wdbrpc_client_context_kill(ctx_type=0, ctx=0)

begin
udp_sock.sendto(pkt, rhost, rport, 0)
res,src = udp_sock.recvfrom(65535, 0.5)
res, = udp_sock.timed_recvfrom(65535, 0.5)

rescue ::Interrupt
raise $!
Expand All @@ -187,7 +183,7 @@ def wdbrpc_client_send_disconnect
begin
if self.udp_sock
self.udp_sock.sendto(pkt, rhost, rport, 0)
self.udp_sock.recvfrom(65535, 5)
self.udp_sock.timed_recvfrom(65535, 5)
end
rescue ::Interrupt
raise $!
Expand Down
3 changes: 2 additions & 1 deletion lib/msf/core/handler/reverse_udp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,8 @@ def start_handler
loop do
# Accept a client connection
begin
inbound, peerhost, peerport = self.listener_sock.recvfrom
inbound, addr = self.listener_sock.recvfrom(65535)
peerhost, peerport = addr[3], addr[1]
next if peerhost.nil?
comm = self.listener_sock.channel.client if self.listener_sock.respond_to?(:channel) && self.listener_sock.channel.respond_to?(:client)

Expand Down
5 changes: 4 additions & 1 deletion lib/rex/post/meterpreter/channels/datagram.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,16 @@ def cls

module SocketInterface
include Rex::Post::Meterpreter::SocketAbstraction::SocketInterface

MAX_SOCKADDR_LENGTH = 128

def type?
'udp'
end

def recvfrom_nonblock(length, flags = 0)
data = super(length, flags)[0]
sockaddr = super(length, flags)[0]
sockaddr = super(MAX_SOCKADDR_LENGTH, flags)[0]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes a different bug. What was going on here is that the socket address was being read from the peer (the other end of the socket pair), and the length argument was used for it, which caused a failure when the length wasn't sufficient for the address. The second read should always be for enough data to get the whole address.

[data, sockaddr]
end

Expand Down
3 changes: 2 additions & 1 deletion lib/rex/proto/dhcp/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,8 @@ def monitor_socket
r,_,_ = ::IO.select(rds,wds,eds,1)

if (r != nil and r[0] == self.sock)
buf,host,port = self.sock.recvfrom(65535)
buf, addr = self.sock.recvfrom(65535)
host, port = addr[3], addr[1]
# Lame compatabilitiy :-/
from = [host, port]
dispatch_request(from, buf)
Expand Down
2 changes: 1 addition & 1 deletion lib/rex/proto/dns/resolver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ def send_udp(packet,packet_data, nameservers)
@logger.info "Contacting nameserver #{ns} port #{@config[:port]}"
#socket.sendto(packet_data, ns.to_s, @config[:port].to_i, 0)
socket.write(packet_data)
ans = socket.recvfrom(@config[:packet_size])
ans = socket.timed_recvfrom(@config[:packet_size])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the context behind changing this to a timed recvfrom?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old Rex::Socket version of #recvfrom was time-limited while the Ruby standard API method is not, so changing this from #recvfrom to #timed_recvfrom ensures it continues to behave the same way. We're effectively changing it to explicitly call the Rex::Socket API which behaves in the way it was expecting (with the exception of the shape of the return data).

break if ans
rescue Timeout::Error
@logger.warn "Nameserver #{ns} not responding within UDP timeout, trying next one"
Expand Down
3 changes: 2 additions & 1 deletion lib/rex/proto/dns/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,8 @@ def monitor_listener
r,_,_ = ::IO.select(rds,wds,eds,1)

if (r != nil and r[0] == self.udp_sock)
buf,host,port = self.udp_sock.recvfrom(65535)
buf, addr = self.udp_sock.recvfrom(65535)
host, port = addr[3], addr[1]
# Mock up a client object for sending back data
cli = MockDnsClient.new(host, port, r[0])
dispatch_request(cli, buf)
Expand Down
2 changes: 1 addition & 1 deletion lib/rex/proto/iax2/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def create_call
def monitor_socket
while true
begin
pkt, src = self.sock.recvfrom(65535)
pkt, = self.sock.recvfrom(65535)
next if not pkt

# Find the matching call object
Expand Down
3 changes: 2 additions & 1 deletion lib/rex/proto/ldap/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,8 @@ def monitor_listener

next unless (!r.nil? && (r[0] == udp_sock))

buf, host, port = udp_sock.recvfrom(65535)
buf, addr = udp_sock.recvfrom(65535)
host, port = addr[3], addr[1]
# Mock up a client object for sending back data
cli = MockLdapClient.new(host, port, r[0])
cli.extend(LdapClient)
Expand Down
4 changes: 2 additions & 2 deletions lib/rex/proto/natpmp/packet.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def get_external_address(udp_sock, host, port, timeout=1)
vprint_status("#{host}:#{port} - Probing NAT-PMP for external address")
udp_sock.sendto(external_address_request, host, port, 0)
external_address = nil
while (r = udp_sock.recvfrom(12, timeout) and r[1])
while (r = udp_sock.timed_recvfrom(12, timeout))
(ver, op, result, epoch, external_address) = parse_external_address_response(r[0])
if external_address
vprint_good("#{host}:#{port} - NAT-PMP external address is #{external_address}")
Expand All @@ -43,7 +43,7 @@ def map_port(udp_sock, host, port, int_port, ext_port, protocol, lifetime, timeo
# send it
udp_sock.sendto(req, host, datastore['RPORT'], 0)
# handle the reply
while (r = udp_sock.recvfrom(16, timeout) and r[1])
while (r = udp_sock.timed_recvfrom(16, timeout))
(_, _, result, _, _, actual_ext_port, _) = parse_map_port_response(r[0])
return (result == 0 ? actual_ext_port : nil)
end
Expand Down
12 changes: 8 additions & 4 deletions lib/rex/proto/tftp/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ def start_server_socket

def monitor_server_sock
yield "Listening for incoming ACKs" if block_given?
res = self.server_sock.recvfrom(65535)
res = self.server_sock.timed_recvfrom(65535)
res = res ? [res[0], res[1][3], res[1][1]] : ['', nil, nil]
if res and res[0]
code, type, data = parse_tftp_response(res[0])
if code == Constants::OpAck and self.action == :upload
Expand Down Expand Up @@ -112,7 +113,8 @@ def monitor_server_sock
end

def monitor_client_sock
res = self.client_sock.recvfrom(65535)
res = self.client_sock.timed_recvfrom(65535)
res = res ? [res[0], res[1][3], res[1][1]] : ['', nil, nil]
if res[1] # Got a response back, so that's never good; Acks come back on server_sock.
code, type, data = parse_tftp_response(res[0])
yield("Aborting, got code:%d, type:%d, message:'%s'" % [code, type, data]) if block_given?
Expand Down Expand Up @@ -190,7 +192,8 @@ def recv_data(host, port, first_block)
end
current_block = first_block
while current_block.size == 512
res = self.server_sock.recvfrom(65535)
res = self.server_sock.timed_recvfrom(65535)
res = res ? [res[0], res[1][3], res[1][1]] : ['', nil, nil]
if res and res[0]
code, block_num, current_block = parse_tftp_response(res[0])
if code == 3
Expand Down Expand Up @@ -313,7 +316,8 @@ def send_data(host,port)
end
end
send_retries = 0
res = self.server_sock.recvfrom(65535)
res = self.server_sock.timed_recvfrom(65535)
res = res ? [res[0], res[1][3], res[1][1]] : ['', nil, nil]
if res
code, type, msg = parse_tftp_response(res[0])
if code == 4
Expand Down
3 changes: 2 additions & 1 deletion lib/rex/proto/tftp/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,8 @@ def monitor_socket
r,w,e = ::IO.select(rds,wds,eds,1)

if (r != nil and r[0] == self.sock)
buf,host,port = self.sock.recvfrom(65535)
buf, addr = self.sock.recvfrom(65535)
host, port = addr[3], addr[1]
# Lame compatabilitiy :-/
from = [host, port]
dispatch_request(from, buf)
Expand Down
2 changes: 1 addition & 1 deletion modules/auxiliary/dos/scada/allen_bradley_pccc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ def check
connect_udp

udp_sock.put(enip_list_identify_pkt)
res = udp_sock.recvfrom(90)
res = udp_sock.timed_recvfrom(90)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to other comments, why did this become timed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the same answer. In summary the old #recvfrom Rex::Socket API was time-bound while the standard API is not, it's blocking. This changes the invocation to continue to use the time-bound Rex::Socket-speific API.


disconnect_udp

Expand Down
2 changes: 1 addition & 1 deletion modules/auxiliary/dos/upnp/miniupnpd_dos.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def initialize(info = {})

def send_probe(udp_sock, probe)
udp_sock.put(probe)
data = udp_sock.recvfrom
data = udp_sock.timed_recvfrom(65535)
if data && !data[0].empty?
return data[0]
else
Expand Down
4 changes: 2 additions & 2 deletions modules/auxiliary/dos/windows/games/kaillera.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def run
connect_udp
print_status('Sending Crash request...')
udp_sock.put("HELLO0.83\0")
res = udp_sock.recvfrom(15)
res = udp_sock.timed_recvfrom(15) || ['']
disconnect_udp

if res[0] =~ /HELLOD00D([0-9]{1,5})/
Expand All @@ -60,7 +60,7 @@ def run
connect_udp
print_status('Checking target...')
udp_sock.put("HELLO0.83\0")
res = udp_sock.recvfrom(15)
res = udp_sock.timed_recvfrom(15) || ['']
disconnect_udp

if res[0] =~ /HELLO/
Expand Down
8 changes: 4 additions & 4 deletions modules/auxiliary/fuzzers/dns/dns_fuzzer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -123,15 +123,15 @@ def dns_alive(method)

if method == 'UDP'
udp_sock.put(testing_pkt)
res, = udp_sock.recvfrom(65535)
res, = udp_sock.timed_recvfrom(65535)
disconnect_udp
elsif method == 'TCP'
sock.put(testing_pkt)
res, = sock.get_once(-1, 20)
disconnect
end

if res && res.empty?
if res.nil? || res.empty?
print_error("#{msg} The remote server is not responding to DNS requests.")
return false
end
Expand Down Expand Up @@ -283,13 +283,13 @@ def dns_send(data, method)
udp_sock.put(data) if method == 'UDP'
sock.put(data) if method == 'TCP'

res, = udp_sock.recvfrom(65535, 1) if method == 'UDP'
res, = udp_sock.timed_recvfrom(65535, 1) if method == 'UDP'
res, = sock.get_once(-1, 1) if method == 'TCP'

disconnect_udp if method == 'UDP'
disconnect if method == 'TCP'

if res && res.empty?
if res.nil? || res.empty?
@fail_count += 1
if @fail_count == 1
@probably_vuln = @lastdata if !@lastdata.nil?
Expand Down
4 changes: 2 additions & 2 deletions modules/auxiliary/fuzzers/ntp/ntp_protocol_fuzzer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,10 @@ def probe(host, port, message)
rescue ::Errno::EISCONN
udp_sock.write(message)
end
reply = udp_sock.recvfrom(65535, datastore['WAIT'] / 1000.0)
reply = udp_sock.timed_recvfrom(65535, datastore['WAIT'] / 1000.0)
while reply && reply[1]
replies << reply
reply = udp_sock.recvfrom(65535, datastore['WAIT'] / 1000.0)
reply = udp_sock.timed_recvfrom(65535, datastore['WAIT'] / 1000.0)
end
replies
end
Expand Down
2 changes: 1 addition & 1 deletion modules/auxiliary/scanner/chargen/chargen_probe.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def run_host(rhost)
begin
connect_udp
udp_sock.write(data)
r = udp_sock.recvfrom(65535, 0.1)
r = udp_sock.timed_recvfrom(65535, 0.1)

if r and r[1]
vprint_status("#{Rex::Socket.to_authority(rhost, rport)} - Response: #{r[0]}")
Expand Down
Loading
Loading