Further improvements to NAT-PMP. Faster, more useful, less not useful
parent
ea6824c46f
commit
07f2d4dafe
|
@ -23,5 +23,13 @@ module Auxiliary::NATPMP
|
|||
self.class
|
||||
)
|
||||
end
|
||||
|
||||
def lifetime
|
||||
@lifetime ||= datastore['LIFETIME']
|
||||
end
|
||||
|
||||
def protocol
|
||||
@protocol ||= datastore['PROTOCOL']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -22,15 +22,50 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
register_options(
|
||||
[
|
||||
OptPort.new('EXTERNAL_PORT', [true, 'The external port to foward from']),
|
||||
OptPort.new('INTERNAL_PORT', [true, 'The internal port to forward to']),
|
||||
OptInt.new('LIFETIME', [true, "Time in ms to keep this port forwarded", 3600000]),
|
||||
OptString.new('EXTERNAL_PORTS', [true, 'The external ports to foward from (0 to let the target choose)', 0]),
|
||||
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 (set to 0 to destroy a mapping)", 3600000]),
|
||||
OptEnum.new('PROTOCOL', [true, "Protocol to forward", 'TCP', %w(TCP UDP)]),
|
||||
],
|
||||
self.class
|
||||
)
|
||||
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)
|
||||
begin
|
||||
|
||||
|
@ -40,22 +75,35 @@ class Metasploit3 < Msf::Auxiliary
|
|||
})
|
||||
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
|
||||
@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)
|
||||
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")
|
||||
requested_forwarding = "#{external_address}:#{external_port}/#{protocol}" +
|
||||
" -> " +
|
||||
"#{map_target}:#{internal_port}/#{protocol}"
|
||||
if actual_ext_port
|
||||
map_target = Rex::Socket.source_address(host)
|
||||
actual_forwarding = "#{external_address}:#{actual_ext_port}/#{protocol}" +
|
||||
" -> " +
|
||||
"#{map_target}:#{internal_port}/#{protocol}"
|
||||
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
|
||||
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'],
|
||||
|
|
|
@ -28,48 +28,6 @@ class Metasploit3 < Msf::Auxiliary
|
|||
], self.class)
|
||||
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)
|
||||
begin
|
||||
udp_sock = Rex::Socket::Udp.create({
|
||||
|
@ -78,15 +36,9 @@ class Metasploit3 < Msf::Auxiliary
|
|||
)
|
||||
add_socket(udp_sock)
|
||||
peer = "#{host}:#{datastore['RPORT']}"
|
||||
vprint_status("#{peer} Scanning #{datastore['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
|
||||
vprint_status("#{peer} Scanning #{protocol} ports #{datastore['PORTS']} using NATPMP")
|
||||
|
||||
external_address = get_external_address(udp_sock, host, datastore['RPORT'])
|
||||
if (external_address)
|
||||
print_good("#{peer} responded with external address of #{external_address}")
|
||||
else
|
||||
|
@ -94,18 +46,14 @@ class Metasploit3 < Msf::Auxiliary
|
|||
return
|
||||
end
|
||||
|
||||
Rex::Socket.portspec_crack(datastore['PORTS']).each do |port|
|
||||
# send one request to clear the mapping if *we've* created it before
|
||||
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
|
||||
# clear all mappings
|
||||
map_port(udp_sock, host, datastore['RPORT'], 0, 0, Rex::Proto::NATPMP.const_get(protocol), lifetime)
|
||||
|
||||
# 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)
|
||||
udp_sock.sendto(map_req, host, datastore['RPORT'], 0)
|
||||
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
|
||||
|
||||
|
@ -136,6 +84,14 @@ class Metasploit3 < Msf::Auxiliary
|
|||
if (int != ext)
|
||||
state = Msf::ServiceState::Open
|
||||
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
|
||||
state = Msf::ServiceState::Closed
|
||||
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'])
|
||||
end
|
||||
|
||||
if inside_workspace_boundary?(external_addr)
|
||||
report_service(
|
||||
:host => external_addr,
|
||||
:port => int,
|
||||
:proto => protocol,
|
||||
:state => state
|
||||
)
|
||||
end
|
||||
|
||||
report_service(
|
||||
:host => host,
|
||||
:port => pkt[2],
|
||||
|
@ -161,5 +108,6 @@ class Metasploit3 < Msf::Auxiliary
|
|||
:proto => 'udp',
|
||||
:state => Msf::ServiceState::Open
|
||||
)
|
||||
true
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue