Index: scripts/upnp-info.nse =================================================================== --- scripts/upnp-info.nse (revision 19944) +++ scripts/upnp-info.nse (arbetskopia) @@ -21,64 +21,21 @@ -- Runs on UDP port 1900 portrule = shortport.portnumber(1900, "udp", {"open", "open|filtered"}) ---- --- Sends UPnP discovery packet to host, --- and extracts service information from results -action = function(host, port) +local function process_response( response ) - -- 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 catch = function() socket:close() end local try = nmap.new_try(catch) + local output = {} - -- connect to the potential UPnP system - try(socket:connect(host, port)) - - 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 + server = string.match(response, "[Ss][Ee][Rr][Vv][Ee][Rr]:%s*(.-)\010") + if server ~= nil then table.insert(output, server ) end location = string.match(response, "[Ll][Oo][Cc][Aa][Tt][Ii][Oo][Nn]:(.-)\010") if location ~= nil then - output = output .. "Location: " .. location + table.insert(output, "Location: " .. location ) local v = nmap.verbosity() @@ -98,17 +55,24 @@ 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 + local peer = {} + local _ + + -- status, _, _, peer.ip, peer.port = socket:get_info() + -- + -- -- 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 ~= peer.ip then + -- output = output .. "\n !! Location did not match target IP address !! " + -- -- return output + -- xhost = peer.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 + local payload = strbuf.new() + 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" @@ -119,7 +83,7 @@ payload = payload .. "Cache-Control: no-cache\r\n" payload = payload .. "Pragma: no-cache\r\n\r\n" - socket = nmap.new_socket() + local socket = nmap.new_socket() socket:set_timeout(5000) try(socket:connect(xhost, xport, "tcp")) @@ -132,7 +96,7 @@ 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 + if webserver ~= nil then table.insert(output, "Webserver: " .. webserver) end -- the schema for UPnP includes a number of entries, which can a number of interesting fields for device in string.gmatch(response, "(.-)") do @@ -144,11 +108,11 @@ nm = string.match(device, "(.-)") ver = string.match(device, "(.-)") - 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 + if fn ~= nil then table.insert(output, "Name: " .. fn) end + if mnf ~= nil then table.insert(output,"Manufacturer: " .. mnf) end + if mdl ~= nil then table.insert(output,"Model Descr: " .. mdl) end + if nm ~= nil then table.insert(output,"Model Name: " .. nm) end + if ver ~= nil then table.insert(output,"Model Version: " .. ver) end end end end @@ -160,3 +124,65 @@ return output end end + + +--- +-- Sends UPnP discovery packet to host, +-- and extracts service information from results +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, port)) + + 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 + local output + local result = {} + + while(1) do + -- read in any response we might get + status, response = socket:receive_bytes(1) + + if (not status) or (response == "TIMEOUT") then + socket:close() + break + end + + local status, _, _, peer_ip, peer_port = socket:get_info() + + -- since we got something back, the port is definitely open + nmap.set_port_state(host, port, "open") + + -- buffer to hold script output + output = process_response( response ) + output = { output } + output.name = peer_ip + table.insert( result, output ) + end + + return stdnse.format_output(true, result) +end