Further improvements to NAT-PMP. Faster, more useful, less not useful

bug/bundler_fix
Jon Hart 2014-10-15 06:39:38 -07:00
parent ea6824c46f
commit 07f2d4dafe
No known key found for this signature in database
GPG Key ID: 2FA9F0A3AFA8E9D3
3 changed files with 85 additions and 81 deletions

View File

@ -23,5 +23,13 @@ module Auxiliary::NATPMP
self.class self.class
) )
end end
def lifetime
@lifetime ||= datastore['LIFETIME']
end
def protocol
@protocol ||= datastore['PROTOCOL']
end
end end
end end

View File

@ -22,15 +22,50 @@ class Metasploit3 < Msf::Auxiliary
register_options( register_options(
[ [
OptPort.new('EXTERNAL_PORT', [true, 'The external port to foward from']), OptString.new('EXTERNAL_PORTS', [true, 'The external ports to foward from (0 to let the target choose)', 0]),
OptPort.new('INTERNAL_PORT', [true, 'The internal port to forward to']), OptString.new('INTERNAL_PORTS', [true, 'The internal ports to forward to', '22,135-139,80,443,445']),
OptInt.new('LIFETIME', [true, "Time in ms to keep this port forwarded", 3600000]), OptInt.new('LIFETIME', [true, "Time in ms to keep this port forwarded (set to 0 to destroy a mapping)", 3600000]),
OptEnum.new('PROTOCOL', [true, "Protocol to forward", 'TCP', %w(TCP UDP)]), OptEnum.new('PROTOCOL', [true, "Protocol to forward", 'TCP', %w(TCP UDP)]),
], ],
self.class self.class
) )
end end
def build_ports(ports_string)
# We don't use Rex::Socket.portspec_crack because we need to allow 0 and preserve order
ports = []
ports_string.split(/[ ,]/).map { |s| s.strip }.compact.each do |port_part|
if /^(?<port>\d+)$/ =~ port_part
ports << port.to_i
elsif /^(?<low>\d+)\s*-\s*(?<high>\d+)$/ =~ port_part
ports |= (low..high).to_a.map(&:to_i)
else
fail ArgumentError, "Invalid port specification #{port_part}"
end
end
ports
end
def setup
super
@external_ports = build_ports(datastore['EXTERNAL_PORTS'])
@internal_ports = build_ports(datastore['INTERNAL_PORTS'])
if @external_ports.size > @internal_ports.size
fail ArgumentError, "Too many external ports specified (#{@external_ports.size}); " +
"must be one port (0) or #{@internal_ports.size} ports"
end
if @external_ports.size < @internal_ports.size
if @external_ports != [0]
fail ArgumentError, "Incorrect number of external ports specified (#{@external_ports.size}); " +
"must be one port (0) or #{@internal_ports.size} ports"
else
@external_ports = [0] * @internal_ports.size
end
end
end
def run_host(host) def run_host(host)
begin begin
@ -40,22 +75,35 @@ class Metasploit3 < Msf::Auxiliary
}) })
add_socket(udp_sock) add_socket(udp_sock)
# new
external_address = get_external_address(udp_sock, host, datastore['RPORT']) || host external_address = get_external_address(udp_sock, host, datastore['RPORT']) || host
actual_ext_port = map_port(udp_sock, host, datastore['RPORT'], datastore['INTERNAL_PORT'], datastore['EXTERNAL_PORT'], Rex::Proto::NATPMP.const_get(datastore['PROTOCOL']), datastore['LIFETIME'])
@external_ports.each_index do |i|
external_port = @external_ports[i]
internal_port = @internal_ports[i]
actual_ext_port = map_port(udp_sock, host, datastore['RPORT'], internal_port, external_port, Rex::Proto::NATPMP.const_get(protocol), lifetime)
map_target = Rex::Socket.source_address(host)
requested_forwarding = "#{external_address}:#{external_port}/#{protocol}" +
" -> " +
"#{map_target}:#{internal_port}/#{protocol}"
if actual_ext_port if actual_ext_port
map_target = Rex::Socket.source_address(host) map_target = Rex::Socket.source_address(host)
if (datastore['EXTERNAL_PORT'] != actual_ext_port) actual_forwarding = "#{external_address}:#{actual_ext_port}/#{protocol}" +
print_status( "#{external_address} " + " -> " +
"#{datastore['EXTERNAL_PORT']}/#{datastore['PROTOCOL']} -> #{map_target} " + "#{map_target}:#{internal_port}/#{protocol}"
"#{datastore['INTERNAL_PORT']}/#{datastore['PROTOCOL']} couldn't be forwarded") if external_port == 0
print_good("#{actual_forwarding} forwarded")
else
if (external_port != 0 && external_port != actual_ext_port)
print_good("#{requested_forwarding} could not be forwarded, but #{actual_forwarding} could")
else
print_good("#{requested_forwarding} forwarded")
end
end
else
print_error("#{requested_forwarding} could not be forwarded")
end end
print_status( "#{external_address} " +
"#{actual_ext_port}/#{datastore['PROTOCOL']} -> #{map_target} " +
"#{datastore['INTERNAL_PORT']}/#{datastore['PROTOCOL']} forwarded")
# report NAT-PMP as being open
report_service( report_service(
:host => host, :host => host,
:port => datastore['RPORT'], :port => datastore['RPORT'],

View File

@ -28,48 +28,6 @@ class Metasploit3 < Msf::Auxiliary
], self.class) ], self.class)
end end
def run_host(host)
begin
udp_sock = Rex::Socket::Udp.create({
'LocalHost' => datastore['CHOST'] || nil,
'Context' => {'Msf' => framework, 'MsfExploit' => self}
})
add_socket(udp_sock)
# new
external_address = get_external_address(udp_sock, host, datastore['RPORT']) || host
actual_ext_port = map_port(udp_sock, host, datastore['RPORT'], datastore['INTERNAL_PORT'], datastore['EXTERNAL_PORT'], Rex::Proto::NATPMP.const_get(datastore['PROTOCOL']), datastore['LIFETIME'])
if actual_ext_port
map_target = Rex::Socket.source_address(host)
if (datastore['EXTERNAL_PORT'] != actual_ext_port)
print_status( "#{external_address} " +
"#{datastore['EXTERNAL_PORT']}/#{datastore['PROTOCOL']} -> #{map_target} " +
"#{datastore['INTERNAL_PORT']}/#{datastore['PROTOCOL']} couldn't be forwarded")
end
print_status( "#{external_address} " +
"#{actual_ext_port}/#{datastore['PROTOCOL']} -> #{map_target} " +
"#{datastore['INTERNAL_PORT']}/#{datastore['PROTOCOL']} forwarded")
# report NAT-PMP as being open
report_service(
:host => host,
:port => datastore['RPORT'],
:proto => 'udp',
:name => 'natpmp',
:state => Msf::ServiceState::Open
)
end
rescue ::Interrupt
raise $!
rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionRefused
nil
rescue ::Exception => e
print_error("Unknown error: #{e.class} #{e.backtrace}")
end
end
def run_host(host) def run_host(host)
begin begin
udp_sock = Rex::Socket::Udp.create({ udp_sock = Rex::Socket::Udp.create({
@ -78,15 +36,9 @@ class Metasploit3 < Msf::Auxiliary
) )
add_socket(udp_sock) add_socket(udp_sock)
peer = "#{host}:#{datastore['RPORT']}" peer = "#{host}:#{datastore['RPORT']}"
vprint_status("#{peer} Scanning #{datastore['PROTOCOL']} ports #{datastore['PORTS']} using NATPMP") vprint_status("#{peer} Scanning #{protocol} ports #{datastore['PORTS']} using NATPMP")
# first, send a request to get the external address
udp_sock.sendto(external_address_request, host, datastore['RPORT'], 0)
external_address = nil
while (r = udp_sock.recvfrom(12, 0.25) and r[1])
(ver,op,result,epoch,external_address) = parse_external_address_response(r[0])
end
external_address = get_external_address(udp_sock, host, datastore['RPORT'])
if (external_address) if (external_address)
print_good("#{peer} responded with external address of #{external_address}") print_good("#{peer} responded with external address of #{external_address}")
else else
@ -94,18 +46,14 @@ class Metasploit3 < Msf::Auxiliary
return return
end end
Rex::Socket.portspec_crack(datastore['PORTS']).each do |port| # clear all mappings
# send one request to clear the mapping if *we've* created it before map_port(udp_sock, host, datastore['RPORT'], 0, 0, Rex::Proto::NATPMP.const_get(protocol), lifetime)
clear_req = map_port_request(port, port, Rex::Proto::NATPMP.const_get(datastore['PROTOCOL']), 0)
udp_sock.sendto(clear_req, host, datastore['RPORT'], 0)
while (r = udp_sock.recvfrom(16, 1.0) and r[1])
end
# now try the real mapping Rex::Socket.portspec_crack(datastore['PORTS']).each do |port|
map_req = map_port_request(port, port, Rex::Proto::NATPMP.const_get(datastore['PROTOCOL']), 1) map_req = map_port_request(port, port, Rex::Proto::NATPMP.const_get(datastore['PROTOCOL']), 1)
udp_sock.sendto(map_req, host, datastore['RPORT'], 0) udp_sock.sendto(map_req, host, datastore['RPORT'], 0)
while (r = udp_sock.recvfrom(16, 1.0) and r[1]) while (r = udp_sock.recvfrom(16, 1.0) and r[1])
handle_reply(host, external_address, r) break if handle_reply(host, external_address, r)
end end
end end
@ -136,6 +84,14 @@ class Metasploit3 < Msf::Auxiliary
if (int != ext) if (int != ext)
state = Msf::ServiceState::Open state = Msf::ServiceState::Open
print_good("#{peer} #{external_addr} - #{int}/#{protocol} #{state} because of successful mapping with unmatched ports") print_good("#{peer} #{external_addr} - #{int}/#{protocol} #{state} because of successful mapping with unmatched ports")
if inside_workspace_boundary?(external_addr)
report_service(
:host => external_addr,
:port => int,
:proto => protocol,
:state => state
)
end
else else
state = Msf::ServiceState::Closed state = Msf::ServiceState::Closed
print_status("#{peer} #{external_addr} - #{int}/#{protocol} #{state} because of successful mapping with matched ports") if (datastore['DEBUG']) print_status("#{peer} #{external_addr} - #{int}/#{protocol} #{state} because of successful mapping with matched ports") if (datastore['DEBUG'])
@ -145,15 +101,6 @@ class Metasploit3 < Msf::Auxiliary
print_status("#{peer} #{external_addr} - #{int}/#{protocol} #{state} because of code #{result} response") if (datastore['DEBUG']) print_status("#{peer} #{external_addr} - #{int}/#{protocol} #{state} because of code #{result} response") if (datastore['DEBUG'])
end end
if inside_workspace_boundary?(external_addr)
report_service(
:host => external_addr,
:port => int,
:proto => protocol,
:state => state
)
end
report_service( report_service(
:host => host, :host => host,
:port => pkt[2], :port => pkt[2],
@ -161,5 +108,6 @@ class Metasploit3 < Msf::Auxiliary
:proto => 'udp', :proto => 'udp',
:state => Msf::ServiceState::Open :state => Msf::ServiceState::Open
) )
true
end end
end end