Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/admin/natpmp/natpmp_map.rb
19813 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'English'
7
class MetasploitModule < Msf::Auxiliary
8
include Msf::Auxiliary::Report
9
include Msf::Auxiliary::Scanner
10
include Msf::Auxiliary::NATPMP
11
include Rex::Proto::NATPMP
12
13
def initialize
14
super(
15
'Name' => 'NAT-PMP Port Mapper',
16
'Description' => 'Map (forward) TCP and UDP ports on NAT devices using NAT-PMP',
17
'Author' => 'Jon Hart <jhart[at]spoofed.org>',
18
'License' => MSF_LICENSE
19
)
20
21
register_options(
22
[
23
OptString.new('EXTERNAL_PORTS', [true, 'The external ports to forward from (0 to let the target choose)', 0]),
24
OptString.new('INTERNAL_PORTS', [true, 'The internal ports to forward to', '22,135-139,80,443,445'])
25
],
26
self.class
27
)
28
end
29
30
def build_ports(ports_string)
31
# We don't use Rex::Socket.portspec_crack because we need to allow 0 and preserve order
32
ports = []
33
ports_string.split(/[ ,]/).map(&:strip).compact.each do |port_part|
34
if /^(?<port>\d+)$/ =~ port_part
35
ports << port.to_i
36
elsif /^(?<low>\d+)\s*-\s*(?<high>\d+)$/ =~ port_part
37
ports |= (low..high).to_a.map(&:to_i)
38
else
39
raise ArgumentError, "Invalid port specification #{port_part}"
40
end
41
end
42
ports
43
end
44
45
def setup
46
super
47
@external_ports = build_ports(datastore['EXTERNAL_PORTS'])
48
@internal_ports = build_ports(datastore['INTERNAL_PORTS'])
49
50
if @external_ports.size > @internal_ports.size
51
raise ArgumentError, "Too many external ports specified (#{@external_ports.size}); " \
52
"must be one port (0) or #{@internal_ports.size} ports"
53
end
54
55
if @external_ports.size < @internal_ports.size
56
if @external_ports != [0]
57
raise ArgumentError, "Incorrect number of external ports specified (#{@external_ports.size}); " \
58
"must be one port (0) or #{@internal_ports.size} ports"
59
else
60
@external_ports = [0] * @internal_ports.size
61
end
62
end
63
end
64
65
def run_host(host)
66
udp_sock = Rex::Socket::Udp.create({
67
'LocalHost' => datastore['CHOST'] || nil,
68
'Context' => { 'Msf' => framework, 'MsfExploit' => self }
69
})
70
add_socket(udp_sock)
71
72
external_address = get_external_address(udp_sock, host, datastore['RPORT']) || host
73
74
@external_ports.each_index do |i|
75
external_port = @external_ports[i]
76
internal_port = @internal_ports[i]
77
78
actual_ext_port = map_port(udp_sock, host, datastore['RPORT'], internal_port, external_port, Rex::Proto::NATPMP.const_get(protocol), lifetime)
79
map_target = Rex::Socket.source_address(host)
80
requested_forwarding = "#{external_address}:#{external_port}/#{protocol}" \
81
' -> ' \
82
"#{map_target}:#{internal_port}/#{protocol}"
83
if actual_ext_port
84
map_target = datastore['CHOST'] || Rex::Socket.source_address(host)
85
actual_forwarding = "#{external_address}:#{actual_ext_port}/#{protocol}" \
86
' -> ' \
87
"#{map_target}:#{internal_port}/#{protocol}"
88
if external_port == 0
89
print_good("#{actual_forwarding} forwarded")
90
elsif external_port != 0 && external_port != actual_ext_port
91
print_good("#{requested_forwarding} could not be forwarded, but #{actual_forwarding} could")
92
else
93
print_good("#{requested_forwarding} forwarded")
94
end
95
else
96
print_error("#{requested_forwarding} could not be forwarded")
97
end
98
99
report_service(
100
host: host,
101
port: datastore['RPORT'],
102
proto: 'udp',
103
name: 'natpmp',
104
state: Msf::ServiceState::Open
105
)
106
end
107
rescue ::Interrupt
108
raise $ERROR_INFO
109
rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionRefused
110
nil
111
rescue StandardError => e
112
print_error("Unknown error: #{e.class} #{e.backtrace}")
113
end
114
end
115
116