Further improvements to NAT-PMP. Faster, more useful, less not useful
parent
ea6824c46f
commit
07f2d4dafe
|
@ -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
|
||||||
|
|
|
@ -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'],
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue