1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-09 14:11:29 +00:00

o Added UPnP-info NSE script by Thomas Buchanan. It gathers

information from the UPnP service (UDP port 1900) which listens on
  many network devices such as routers, printers, and networked media
  players.
This commit is contained in:
fyodor
2008-01-10 22:12:11 +00:00
parent 2cf283480f
commit 980d9ddd8d
3 changed files with 161 additions and 0 deletions

View File

@@ -1,6 +1,11 @@
# Nmap Changelog ($Id$); -*-text-*- # Nmap Changelog ($Id$); -*-text-*-
o Added UPnP-info NSE script by Thomas Buchanan. It gathers
information from the UPnP service (UDP port 1900) which listens on
many network devices such as routers, printers, and networked media
players.
o Improved rpcinfo.nse to better sort and display available RPC o Improved rpcinfo.nse to better sort and display available RPC
services. [Sven Klemm] services. [Sven Klemm]

155
scripts/UPnP-info.nse Normal file
View File

@@ -0,0 +1,155 @@
-- UPnP network information script
-- rev 0.2 (1-9-2007)
id = "UPnP"
description = "Attempts to extract system information from UPnP service"
author = "Thomas Buchanan <tbuchanan@thecompassgrp.net>"
license = "See nmaps COPYING for licence"
categories = {"safe"}
require("stdnse")
require("shortport")
require("strbuf")
portrule = shortport.portnumber(1900, "udp", {"open", "open|filtered"})
action = function(host, port)
-- create the socket used for our connection
local socket = nmap.new_socket()
-- set a reasonable timeout value
socket:set_timeout(5000)
-- do some exception handling / cleanup
local catch = function()
socket:close()
end
local try = nmap.new_try(catch)
-- connect to the potential UPnP system
try(socket:connect(host.ip, port.number, "udp"))
local payload = strbuf.new()
-- for details about the UPnP message format, see http://upnp.org/resources/documents.asp
payload = payload .. "M-SEARCH * HTTP/1.1\r\n"
payload = payload .. "Host:239.255.255.250:1900\r\n"
payload = payload .. "ST:upnp:rootdevice\r\n"
payload = payload .. "Man:\"ssdp:discover\"\r\n"
payload = payload .. "MX:3\r\n\r\n"
try(socket:send(strbuf.dump(payload)))
local status
local response
-- read in any response we might get
status, response = socket:receive_bytes(1)
if (not status) or (response == "TIMEOUT") then
socket:close()
return
end
-- since we got something back, the port is definitely open
nmap.set_port_state(host, port, "open")
-- buffer to hold script output
local output
if response ~= nil then
-- We should get a response back that has contains one line for the server, and one line for the xml file location
-- these match any combination of upper and lower case responses
local server, location
server = string.match(response, "[Ss][Ee][Rr][Vv][Ee][Rr]:(.-)\010")
if server ~= nil then output = server .. "\n" end
location = string.match(response, "[Ll][Oo][Cc][Aa][Tt][Ii][Oo][Nn]:(.-)\010")
if location ~= nil then
output = output .. "Location: " .. location
local v = nmap.verbosity()
-- the following check can output quite a lot of information, so we require at least one -v flag
if v > 0 then
-- split the location into an IP address, port, and path name for the xml file
local xhost, xport, xfile
xhost = string.match(location, "http://(.-)/")
-- check to see if the host portionof the location specifies a port
-- if not, use port 80 as a standard web server port
if xhost ~= nil and string.match(xhost, ":") then
xport = string.match(xhost, ":(.*)")
xhost = string.match(xhost, "(.*):")
end
if xport == nil then
xport = 80
end
-- check if the IP address in the location matches the IP address we're scanning
-- if not, alert the user, but continue to scan the IP address we're interested in
if xhost ~= host.ip then
output = output .. "\n !! Location did not match target IP address !! "
-- return output
xhost = host.ip
end
-- extract the path name from the location field, but strip off the \r that HTTP servers return
xfile = string.match(location, "http://.-/(.-)\013")
if xfile ~= nil then
strbuf.clear(payload)
-- create an HTTP request for the file, using the host and port we extracted earlier
payload = payload .. "GET /" .. xfile .. " HTTP/1.1\r\n"
payload = payload .. "Accept: text/xml, application/xml, text/html\r\n"
payload = payload .. "User-Agent: Mozilla/4.0 (compatible; NMAP NSE)\r\n"
payload = payload .. "Host: " .. xhost .. ":" .. xport .. "\r\n"
payload = payload .. "Connection: Keep-Alive\r\n"
payload = payload .. "Cache-Control: no-cache\r\n"
payload = payload .. "Pragma: no-cache\r\n\r\n"
socket = nmap.new_socket()
socket:set_timeout(5000)
try(socket:connect(xhost, xport, "tcp"))
try(socket:send(strbuf.dump(payload)))
-- we're expecting an xml file, and for UPnP purposes it should end in </root>
status, response = socket:receive_buf("</root>", true)
if (status) and (response ~= "TIMEOUT") then
if string.match(response, "HTTP/1.%d 200") then
local webserver
-- extract information about the webserver that is handling responses for the UPnP system
webserver = string.match(response, "[Ss][Ee][Rr][Vv][Ee][Rr]:(.-)\010")
if webserver ~= nil then output = output .. "\nWebserver: " .. webserver end
-- the schema for UPnP includes a number of <device> entries, which can a number of interesting fields
for device in string.gmatch(response, "<deviceType>(.-)</UDN>") do
local fn, mnf, mdl, nm, ver
fn = string.match(device, "<friendlyName>(.-)</friendlyName>")
mnf = string.match(device, "<manufacturer>(.-)</manufacturer>")
mdl = string.match(device, "<modelDescription>(.-)</modelDescription>")
nm = string.match(device, "<modelName>(.-)</modelName>")
ver = string.match(device, "<modelNumber>(.-)</modelNumber>")
if fn ~= nil then output = output .. "\n Name: " .. fn end
if mnf ~= nil then output = output .. "\n Manufacturer: " .. mnf end
if mdl ~= nil then output = output .. "\n Model Descr: " .. mdl end
if nm ~= nil then output = output .. "\n Model Name: " .. nm end
if ver ~= nil then output = output .. "\n Model Version: " .. ver end
end
end
end
socket:close()
end
end
end
return output
end
end

View File

@@ -16,6 +16,7 @@ Entry{ category = "safe", filename = "SNMPsysdesr.nse" }
Entry{ category = "vulnerability", filename = "SQLInject.nse" } Entry{ category = "vulnerability", filename = "SQLInject.nse" }
Entry{ category = "intrusive", filename = "SSHv1-support.nse" } Entry{ category = "intrusive", filename = "SSHv1-support.nse" }
Entry{ category = "intrusive", filename = "SSLv2-support.nse" } Entry{ category = "intrusive", filename = "SSLv2-support.nse" }
Entry{ category = "safe", filename = "UPnP-info.nse" }
Entry{ category = "intrusive", filename = "anonFTP.nse" } Entry{ category = "intrusive", filename = "anonFTP.nse" }
Entry{ category = "intrusive", filename = "bruteTelnet.nse" } Entry{ category = "intrusive", filename = "bruteTelnet.nse" }
Entry{ category = "demo", filename = "chargenTest.nse" } Entry{ category = "demo", filename = "chargenTest.nse" }