local nmap = require "nmap" local http = require "http" local stdnse = require "stdnse" local string = require "string" local shortport = require "shortport" description = [[ Trane Tracer SC is an intelligent field panel for communicating with HVAC equipment controllers. According to Trane U.S. Inc., Tracer SC is deployed across several sectors including Commercial Facilities and others. Contents of specific directories on the Tracer SC are exposed with the web server application to unauthenticated users. Valid on Trane Tracer SC version 4.40.1211 and below. Tested on 7/3/17. References: * http://websec.mx ]] --- -- @usage nmap -p80 --script trane-info.nse -- -- @output -- | trane-info: -- | serverName: TracerSC -- | serverTime: 2017-07-03T21:01:02-04:00 -- | serverBootTime: 2017-06-25T03:14:38-04:00 -- | vendorName: Trane -- | productName: Tracer SC -- | productVersion: v4.40.1211 (release) -- | kernelVersion: 2.6.30_HwVer12AB-hydra -- | hardwareType: HwVer12AB -- | hardwareSerialNumber: E15A##### -- | 1:equipmentUri: /equipment/generic/generic/## -- | 1:displayName: BOILER ROOMS -- | 1:deviceName: BOILER ROOMS -- | 1:equipmentFamily: Generic -- | 1:roleDocument: BOILER_ROOMS -- | 1:isOffline: false -- | 2:equipmentUri: /equipment/generic/generic/## -- | 2:displayName: BOILER ROOMS -- | 2:deviceName: BOILER ROOMS -- | 2:equipmentFamily: Generic -- | 2:roleDocument: BOILER_ROOMS -- | 2:isOffline: false -- | 3:equipmentUri: /equipment/generic/generic/## -- | 3:displayName: EXHAUSTS 3 RM-6 -- | 3:deviceName: EXHAUSTS 3 RM-6 -- | 3:equipmentFamily: Generic -- | 3:roleDocument: EXHAUSTS_3_RM-6 -- | 3:isOffline: false -- -- @xmloutput -- TracerSC -- 2017-07-03T21:01:02-04:00 -- 2017-06-25T03:14:38-04:00 -- Trane -- Tracer SC -- v4.40.1211 (release) -- -- 2.6.30_HwVer12AB-hydra -- HwVer12AB -- E15A##### -- /equipment/generic/generic/## -- BOILER ROOMS -- BOILER ROOMS -- Generic -- BOILER_ROOMS -- false -- /equipment/generic/generic/## -- BOILER ROOMS -- BOILER ROOMS -- Generic -- BOILER_ROOMS -- false -- /equipment/generic/generic/## -- EXHAUSTS 3 RM-6 -- EXHAUSTS 3 RM-6 -- Generic -- EXHAUSTS_3_RM-6 -- false --- author = "Pedro Joaquin " license = "Same as Nmap--See https://nmap.org/book/man-legal.html" categories = {"discover", "version", "safe"} portrule = shortport.portnumber({80}) local function GetInformation(host, port) local output = stdnse.output_table() --Get information from /evox/about local uri = '/evox/about' local response = http.get(host, port, uri) if response['status-line'] and response['status-line']:match("200") then --Verify parsing of XML from /evox/about local deviceType = response['body']:match('serverName" val=([^<]*)/>') if not deviceType then stdnse.debug1("Problem with XML parsing of /evox/about") return nil,"Problem with XML parsing of /evox/about" end --Parse information from /evox/about local keylist = {"serverName","serverTime","serverBootTime","vendorName","productName","productVersion","kernelVersion","hardwareType","hardwareSerialNumber"} for _,key in ipairs(keylist) do stdnse.debug1("Looking for : "..key) output[key] = response['body']:match(key..'" val=([^<]*)/>') stdnse.debug1("Found : "..output[key]) output[key] = output[key]:gsub('"', "") end --Get information from /evox/equipment/installedSummary local uri = '/evox/equipment/installedSummary' local response = http.get(host, port, uri) if response['status-line'] and response['status-line']:match("200") then --Verify parsing of XML from /evox/equipment/installedSummary local error = response['body']:match('Error code: 00017') if error then stdnse.debug1("/evox/equipment/installedSummary is not available") end local equipmentUri = response['body']:match('equipmentUri" val=([^<]*)/>') if not equipmentUri then stdnse.debug1("Problem with XML parsing") end if not error then --Parse information from /evox/equipment/installedSummary local keylist = {"equipmentUri","displayName","deviceName","equipmentFamily","roleDocument","isOffline"} local _,lastequipmentUri = response['body']:find(".*equipmentUri") stdnse.debug1("lastequipmentUri : "..lastequipmentUri) local count = 1 local nextequipmentUri = 1 while nextequipmentUri < lastequipmentUri do for _,key in ipairs(keylist) do stdnse.debug1("Looking for : "..key) output[count..":"..key] = response['body']:match(key..'" val=([^<]*)/>',nextequipmentUri) if output[count..":"..key] == nil then output[count..":"..key] = "Not available" else output[count..":"..key] = output[count..":"..key]:gsub('"', "") stdnse.debug1("Found : "..output[count..":"..key]) end end _,nextequipmentUri = response['body']:find("equipmentUri",nextequipmentUri) count = count + 1 stdnse.debug1("Count : "..count) end end end stdnse.debug1("status-line: "..response['status-line']) local error = response['status-line']:match('Error') if error then stdnse.debug1("Request returned a network error.") return nil, "Request returned a network error." end -- Set the port version port.version.name = "http" port.version.name_confidence = 10 port.version.product = output["productName"] port.version.version = output["productVersion"] port.version.devicetype = output["hardwareType"] table.insert(port.version.cpe, "cpe:/h:".. output["vendorName"] .. ":" .. output["productName"]) nmap.set_port_version(host, port, "hardmatched") return output 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