Skip to content

Commit 4928cd3

Browse files
committed
Land rapid7#4187, @BorjaMerino's post module to get output rules
2 parents 30916e3 + 49fef9e commit 4928cd3

File tree

1 file changed

+196
-0
lines changed

1 file changed

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

0 commit comments

Comments
 (0)