local nmap = require "nmap" local http = require "http" local stdnse = require "stdnse" local string = require "string" local shortport = require "shortport" description = [[ The Belkin Wemo Switch is a network enabled power outlet. This scripts obtains information from Belkin Wemo Switch including nearby wireless networks and the current switch state (ON/OFF). There is a separate NSE script that may be used for changing the switch state. No authentication is required. Valid on Belkin Wemo Switch version WeMo_WW_2.00.10966.PVT-OWRT-SNS on 6/24/17 References: * http://websec.ca/blog/view/Belkin-Wemo-Switch-NMap-Scripts * https://www.tripwire.com/state-of-security/featured/my-sector-story-root-shell-on-the-belkin-wemo-switch/ * https://www.exploitee.rs/index.php/Belkin_Wemo ]] --- -- @usage nmap -p49152,49153,49154 --script wemo-info.nse -- -- @output -- | wemo-info: -- | friendlyName: : Wemo Switch -- | deviceType: urn:Belkin:device:controllee:1 -- | manufacturer: Belkin International Inc. -- | manufacturerURL: http://www.belkin.com -- | modelDescription: Belkin Plugin Socket 1.0 -- | modelName: Socket -- | modelNumber: 1.0 -- | modelURL: http://www.belkin.com/plugin/ -- | serialNumber: 220333K0203A4E -- | UDN: uuid:Socket-1_0-220333K0203A4E -- | UPC: 123456789 -- | macAddress: EC1A59EE48E3 -- | firmwareVersion: WeMo_WW_2.00.10966.PVT-OWRT-SNS -- | iconVersion: 0|49154 -- | binaryState: 1 -- | Switch is currently turned: ON -- | Nearby wireless networks: Page:1/1/4$ -- | Visita Cozumel FTW|5|0|OPEN/NONE, -- | PVGP-2|6|0|WPA1PSKWPA2PSK/TKIPAES, -- | INFINITUM|8|65|WPA2PSK/AES, -- |_INFINITUM|11|0|WPA1PSKWPA2PSK/TKIPAES, -- -- @xmloutput -- urn:Belkin:device:controllee:1 -- Belkin International Inc. -- http://www.belkin.com -- Belkin Plugin Socket 1.0 -- Socket -- 1.0 -- http://www.belkin.com/plugin/ -- 220333K0203A4E -- uuid:Socket-1_0-220333K0203A4E -- 123456789 -- EC1A59ED59C4 -- WeMo_WW_2.00.10966.PVT-OWRT-SNS -- 0|49153 -- 1 -- ON -- Page:1/1/4$ Visita Cozumel FTW|5|0|OPEN/NONE, PVGP-2|6|0|WPA1PSKWPA2PSK/TKIPAES, INFINITUM|8|65|WPA2PSK/AES, INFINITUM|11|0|WPA1PSKWPA2PSK/TKIPAES, --- author = "Pedro Joaquin " license = "Same as Nmap--See https://nmap.org/book/man-legal.html" categories = {"discover", "version", "safe"} portrule = shortport.portnumber({49152,49153,49154}) local function GetInformation(host, port) local uri = '/setup.xml' local response = http.get(host, port, uri) if response['status-line'] and response['status-line']:match("200 OK") then --Verify parsing of XML from /setup.xml local deviceType = response['body']:match("([^<]*)") if not deviceType then stdnse.debug1("Problem with XML parsing") return nil,"Problem with XML parsing" end --Parse information from /setup.xml local output = stdnse.output_table() local keylist = {"friendlyName","deviceType","manufacturer","manufacturerURL","modelDescription", "modelName","modelName","modelNumber","modelURL","serialNumber","UDN","UPC","macAddress","firmwareVersion","iconVersion","binaryState"} for _,key in ipairs(keylist) do stdnse.debug1("Looking for : "..key) output[key] = response['body']:match("<"..key..">([^<]*)") end --Identify current Switch state local bstate="Switch is currently turned" if output["binaryState"] == "1" then output[bstate] = "ON" else output[bstate] = "OFF" end --Post request to obtain nearby wireless network information local req = '' local path = "/upnp/control/WiFiSetup1" local options = {header={["SOAPACTION"]='"urn:Belkin:service:WiFiSetup1:1#GetApList"', ["Content-Type"]="text/xml"}} local result = http.post( host, port, path, options, nil, req) stdnse.debug1("Status-a : %s", result['status-line'] or "No Response") if result['status-line'] and result['status-line']:match("200 OK") then output["Nearby wireless networks"] = result['body']:match("([^<]*)") else stdnse.debug1("Status-b : %s", result['status-line'] or "No Response") return false, "Couldn't download file: " .. path end -- set the port version port.version.name = "http" port.version.name_confidence = 10 port.version.product = output["modelDescription"] or nil port.version.version = output["firmwareVersion"] or nil port.version.devicetype = output["deviceType"] or nil table.insert(port.version.cpe, "cpe:/h:".. output["manufacturer"] .. ":" .. output["modelDescription"]) nmap.set_port_version(host, port, "hardmatched") return output else stdnse.debug1("Could not open '%s'", uri) return false, "Could not open "..uri end end action = function(host,port) -- Identify servers that answer 200 to invalid HTTP requests and exit as these would invalidate the tests local status_404, result_404, _ = http.identify_404(host,port) if ( status_404 and result_404 == 200 ) then stdnse.debug1("Exiting due to ambiguous response from web server on %s:%s. All URIs return status 200.", host.ip, port.number) return nil end return GetInformation(host, port) end