Path: blob/master/modules/post/windows/recon/outbound_ports.rb
19567 views
# -*- coding: binary -*-12##3# This module requires Metasploit: https://metasploit.com/download4# Current source: https://github.com/rapid7/metasploit-framework5##67class MetasploitModule < Msf::Post8include Msf::Post::Windows::Priv910def initialize(info = {})11super(12update_info(13info,14'Name' => 'Windows Outbound-Filtering Rules',15'Description' => %q{16This module makes some kind of TCP traceroute to get outbound-filtering rules.17It will try to make a TCP connection to a certain public IP address (this IP18does not need to be under your control) using different TTL incremental values.19This way if you get an answer (ICMP TTL time exceeded packet) from a public IP20device you can infer that the destination port is allowed. Setting STOP to21true the module will stop as soon as you reach a public IP (this will generate22less noise in the network).23},24'License' => MSF_LICENSE,25'Author' => 'Borja Merino <bmerinofe[at]gmail.com>',26'Platform' => 'win',27'SessionTypes' => ['meterpreter'],28'References' => [29['URL', 'http://www.shelliscoming.com/2014/11/getting-outbound-filtering-rules-by.html']30],31'Compat' => {32'Meterpreter' => {33'Commands' => %w[34stdapi_railgun_api35]36}37},38'Notes' => {39'Stability' => [CRASH_SAFE],40'SideEffects' => [],41'Reliability' => []42}43)44)4546register_options(47[48OptAddress.new('ADDRESS', [ true, 'Destination IP address.']),49OptInt.new('HOPS', [true, 'Number of hops to get.', 3]),50OptInt.new('MIN_TTL', [true, 'Starting TTL value.', 1]),51OptString.new('PORTS', [true, 'Ports to test (e.g. 80,443,100-110).', '80,443']),52OptInt.new('TIMEOUT', [true, 'Timeout for the ICMP socket.', 3]),53OptBool.new('STOP', [true, 'Stop when it finds a public IP.', true])54]55)56end5758def icmp_setup59handler = client.railgun.ws2_32.socket('AF_INET', 'SOCK_RAW', 'IPPROTO_ICMP')60if handler['GetLastError'] == 061vprint_good('ICMP raw socket created successfully')62else63print_error("There was an error setting the ICMP raw socket; GetLastError: #{handler['GetLastError']}")64return nil65end6667r = client.railgun.ws2_32.bind(handler['return'], "\x02\x00\x00\x00" << Rex::Socket.addr_aton(session.session_host) << "\x00" * 8, 16)68if r['GetLastError'] == 069vprint_good("ICMP socket successfully bound to #{session.session_host}")70else71print_error("There was an error binding the ICMP socket to #{session.session_host}; GetLastError: #{r['GetLastError']}")72return nil73end7475# int WSAIoctl(76# _In_ SOCKET s,77# _In_ DWORD dwIoControlCode,78# _In_ LPVOID lpvInBuffer,79# _In_ DWORD cbInBuffer,80# _Out_ LPVOID lpvOutBuffer,81# _In_ DWORD cbOutBuffer,82# _Out_ LPDWORD lpcbBytesReturned,83# _In_ LPWSAOVERLAPPED lpOverlapped,84# _In_ LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine85# );8687sio_rcvall = 0x9800000188r = client.railgun.ws2_32.WSAIoctl(handler['return'], sio_rcvall, "\x01", 4, nil, 0, 4, nil, nil)89if r['GetLastError'] == 090return handler['return']91else92print_error("There was an error calling WSAIoctl (ICMP raw socket); GetLastError: #{r['GetLastError']}")93return nil94end95end9697def tcp_setup(ttl)98handler = client.railgun.ws2_32.socket('AF_INET', 'SOCK_STREAM', 'IPPROTO_TCP')99if handler['GetLastError'] == 0100vprint_status('TCP socket created successfully')101else102print_error("There was an error setting the TCP socket; GetLastError: #{handler['GetLastError']}")103return nil104end105106# 0x8004667E = FIONBIO107# Enable non-blocking mode when *argp (third parameter in ioctlsocket) is set to a nonzero value108cmd = 0x8004667E109r = client.railgun.ws2_32.ioctlsocket(handler['return'], cmd, 1)110if r['GetLastError'] == 0111vprint_status('TCP socket successfully configured in non-blocking mode')112else113print_error("There was an error setting the TCP socket in non-blocking mode; GetLastError: #{r['GetLastError']}")114return nil115end116117# int setsockopt(118# _In_ SOCKET s,119# _In_ int level,120# _In_ int optname,121# _In_ const char *optval,122# _In_ int optlen123# );124ipproto_ip = 0125ip_ttl = 4126r = client.railgun.ws2_32.setsockopt(handler['return'], ipproto_ip, ip_ttl, [ttl].pack('C'), 4)127if r['GetLastError'] == 0128vprint_status("TTL value successfully set to #{ttl}")129return handler['return']130else131print_error("There was an error setting the TTL value; GetLastError: #{r['GetLastError']}")132return nil133end134end135136def connections(remote, dst_port, h_icmp, h_tcp, to)137sock_addr = "\x02\x00"138sock_addr << [dst_port].pack('n')139sock_addr << Rex::Socket.addr_aton(remote)140sock_addr << "\x00" * 8141r = client.railgun.ws2_32.connect(h_tcp, sock_addr, 16)142143# A GetLastError == 1035 is expected since the socket is set to non-blocking mode144unless r['GetLastError'] == 10035145print_error("There was an error creating the connection to the peer #{remote}; GetLastError: #{r['GetLastError']}")146return147end148149from = ' ' * 16150151begin152::Timeout.timeout(to) do153r = client.railgun.ws2_32.recvfrom(h_icmp, "\x00" * 100, 100, 0, from, 16)154hop = Rex::Socket.addr_ntoa(r['from'][4..7])155return hop156end157rescue ::Timeout::Error158return nil159end160end161162def run163unless is_admin?164print_error("You don't have enough privileges. Try getsystem.")165return166end167168version = get_version_info169if version.xp_or_2003?170print_error('Windows XP/Server 2003 is not supported')171return172end173174output = cmd_exec('netsh', ' advfirewall firewall add rule name="All ICMP v4" dir=in action=allow protocol=icmpv4:any,any')175print_status("ICMP firewall IN rule established: #{output}")176177session.railgun.ws2_32178remote = datastore['ADDRESS']179to = datastore['TIMEOUT']180181ports = Rex::Socket.portspec_crack(datastore['PORTS'])182183ports.each do |dport|184pub_ip = false185print_status("Testing port #{dport}...")1860.upto(datastore['HOPS'] - 1) do |i|187i += datastore['MIN_TTL']188h_icmp = icmp_setup189break if h_icmp.nil?190191h_tcp = tcp_setup(i)192break if h_tcp.nil?193194hop = connections(remote, dport, h_icmp, h_tcp, to)195if hop.nil?196print_error("#{i} *")197else198print_good("#{i} #{hop}")199unless Rex::Socket.is_internal?(hop)200pub_ip = true201break if datastore['STOP']202end203end204client.railgun.ws2_32.closesocket(h_tcp)205client.railgun.ws2_32.closesocket(h_icmp)206end207print_good("Public IP reached. The TCP port #{dport} is not filtered") if pub_ip208end209end210end211212213