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

  1. description = [[
  2. Shows the title of the default page of a web server.
  3.  
  4. The script will follow no more than one HTTP redirect, and only if the
  5. redirection leads to the same host. The script may send a DNS query to
  6. determine whether the host the redirect leads to has the same IP address as the
  7. original target.
  8. ]]
  9.  
  10. ---
  11. --@output
  12. -- Interesting ports on scanme.nmap.org (64.13.134.52):
  13. -- PORT   STATE SERVICE
  14. -- 80/tcp open  http
  15. -- |_ html-title.nse: Go ahead and ScanMe!
  16.  
  17. author = "Diman Todorov <diman.todorov@gmail.com>"
  18.  
  19. license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
  20.  
  21. categories = {"default", "discovery", "safe"}
  22.  
  23. local url    = require 'url'
  24. local dns    = require 'dns'
  25. local http   = require 'http'
  26. local ipOps  = require 'ipOps'
  27. local stdnse = require 'stdnse'
  28.  
  29. portrule = function(host, port)
  30.     local svc = { std = { ["http"] = 1, ["http-alt"] = 1 },
  31.                 ssl = { ["https"] = 1, ["https-alt"] = 1 } }
  32.     if port.protocol ~= 'tcp'
  33.     or not ( svc.std[port.service] or svc.ssl[port.service] ) then
  34.         return false
  35.     end
  36.     -- Don't bother running on SSL ports if we don't have SSL.
  37.     if (svc.ssl[port.service] or port.version.service_tunnel == 'ssl')
  38.     and not nmap.have_ssl() then
  39.         return false
  40.     end
  41.     return true
  42. end
  43.  
  44. action = function(host, port)
  45.  
  46.   local data, result, redir, title
  47.  
  48.   data = http.get( host, port, '/' )
  49.  
  50.   -- check for a redirect
  51.   if data and data.status and tostring( data.status ):match( "30%d" ) and data.header and data.header.location then
  52.     redir = ("Did not follow redirect to %s"):format( data.header.location )
  53.     local url = url.parse( data.header.location )
  54.     local loc = redirect_ok( url, host, port )
  55.     if loc then
  56.       -- follow redirect
  57.       redir = ("Requested resource was %s://%s%s%s"):format( url.scheme or port.service, loc.host, (url.port and (":%s"):format(url.port)) or "", loc.path )
  58.       data = http.get( loc.host, loc.port, loc.path )
  59.     else
  60.       loc = nil -- killed so we know we didn't follow a redirect
  61.     end
  62.   end
  63.  
  64.   -- check that body was received
  65.   if data.body and data.body ~= "" then
  66.     result = data.body
  67.   else
  68.     -- debug msg and no output; or no debug msg and some output if we were redirected.
  69.     if not redir then stdnse.print_debug( "html-title.nse: %s did not respond with any data.", host.targetname or host.ip ) end
  70.     return (redir and ("%s %s no page was returned."):format( redir, (loc and ", but") or "and" )) or nil
  71.   end
  72.  
  73.   -- try and match title tags
  74.   title = string.match(result, "<[Tt][Ii][Tt][Ll][Ee][^>]*>([^<]*)</[Tt][Ii][Tt][Ll][Ee]>")
  75.  
  76.   if title and title ~= "" then
  77.     result = string.gsub(title , "[\n\r\t]", "")
  78.     if string.len(title) > 65 then
  79.       stdnse.print_debug("html-title.nse: (%s) Title got truncated!", host.targetname or host.ip );
  80.       result = string.sub(result, 1, 62) .. "..."
  81.     end
  82.   else
  83.     result = ("Site doesn't have a title%s"):format( ( data.header and data.header["content-type"] and (" (%s)."):format( data.header["content-type"] ) ) or ".")
  84.   end
  85.  
  86.   return (redir and ("%s\n%s"):format( result, redir )) or result
  87.  
  88. end
  89.  
  90.  
  91. -- Check if the given URL is okay to redirect to. Return a table with keys
  92. -- "host", "port", and "path" if okay, nil otherwise.
  93. function redirect_ok(url, host, port)
  94.   -- A battery of tests a URL is subjected to in order to decide if it may be
  95.   -- redirected to. They incrementally fill in loc.host, loc.port, and loc.path.
  96.   local rules = {
  97.     function (loc, url, host, port)
  98.       -- bail if userinfo is present
  99.       return ( url.userinfo and false ) or true
  100.     end,
  101.  
  102.     function (loc, url, host, port)
  103.       -- if present, url.host must be the same scanned target
  104.       -- loc.host must be set if returning true
  105.       if not url.host then
  106.         loc.host = ( host.targetname or host.ip )
  107.         return true
  108.       end
  109.       if url.host and
  110.       url.host == host.ip or
  111.       url.host == host.targetname or
  112.       url.host == ( host.name ~= '' and host.name ) or
  113.       is_vhost( url.host, host ) then -- dns lookup as last resort
  114.         loc.host = url.host
  115.         return true
  116.       end
  117.       return false
  118.     end,
  119.  
  120.     function (loc, url, host, port)
  121.       -- if present, url.port must be the same as the scanned port
  122.       -- loc.port must be set if returning true
  123.       if (not url.port) or tonumber(url.port) == port.number then
  124.         loc.port = port
  125.         return true
  126.       end
  127.       return false
  128.     end,
  129.  
  130.     function (loc, url, host, port)
  131.       -- if url.scheme is present then it must match the scanned port
  132.       if url.scheme and url.port then return true end
  133.       if url.scheme and url.scheme ~= port.service then return false end
  134.       return true
  135.     end,
  136.  
  137.     function (loc, url, host, port)
  138.       -- path cannot be unchanged unless host has changed
  139.       -- loc.path must be set if returning true
  140.       if ( not url.path or url.path == "/" ) and url.host == ( host.targetname or host.ip) then return false end
  141.       if not url.path then loc.path = "/"; return true end
  142.       loc.path = ( ( url.path:sub(1,1) == "/" and "" ) or "/" ) .. url.path -- ensuring leading slash
  143.       return true
  144.     end,
  145.  
  146.     function (loc, url, host, port)
  147.       -- always true - jut add the query to loc.path
  148.       if url.query then loc.path = ("%s?%s"):format( loc.path, url.query ) end
  149.       return true
  150.     end
  151.   }
  152.  
  153.   local loc = {}
  154.   for i, rule in ipairs( rules ) do
  155.     if not rule( loc, url, host, port ) then return nil end
  156.   end
  157.  
  158.   if loc.host and loc.port and loc.path then
  159.     return loc
  160.   else
  161.     return nil
  162.   end
  163. end
  164.  
  165. function is_vhost( rhost, host )
  166.  
  167.   -- query is sane?
  168.   if rhost:match( ":" ) or rhost:match( "^[%d%.]+$" ) then
  169.     return false
  170.   end
  171.  
  172.   local opts = {}
  173.   opts.dtype = "A"
  174.   opts.retAll = true
  175.   if host.ip:match( ":" ) then opts.dtype = "AAAA" end
  176.  
  177.   local answer, msg = dns.query( rhost, opts )
  178.  
  179.   if not answer then
  180.     stdnse.print_debug( "html-title.nse: DNS query failed for target %s.  Query was: %s. Error Code: %s", host.targetname or host.ip, rhost, msg or "nil" )
  181.     return false
  182.   end
  183.  
  184.   for i, ip_rec in ipairs( answer ) do
  185.     if ipOps.compare_ip( ip_rec, "eq", host.ip ) then
  186.       return true
  187.     end
  188.   end
  189.  
  190.   return false
  191. end
  192.