home *** CD-ROM | disk | FTP | other *** search
/ Hackers Magazine 57 / CdHackersMagazineNr57.iso / Software / Networking / nmap-5.00-setup.exe / scripts / upnp-info.nse < prev    next >
Text File  |  2009-07-06  |  6KB  |  163 lines

  1. description = [[
  2. Attempts to extract system information from the UPnP service.
  3. ]]
  4.  
  5. ---
  6. -- @output
  7. -- |  upnp-info:  System/1.0 UPnP/1.0 IGD/1.0
  8. -- |_ Location: http://192.168.1.1:80/UPnP/IGD.xml
  9.  
  10. author = "Thomas Buchanan <tbuchanan@thecompassgrp.net>"
  11.  
  12. license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
  13.  
  14. categories = {"default", "safe"}
  15.  
  16. require("stdnse")
  17. require("shortport")
  18. require("strbuf")
  19.  
  20. ---
  21. -- Runs on UDP port 1900
  22. portrule = shortport.portnumber(1900, "udp", {"open", "open|filtered"})
  23.  
  24. ---
  25. -- Sends UPnP discovery packet to host, 
  26. -- and extracts service information from results
  27. action = function(host, port)
  28.     
  29.     -- create the socket used for our connection
  30.     local socket = nmap.new_socket()
  31.     
  32.     -- set a reasonable timeout value
  33.     socket:set_timeout(5000)
  34.     
  35.     -- do some exception handling / cleanup
  36.     local catch = function()
  37.         socket:close()
  38.     end
  39.     
  40.     local try = nmap.new_try(catch)
  41.     
  42.     -- connect to the potential UPnP system
  43.     try(socket:connect(host.ip, port.number, "udp"))
  44.     
  45.     local payload = strbuf.new()
  46.     
  47.     -- for details about the UPnP message format, see http://upnp.org/resources/documents.asp
  48.     payload = payload .. "M-SEARCH * HTTP/1.1\r\n"
  49.     payload = payload .. "Host:239.255.255.250:1900\r\n"
  50.     payload = payload .. "ST:upnp:rootdevice\r\n"
  51.     payload = payload .. "Man:\"ssdp:discover\"\r\n"
  52.     payload = payload .. "MX:3\r\n\r\n"
  53.     
  54.     try(socket:send(strbuf.dump(payload)))
  55.     
  56.     local status
  57.     local response
  58.     
  59.     -- read in any response we might get
  60.     status, response = socket:receive_bytes(1)
  61.  
  62.     if (not status) or (response == "TIMEOUT") then
  63.         socket:close()
  64.         return
  65.     end
  66.  
  67.     -- since we got something back, the port is definitely open
  68.     nmap.set_port_state(host, port, "open")
  69.     
  70.     -- buffer to hold script output
  71.     local output
  72.     
  73.     if response ~= nil then
  74.         -- We should get a response back that has contains one line for the server, and one line for the xml file location
  75.         -- these match any combination of upper and lower case responses
  76.         local server, location
  77.         server = string.match(response, "[Ss][Ee][Rr][Vv][Ee][Rr]:(.-)\010")
  78.         if server ~= nil then output = server .. "\n" end
  79.         location = string.match(response, "[Ll][Oo][Cc][Aa][Tt][Ii][Oo][Nn]:(.-)\010")
  80.         if location ~= nil then
  81.             output = output .. "Location: " .. location 
  82.             
  83.             local v = nmap.verbosity()
  84.             
  85.             -- the following check can output quite a lot of information, so we require at least one -v flag
  86.             if v > 0 then
  87.                 -- split the location into an IP address, port, and path name for the xml file
  88.                 local xhost, xport, xfile
  89.                 xhost = string.match(location, "http://(.-)/")
  90.                 -- check to see if the host portionof the location specifies a port
  91.                 -- if not, use port 80 as a standard web server port
  92.                 if xhost ~= nil and string.match(xhost, ":") then
  93.                     xport = string.match(xhost, ":(.*)")
  94.                     xhost = string.match(xhost, "(.*):")
  95.                 end
  96.  
  97.                 if xport == nil then
  98.                     xport = 80
  99.                 end
  100.  
  101.                 -- check if the IP address in the location matches the IP address we're scanning
  102.                 -- if not, alert the user, but continue to scan the IP address we're interested in
  103.                 if xhost ~= host.ip then 
  104.                     output = output .. "\n !! Location did not match target IP address !! "
  105.                 --    return output 
  106.                     xhost = host.ip
  107.                 end
  108.  
  109.                 -- extract the path name from the location field, but strip off the \r that HTTP servers return
  110.                 xfile = string.match(location, "http://.-/(.-)\013")
  111.                 if xfile ~= nil then
  112.                     strbuf.clear(payload)
  113.                     -- create an HTTP request for the file, using the host and port we extracted earlier
  114.                     payload = payload .. "GET /" .. xfile .. " HTTP/1.1\r\n"
  115.                     payload = payload .. "Accept: text/xml, application/xml, text/html\r\n"
  116.                     payload = payload .. "User-Agent: Mozilla/5.0 (compatible; Nmap Scripting Engine; http://nmap.org/book/nse.html)\r\n"
  117.                     payload = payload .. "Host: " .. xhost .. ":" .. xport .. "\r\n"
  118.                     payload = payload .. "Connection: Keep-Alive\r\n"
  119.                     payload = payload .. "Cache-Control: no-cache\r\n"
  120.                     payload = payload .. "Pragma: no-cache\r\n\r\n"
  121.  
  122.                     socket = nmap.new_socket()
  123.                     socket:set_timeout(5000)
  124.  
  125.                     try(socket:connect(xhost, xport, "tcp"))
  126.                     try(socket:send(strbuf.dump(payload)))
  127.                     -- we're expecting an xml file, and for UPnP purposes it should end in </root>
  128.                     status, response = socket:receive_buf("</root>", true)
  129.  
  130.                     if (status) and (response ~= "TIMEOUT") then
  131.                         if string.match(response, "HTTP/1.%d 200") then
  132.                             local webserver
  133.                             -- extract information about the webserver that is handling responses for the UPnP system
  134.                             webserver = string.match(response, "[Ss][Ee][Rr][Vv][Ee][Rr]:(.-)\010")
  135.                             if webserver ~= nil then output = output .. "\nWebserver: " .. webserver end
  136.  
  137.                             -- the schema for UPnP includes a number of <device> entries, which can a number of interesting fields
  138.                             for device in string.gmatch(response, "<deviceType>(.-)</UDN>") do
  139.                                 local fn, mnf, mdl, nm, ver
  140.  
  141.                                 fn = string.match(device, "<friendlyName>(.-)</friendlyName>")
  142.                                 mnf = string.match(device, "<manufacturer>(.-)</manufacturer>")
  143.                                 mdl = string.match(device, "<modelDescription>(.-)</modelDescription>")
  144.                                 nm = string.match(device, "<modelName>(.-)</modelName>")
  145.                                 ver = string.match(device, "<modelNumber>(.-)</modelNumber>")
  146.  
  147.                                 if fn ~= nil then output = output .. "\n Name: " .. fn end
  148.                                 if mnf ~= nil then output = output .. "\n  Manufacturer: " .. mnf end
  149.                                 if mdl ~= nil then output = output .. "\n  Model Descr: " .. mdl end
  150.                                 if nm ~= nil then output = output .. "\n  Model Name: " .. nm end
  151.                                 if ver ~= nil then output = output .. "\n  Model Version: " .. ver end
  152.                             end
  153.                         end
  154.                     end
  155.  
  156.                     socket:close()
  157.                 end
  158.             end    
  159.         end
  160.         return output
  161.     end
  162. end
  163.