summaryrefslogtreecommitdiffstats
path: root/scripts/hnap-info.nse
blob: 0cc53fc5387b64a7d6bae2ceb1e9640500165e90 (plain)
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
local http = require "http"
local table = require "table"
local shortport = require "shortport"
local stdnse = require "stdnse"
local slaxml = require "slaxml"
local nmap = require "nmap"

description = [[
Retrieve hardwares details and configuration information utilizing HNAP, the "Home Network Administration Protocol".
It is an HTTP-Simple Object Access Protocol (SOAP)-based protocol which allows for remote topology discovery,
configuration, and management of devices (routers, cameras, PCs, NAS, etc.)]]

---
-- @usage
-- nmap --script hnap-info -p80,8080 <target>
--
-- @output
-- PORT     STATE SERVICE    REASON
-- 8080/tcp open  http-proxy syn-ack
-- | hnap-info:
-- |   Type: GatewayWithWiFi
-- |   Device: Ingraham
-- |   Vendor: Linksys
-- |   Description: Linksys E1200
-- |   Model: E1200
-- |   Firmware: 1.0.00 build 11
-- |   Presentation URL: http://192.168.1.1/
-- |   SOAPACTIONS:
-- |     http://purenetworks.com/HNAP1/IsDeviceReady
-- |     http://purenetworks.com/HNAP1/GetDeviceSettings
-- |     http://purenetworks.com/HNAP1/SetDeviceSettings
-- |     http://purenetworks.com/HNAP1/GetDeviceSettings2
-- |     http://purenetworks.com/HNAP1/SetDeviceSettings2
--
--
-- @xmloutput
-- <elem key="Type">GatewayWithWiFi</elem>
-- <elem key="Device">Ingraham</elem>
-- <elem key="Vendor">Linksys</elem>
-- <elem key="Description">Linksys E1200</elem>
-- <elem key="Model">E1200</elem>
-- <elem key="Firmware">1.0.00 build 11</elem>
-- <elem key="Presentation URL">http://192.168.1.1/</elem>
-- <table key="SOAPACTIONS">
--   <elem>http://purenetworks.com/HNAP1/IsDeviceReady</elem>
--   <elem>http://purenetworks.com/HNAP1/GetDeviceSettings</elem>
--   <elem>http://purenetworks.com/HNAP1/SetDeviceSettings</elem>
--   <elem>http://purenetworks.com/HNAP1/GetDeviceSettings2</elem>
--   <elem>http://purenetworks.com/HNAP1/SetDeviceSettings2</elem>
-- </table>
-----------------------------------------------------------------------

author = "Gyanendra Mishra"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {
  "safe",
  "discovery",
  "default",
  "version"
}


portrule = function(host, port)
  return (shortport.http(host,port) and nmap.version_intensity() >= 7)
end

local ELEMENTS = {["Type"] = "Type",
["DeviceName"] = "Device",
["VendorName"] = "Vendor",
["ModelDescription"] = "Description",
["ModelName"] = "Model",
["FirmwareVersion"] = "Firmware",
["PresentationURL"] = "Presentation URL",
["string"] = "SOAPACTIONS",
["SubDeviceURLs"] = "Sub Device URLs"}

function get_text_callback(store, name)
  if ELEMENTS[name] == nil then return end
  name = ELEMENTS[name]
  if name == 'SOAPACTIONS' or name == 'Sub Device URLs' or name == 'Type' then
    return function(content)
      store[name] = store[name] or {}
      table.insert(store[name], content)
    end
  else
    return function(content)
      store[name] = content
    end
  end
end

function action (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

  local output = stdnse.output_table()
  local response = http.get(host, port, '/HNAP1')
  if response.status and response.status == 200 then
    local parser = slaxml.parser:new()
    parser._call = {startElement = function(name)
      parser._call.text = get_text_callback(output, name) end,
      closeElement = function(name) parser._call.text = function() return nil end end
    }
    parser:parseSAX(response.body, {stripWhitespace=true})

    -- exit if the parser does not return output
    if not next(output) then return nil end

    -- set the port verson
    port.version.name = "hnap"
    port.version.name_confidence = 10
    port.version.product = output["Description"] or nil
    port.version.version = output["Model"] or nil
    port.version.devicetype = output["Type"] and output["Type"][1] or nil
    port.version.cpe = port.version.cpe or {}

    if output["Vendor"] and output["Model"] then
      table.insert(port.version.cpe, "cpe:/h:".. output["Vendor"]:lower() .. ":" .. output["Model"]:lower())
    end
    nmap.set_port_version(host, port, "hardmatched")

    return output
  end
end