Nmap Development mailing list archives

[PATCH] showHTMLTitle.nse - bugfix and a few improvements


From: jah <jah () zadkiel plus com>
Date: Wed, 24 Sep 2008 02:38:52 +0100

Hi nmappers,

The last time I sent a patch for showHTMLTitle I introduced the ability
to follow a redirect from the default page.  If the server sends a
location header which is not an absolute URI (as per HTML 1.1 spec) a
print_debug statement fails, killing script execution.  My fault, I
should have seen this coming.  The attached fixes this by handling
relative URIs and making the script much more careful about handling the
location header.

Additionally there's a few minor improvements:

The script now only reports "Site doesn't have a title." when there's a
response containing some data and the data doesn't contain title tags or
contains an empty title tag.  The script will silently exit if it
doesn't get some data to play with, but will print some debug info.

The pattern to match the title tags is less greedy which means that,
should there be more than one title in a response (which happens quite
often, would you believe), it will return the first of them rather than
everything between the opening of the first and the closing of the last.

The pattern will also match title tags if they should have attributes of
some form.

One thing that I didn't do because I thought it was overkill (?) was to
use the dns library to check whether a hostname in a location header
resolves to the target IP when the hostname does not match either
host.targetname or host.name.  Instead, it prints debug info.

As a final note, a little gem of useless information: if the script
result displays a title surrounded by addition operators it is likely a
javascript variable - I haven't yet worked out the best way to get its
value.

Regards,

jah
--- showHTMLTitle.nse.orig      2008-08-31 23:57:37.515625000 +0100
+++ showHTMLTitle.nse   2008-09-24 02:25:57.188625000 +0100
@@ -14,8 +14,9 @@
 
 categories = {"default", "demo", "safe"}
 
-require 'http'
-require 'url'
+local url    = require 'url'
+local http   = require 'http'
+local stdnse = require 'stdnse'
 
 portrule = function(host, port)
        if not (port.service == 'http' or port.service == 'https') then
@@ -30,28 +31,48 @@
 end
 
 action = function(host, port)
+
        local data, result, title, protocol
 
        data = http.get( host, port, '/' )
-       -- follow ONE redirect if host is not some other host
-       if data.status == 301 or data.status == 302 then
+
+       -- check for a redirect
+       if type( data ) == "table" and type( data.status ) == "number" and tostring( data.status ):match( "30%d" ) and 
type( data.header ) == "table" and type( data.header.location ) == "string" then
                local url = url.parse( data.header.location )
-               if url.host == host.targetname or url.host == ( host.name ~= '' and host.name ) or url.host == host.ip 
then
-                       stdnse.print_debug("showHTMLTitle.nse: Default page is located at %s://%s%s", url.scheme, 
url.authority, url.path)
-                       data = http.get( host, port, url.path )
+               local loc
+               -- follow ONE redirect if host is not some other host.  Handle absolute Location: URI and 
non-(HTTP/1.1)-compliant relative ones too.
+               if type( url.host ) == "string" and url.host == host.targetname or url.host == ( host.name ~= '' and 
host.name ) or url.host == host.ip then
+                       loc = ( ( type( url.authority ) == "string" and ("%s://%s"):format( url.scheme or "http", 
url.authority ) ) or "" )
+                       .. ( ( type( url.path ) == "string" and url.path) or "/" )
+                       .. ( ( type( url.query ) == "string" and ("?%s"):format( url.query ) ) or "" )
+                       data = http.get_url( loc )
+               elseif type( url.host ) ~= "string" and type( url.path ) == "string" and url.path ~= "/" then
+                       loc = ( ( type( url.path ) == "string" and url.path) or "/" )
+                       .. ( ( type( url.query ) == "string" and ("?%s"):format( url.query ) ) or "" )
+                       data = http.get( host, port, loc )
                end
+               stdnse.print_debug("showHTMLTitle.nse: (%s) Default page %s", host.targetname or host.ip, ( type( loc ) 
== "string" and ("is located at %s."):format( loc ) ) or ("may be located at %s."):format( data.header.location ) )
+       end
+
+       if type( data.body ) == "string" and data.body ~= "" then
+               result = data.body
+       elseif type( data.status ) ~= "number" and not next( data.header ) and ( type( data.body ) ~= "string" or 
data.body == "" ) then
+               stdnse.print_debug( "showHTMLTitle.nse: %s did not respond with an HTTP header or any data.", 
host.targetname or host.ip )
+               return nil
+       else
+               stdnse.print_debug( "showHTMLTitle.nse: %s did not respond with any data.", host.targetname or host.ip )
+               return nil
        end
-       result = data.body
 
        -- watch out, this doesn't really work for all html tags
        result = string.gsub(result, "<(/?%a+)>", function(c) return "<" .. string.lower(c) .. ">" end)
 
-       title = string.match(result, "<title>(.+)</title>")
+       title = string.match(result, "<title[^>]*>([^<]*)</title>")
 
-       if title ~= nil then
+       if type( title ) == "string" and title ~= "" then
                result = string.gsub(title , "[\n\r\t]", "")
                if string.len(title) > 65 then
-                       stdnse.print_debug("showHTMLTitle.nse: Title got truncated!");
+                       stdnse.print_debug("showHTMLTitle.nse: (%s) Title got truncated!", host.targetname or host.ip );
                        result = string.sub(result, 1, 62) .. "..."
                end
        else
@@ -59,5 +80,5 @@
        end
 
        return result
-end
 
+end

_______________________________________________
Sent through the nmap-dev mailing list
http://cgi.insecure.org/mailman/listinfo/nmap-dev
Archived at http://SecLists.Org

Current thread: