-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathsonos-info.nse
208 lines (160 loc) · 6.92 KB
/
sonos-info.nse
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
description = [[
Extracts and outputs Sonos info
]]
---
-- @usage nmap -p1400 --script sonos-info.nse <host>
--
-- This script uses patterns to extract useful Sonos info from HTTP/XML
-- responses and writes these to the output.
--
-- @output
-- PORT STATE SERVICE REASON
-- 1400/tcp open cadkey-tablet syn-ack
-- | sonos-info:
-- | modelName: Sonos ProductName
-- | HardwareVersion: 0.0.0.0-0
-- | modelNumber: S0
-- | MACAddress: 00:00:00:00:00:00
-- | modelDescription: Sonos ProductDescription
-- | displayVersion: 0.0
-- | LocalUID: RINCON_00000000000000000
-- | SoftwareVersion: 00.0-00000
-- | WiFiMACAddress: 00:00:00:00:00:00
-- | ExtraInfo: OTP: 0.00.0(0-00-0-zp0s-0.0)
-- |_ SerialNumber: 00--00-00-00-00:00
-- | ZoneName: Room
-- | IPAddress: 000.000.000.000
--
---
categories = {"discovery", "safe"}
author = "Barry Caruth"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
local http = require "http"
local shortport = require "shortport"
local stdnse = require "stdnse"
local table = require "table"
local string = require "string"
-- Patterns to find useful Sonos information in XML response
xml_patterns = {
"<ZoneName>.-</ZoneName>",
"<LocalUID>.-</LocalUID>",
"<SerialNumber>.-</SerialNumber>",
"<SoftwareVersion>.-</SoftwareVersion>",
"<displayVersion>.-</displayVersion>",
"<HardwareVersion>.-</HardwareVersion>",
"<IPAddress>.-</IPAddress>",
"<MACAddress>.-</MACAddress>",
"<ExtraInfo>.-</ExtraInfo>",
"<modelNumber>.-</modelNumber>",
"<modelDescription>.-</modelDescription>",
"<modelName>.-</modelName>",
"<Command cmdline=\'/usr/sbin/brctl showstp br0\'>.-path cost%s-0%s-.-</Command>",
"<Command cmdline=\'/sbin/ifconfig\'>.-</Command>",
}
-- Only run on devices listening on the Sonos web UI port
portrule = shortport.port_or_service( 1400, "http", "tcp", "open")
action = function(host, port)
local info = {}
-- Lua's abbreviated patterns support doesn't have a fixed-number-of-repetitions syntax.
for i, pattern in ipairs(xml_patterns) do
xml_patterns[i] = xml_patterns[i]
end
local index, target, response
local body = {}
-- Get the status page to determine the Sonos device type
target = "/status"
response = http.get(host, port, target)
if response.body then
-- get info URL which varies depending on whether a Sonos bridge/boost or player
target = string.match ( response.body, "(/status/z[lp])")
-- get info content and store in table
response = http.get(host, port, target)
if response.body then
body["info"] = response.body
end
-- get device description content and store in table
target = "/xml/device_description.xml"
response = http.get(host, port, target)
if response.body then
body["desc"] = response.body
end
-- get stp content and store in table
target = "/status/showstp"
response = http.get(host, port, target)
if response.body then
body["stp"] = response.body
end
-- get ifconfig content and store in table
target = "/status/ifconfig"
response = http.get(host, port, target)
if response.body then
body["ifconfig"] = response.body
end
if next(body) then
-- loop through all responses
for j, response in pairs(body) do
-- debug output
stdnse.print_debug(2, "Index: %s", j )
stdnse.print_debug(2, "Response: %s", response )
-- retrive info from XML response using patterns
for i, pattern in ipairs(xml_patterns) do
stdnse.print_debug(1, "Pattern: %s", pattern )
-- assume zero or one match for each patterm
local c = string.match(response, pattern)
-- check we got a match to parse
if c then
stdnse.print_debug(1, "Raw XML: %s", c )
-- parse XML node to separate attribute from value
local attribute, value = string.match (c, '^<(.-)>(.-)</.->$')
-- Special handling for STP information
if string.match(attribute, '^Command cmdline=\'/usr/sbin/brctl showstp br0\'$') then
-- Record that this is the STP Root Bridge
info["STPRootBridge"] = "Yes"
-- Modify the attribute to something more meaningful
attribute = "STPSecondaryNodes"
local stp_secondary = {}
-- find all secondary nodes
for node in string.gmatch(value, 'tunnel to (%w%w:%w%w:%w%w:%w%w:%w%w:%w%w) %(remote STP state = forwarding') do
stdnse.print_debug(1, "STP Node: %s", node )
-- store MAC address in table
stp_secondary[node] = "Yes"
end
-- copy node table ready for storage
value = stp_secondary
end
-- Special handling for ifconfig information
if string.match(attribute, '^Command cmdline=\'/sbin/ifconfig\'$') then
-- Modify the attribute to something more meaningful
attribute = "WiFiMACAddress"
-- Special handling for ifconfig information
local ath0_mac_address = string.match(value, 'ath0.-(%w%w:%w%w:%w%w:%w%w:%w%w:%w%w)')
if ath0_mac_address then
stdnse.print_debug(1, "ATH0 MAC Address: %s", ath0_mac_address )
value = ath0_mac_address
end
end
-- optional debug output
stdnse.print_debug(1, "Attribute: %s", attribute)
stdnse.print_debug(1, "Value: %s", value)
-- store result in info table
if attribute then
info[attribute] = value
end
end -- of match parse
end -- of pattern loop
-- if we got something then increment counter
if (index) then
index = index + 1
else
index = 1
end
end
end -- of body loop
end -- of status check
-- If the table is empty.
if next(info) == nil then
return "Couldn't find any Sonos info."
end
-- return results table
return info
end