CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/windows/recon/outbound_ports.rb
Views: 11784
1
# -*- coding: binary -*-
2
3
##
4
# This module requires Metasploit: https://metasploit.com/download
5
# Current source: https://github.com/rapid7/metasploit-framework
6
##
7
8
class MetasploitModule < Msf::Post
9
include Msf::Post::Windows::Priv
10
11
def initialize(info = {})
12
super(
13
update_info(
14
info,
15
'Name' => 'Windows Outbound-Filtering Rules',
16
'Description' => %q{
17
This module makes some kind of TCP traceroute to get outbound-filtering rules.
18
It will try to make a TCP connection to a certain public IP address (this IP
19
does not need to be under your control) using different TTL incremental values.
20
This way if you get an answer (ICMP TTL time exceeded packet) from a public IP
21
device you can infer that the destination port is allowed. Setting STOP to
22
true the module will stop as soon as you reach a public IP (this will generate
23
less noise in the network).
24
},
25
'License' => MSF_LICENSE,
26
'Author' => 'Borja Merino <bmerinofe[at]gmail.com>',
27
'Platform' => 'win',
28
'SessionTypes' => ['meterpreter'],
29
'References' => [
30
['URL', 'http://www.shelliscoming.com/2014/11/getting-outbound-filtering-rules-by.html']
31
],
32
'Compat' => {
33
'Meterpreter' => {
34
'Commands' => %w[
35
stdapi_railgun_api
36
]
37
}
38
}
39
)
40
)
41
42
register_options(
43
[
44
OptAddress.new('ADDRESS', [ true, 'Destination IP address.']),
45
OptInt.new('HOPS', [true, 'Number of hops to get.', 3]),
46
OptInt.new('MIN_TTL', [true, 'Starting TTL value.', 1]),
47
OptString.new('PORTS', [true, 'Ports to test (e.g. 80,443,100-110).', '80,443']),
48
OptInt.new('TIMEOUT', [true, 'Timeout for the ICMP socket.', 3]),
49
OptBool.new('STOP', [true, 'Stop when it finds a public IP.', true])
50
]
51
)
52
end
53
54
def icmp_setup
55
handler = client.railgun.ws2_32.socket('AF_INET', 'SOCK_RAW', 'IPPROTO_ICMP')
56
if handler['GetLastError'] == 0
57
vprint_good('ICMP raw socket created successfully')
58
else
59
print_error("There was an error setting the ICMP raw socket; GetLastError: #{handler['GetLastError']}")
60
return nil
61
end
62
63
r = client.railgun.ws2_32.bind(handler['return'], "\x02\x00\x00\x00" << Rex::Socket.addr_aton(session.session_host) << "\x00" * 8, 16)
64
if r['GetLastError'] == 0
65
vprint_good("ICMP socket successfully bound to #{session.session_host}")
66
else
67
print_error("There was an error binding the ICMP socket to #{session.session_host}; GetLastError: #{r['GetLastError']}")
68
return nil
69
end
70
71
# int WSAIoctl(
72
# _In_ SOCKET s,
73
# _In_ DWORD dwIoControlCode,
74
# _In_ LPVOID lpvInBuffer,
75
# _In_ DWORD cbInBuffer,
76
# _Out_ LPVOID lpvOutBuffer,
77
# _In_ DWORD cbOutBuffer,
78
# _Out_ LPDWORD lpcbBytesReturned,
79
# _In_ LPWSAOVERLAPPED lpOverlapped,
80
# _In_ LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
81
# );
82
83
sio_rcvall = 0x98000001
84
r = client.railgun.ws2_32.WSAIoctl(handler['return'], sio_rcvall, "\x01", 4, nil, 0, 4, nil, nil)
85
if r['GetLastError'] == 0
86
return handler['return']
87
else
88
print_error("There was an error calling WSAIoctl (ICMP raw socket); GetLastError: #{r['GetLastError']}")
89
return nil
90
end
91
end
92
93
def tcp_setup(ttl)
94
handler = client.railgun.ws2_32.socket('AF_INET', 'SOCK_STREAM', 'IPPROTO_TCP')
95
if handler['GetLastError'] == 0
96
vprint_status('TCP socket created successfully')
97
else
98
print_error("There was an error setting the TCP socket; GetLastError: #{handler['GetLastError']}")
99
return nil
100
end
101
102
# 0x8004667E = FIONBIO
103
# Enable non-blocking mode when *argp (third parameter in ioctlsocket) is set to a nonzero value
104
cmd = 0x8004667E
105
r = client.railgun.ws2_32.ioctlsocket(handler['return'], cmd, 1)
106
if r['GetLastError'] == 0
107
vprint_status('TCP socket successfully configured in non-blocking mode')
108
else
109
print_error("There was an error setting the TCP socket in non-blocking mode; GetLastError: #{r['GetLastError']}")
110
return nil
111
end
112
113
# int setsockopt(
114
# _In_ SOCKET s,
115
# _In_ int level,
116
# _In_ int optname,
117
# _In_ const char *optval,
118
# _In_ int optlen
119
# );
120
ipproto_ip = 0
121
ip_ttl = 4
122
r = client.railgun.ws2_32.setsockopt(handler['return'], ipproto_ip, ip_ttl, [ttl].pack('C'), 4)
123
if r['GetLastError'] == 0
124
vprint_status("TTL value successfully set to #{ttl}")
125
return handler['return']
126
else
127
print_error("There was an error setting the TTL value; GetLastError: #{r['GetLastError']}")
128
return nil
129
end
130
end
131
132
def connections(remote, dst_port, h_icmp, h_tcp, to)
133
sock_addr = "\x02\x00"
134
sock_addr << [dst_port].pack('n')
135
sock_addr << Rex::Socket.addr_aton(remote)
136
sock_addr << "\x00" * 8
137
r = client.railgun.ws2_32.connect(h_tcp, sock_addr, 16)
138
139
# A GetLastError == 1035 is expected since the socket is set to non-blocking mode
140
unless r['GetLastError'] == 10035
141
print_error("There was an error creating the connection to the peer #{remote}; GetLastError: #{r['GetLastError']}")
142
return
143
end
144
145
from = ' ' * 16
146
147
begin
148
::Timeout.timeout(to) do
149
r = client.railgun.ws2_32.recvfrom(h_icmp, "\x00" * 100, 100, 0, from, 16)
150
hop = Rex::Socket.addr_ntoa(r['from'][4..7])
151
return hop
152
end
153
rescue ::Timeout::Error
154
return nil
155
end
156
end
157
158
def run
159
unless is_admin?
160
print_error("You don't have enough privileges. Try getsystem.")
161
return
162
end
163
164
version = get_version_info
165
if version.xp_or_2003?
166
print_error('Windows XP/Server 2003 is not supported')
167
return
168
end
169
170
output = cmd_exec('netsh', ' advfirewall firewall add rule name="All ICMP v4" dir=in action=allow protocol=icmpv4:any,any')
171
print_status("ICMP firewall IN rule established: #{output}")
172
173
session.railgun.ws2_32
174
remote = datastore['ADDRESS']
175
to = datastore['TIMEOUT']
176
177
ports = Rex::Socket.portspec_crack(datastore['PORTS'])
178
179
ports.each do |dport|
180
pub_ip = false
181
print_status("Testing port #{dport}...")
182
0.upto(datastore['HOPS'] - 1) do |i|
183
i += datastore['MIN_TTL']
184
h_icmp = icmp_setup
185
return if h_icmp.nil?
186
187
h_tcp = tcp_setup(i)
188
return if h_tcp.nil?
189
190
hop = connections(remote, dport, h_icmp, h_tcp, to)
191
if hop.nil?
192
print_error("#{i} *")
193
else
194
print_good("#{i} #{hop}")
195
unless Rex::Socket.is_internal?(hop)
196
pub_ip = true
197
break if datastore['STOP']
198
end
199
end
200
client.railgun.ws2_32.closesocket(h_tcp)
201
client.railgun.ws2_32.closesocket(h_icmp)
202
end
203
print_good("Public IP reached. The TCP port #{dport} is not filtered") if pub_ip
204
end
205
end
206
end
207
208