|
| 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 | + |
| 27 | + 'License' => MSF_LICENSE, |
| 28 | + 'Author' => [ 'Borja Merino <bmerinofe[at]gmail.com>' ], |
| 29 | + 'Platform' => [ 'win' ], |
| 30 | + 'SessionTypes' => [ 'meterpreter' ] |
| 31 | + )) |
| 32 | + register_options( |
| 33 | + [ |
| 34 | + OptAddress.new("ADDRESS" , [ true, 'Destination IP address.']), |
| 35 | + OptInt.new('HOPS', [true, 'Number of hops to get.', 3]), |
| 36 | + OptInt.new('MIN_TTL', [true, 'Starting TTL value.', 1]), |
| 37 | + OptString.new('PORTS', [true, 'Ports to test (e.g. 80,443,100-110).']), |
| 38 | + OptInt.new('TIMEOUT', [true, 'Timeout for the ICMP socket.', 3]), |
| 39 | + OptBool.new('STOP', [true, 'Stop when it finds a public IP.', false]) |
| 40 | + ], self.class) |
| 41 | + |
| 42 | + end |
| 43 | + |
| 44 | + def icmp_setup |
| 45 | + handler = client.railgun.ws2_32.socket("AF_INET", "SOCK_RAW", "IPPROTO_ICMP") |
| 46 | + if handler['GetLastError'] != 0 |
| 47 | + print_error("There was an error setting the ICMP raw socket; GetLastError: #{handler['GetLastError']}") |
| 48 | + return nil |
| 49 | + end |
| 50 | + vprint_status("ICMP raw socket created successfully") |
| 51 | + sockaddr = Rex::Socket.to_sockaddr(session.tunnel_peer.partition(':')[0], 0) |
| 52 | + r = client.railgun.ws2_32.bind(handler['return'],sockaddr,16) |
| 53 | + if r['GetLastError'] != 0 |
| 54 | + print_error("There was an error binding the ICMP socket; GetLastError: #{r['GetLastError']}") |
| 55 | + return nil |
| 56 | + end |
| 57 | + |
| 58 | + # int WSAIoctl( |
| 59 | + # _In_ SOCKET s, |
| 60 | + # _In_ DWORD dwIoControlCode, |
| 61 | + # _In_ LPVOID lpvInBuffer, |
| 62 | + # _In_ DWORD cbInBuffer, |
| 63 | + # _Out_ LPVOID lpvOutBuffer, |
| 64 | + # _In_ DWORD cbOutBuffer, |
| 65 | + # _Out_ LPDWORD lpcbBytesReturned, |
| 66 | + # _In_ LPWSAOVERLAPPED lpOverlapped, |
| 67 | + # _In_ LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine |
| 68 | + # ); |
| 69 | + |
| 70 | + sio_rcvall = 0x98000001 |
| 71 | + r = client.railgun.ws2_32.WSAIoctl(handler['return'],sio_rcvall,"\x01",4,nil,0,4,nil,nil) |
| 72 | + if r['GetLastError'] != 0 |
| 73 | + print_error("There was an error calling WSAIoctl (ICMP raw socket); GetLastError: #{r['GetLastError']}") |
| 74 | + return nil |
| 75 | + end |
| 76 | + return handler['return'] |
| 77 | + end |
| 78 | + |
| 79 | + def tcp_setup(ttl) |
| 80 | + handler = client.railgun.ws2_32.socket("AF_INET", "SOCK_STREAM", "IPPROTO_TCP") |
| 81 | + if handler['GetLastError'] != 0 |
| 82 | + print_error("There was an error setting the TCP socket; GetLastError: #{handler['GetLastError']}") |
| 83 | + return nil |
| 84 | + end |
| 85 | + vprint_status("TCP socket created successfully") |
| 86 | + |
| 87 | + fionbio = 0x8004667E |
| 88 | + r = client.railgun.ws2_32.ioctlsocket(handler['return'],fionbio,1) |
| 89 | + if r['GetLastError'] != 0 |
| 90 | + print_error("There was an error setting the TCP socket in non-blocking mode; GetLastError: #{r['GetLastError']}") |
| 91 | + return nil |
| 92 | + end |
| 93 | + vprint_status("TCP socket successfully configured in non-blocking mode") |
| 94 | + |
| 95 | + # int setsockopt( |
| 96 | + # _In_ SOCKET s, |
| 97 | + # _In_ int level, |
| 98 | + # _In_ int optname, |
| 99 | + # _In_ const char *optval, |
| 100 | + #_In_ int optlen |
| 101 | + # ); |
| 102 | + |
| 103 | + ipproto_ip = 0x00000000 |
| 104 | + ip_ttl = 0x00000004 |
| 105 | + r = client.railgun.ws2_32.setsockopt(handler['return'], ipproto_ip, ip_ttl,[ttl].pack('C'),4) |
| 106 | + if r['GetLastError'] != 0 |
| 107 | + print_error("There was an error setting the TTL value; GetLastError: #{r['GetLastError']}") |
| 108 | + return nil |
| 109 | + end |
| 110 | + vprint_status("TTL value successfully set to #{ttl}") |
| 111 | + return handler['return'] |
| 112 | + end |
| 113 | + |
| 114 | + def connections(remote,dport,h_icmp,h_tcp, to) |
| 115 | + sockaddr = Rex::Socket.to_sockaddr(remote, dport) |
| 116 | + r = client.railgun.ws2_32.connect(h_tcp,sockaddr,16) |
| 117 | + # A GetLastError == 1035 is expected since the socket is set to non-blocking mode |
| 118 | + if r['GetLastError'] != 10035 |
| 119 | + print_error("There was an error creating the connection to the peer #{remote}; GetLastError: #{r['GetLastError']}") |
| 120 | + return |
| 121 | + end |
| 122 | + |
| 123 | + from=" "*16 |
| 124 | + |
| 125 | + begin |
| 126 | + ::Timeout.timeout(to) do |
| 127 | + r = client.railgun.ws2_32.recvfrom(h_icmp,"",100,0,from,16) |
| 128 | + hop = Rex::Socket.addr_ntoa(r['from'][4..7]) |
| 129 | + return hop |
| 130 | + end |
| 131 | + rescue ::Timeout::Error |
| 132 | + return nil |
| 133 | + end |
| 134 | + |
| 135 | + end |
| 136 | + |
| 137 | + def run |
| 138 | + if not is_admin? |
| 139 | + print_error("You don't have enough privileges. Try getsystem.") |
| 140 | + return |
| 141 | + end |
| 142 | + |
| 143 | + if sysinfo["OS"] =~ /XP/ |
| 144 | + print_error("Windows XP is not supported") |
| 145 | + return |
| 146 | + end |
| 147 | + |
| 148 | + output = cmd_exec("netsh"," advfirewall firewall add rule name=\"All ICMP v4\" dir=in action=allow protocol=icmpv4:any,any") |
| 149 | + print_status("ICMP firewall IN rule established: #{output}") |
| 150 | + |
| 151 | + session.railgun.ws2_32 |
| 152 | + remote = datastore['ADDRESS'] |
| 153 | + to = datastore['TIMEOUT'] |
| 154 | + |
| 155 | + ports = Rex::Socket.portspec_crack(datastore['PORTS']) |
| 156 | + |
| 157 | + ports.each do |dport| |
| 158 | + print_status("Testing port #{dport}...") |
| 159 | + 0.upto(datastore['HOPS']-1) { |i| |
| 160 | + i = i + datastore['MIN_TTL'] |
| 161 | + h_icmp = icmp_setup |
| 162 | + h_tcp = tcp_setup(i) |
| 163 | + return if h_icmp == nil or h_tcp == nil |
| 164 | + |
| 165 | + hop = connections(remote, dport, h_icmp, h_tcp, to) |
| 166 | + if hop != nil |
| 167 | + print_good("#{i} #{hop}") |
| 168 | + if datastore['STOP'] == true and hop !~ /^\s*(?:10\.|192\.168|172.(?:1[6-9]|2[0-9]|3[01])\.|169\.254)/ |
| 169 | + print_good("Public IP reached. The port #{dport} is not filtered") |
| 170 | + break |
| 171 | + end |
| 172 | + else |
| 173 | + print_error("#{i} *") |
| 174 | + end |
| 175 | + client.railgun.ws2_32.closesocket(h_tcp) |
| 176 | + client.railgun.ws2_32.closesocket(h_icmp) |
| 177 | + } |
| 178 | + end |
| 179 | + end |
| 180 | +end |
0 commit comments