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

  1. description = [[
  2. A simple banner grabber which connects to an open TCP port and prints out anything sent by the listening service within five seconds.
  3.  
  4. The banner will be truncated to fit into a single line, but an extra line may be printed for every
  5. increase in the level of verbosity requested on the command line.
  6. ]]
  7.  
  8. ---
  9. -- @output
  10. -- 21/tcp open  ftp
  11. -- |_ banner: 220 FTP version 1.0\x0D\x0A
  12.  
  13.  
  14. author      = "jah <jah at zadkiel.plus.com>"
  15. license     = "See Nmap License: http://nmap.org/book/man-legal.html"
  16. runlevel    = 1
  17. categories  = {"discovery", "safe"}
  18.  
  19.  
  20.  
  21. local nmap   = require "nmap"
  22. local comm   = require "comm"
  23. local stdnse = require "stdnse"
  24.  
  25.  
  26. ---
  27. -- Script is executed for any TCP port.
  28. portrule = function( host, port )
  29.   return port.protocol == "tcp"
  30. end
  31.  
  32.  
  33. ---
  34. -- Grabs a banner and outputs it nicely formatted.
  35. action = function( host, port )
  36.  
  37.   local out = grab_banner(host, port)
  38.   return output( out )
  39.  
  40. end
  41.  
  42.  
  43.  
  44. ---
  45. -- Connects to the target on the given port and returns any data issued by a listening service.
  46. -- @param host  Host Table.
  47. -- @param port  Port Table.
  48. -- @return      String or nil if data was not received.
  49. function grab_banner(host, port)
  50.  
  51.   local opts = {}
  52.   opts.timeout = get_timeout()
  53.   opts.proto = port.protocol
  54.  
  55.   local status, response = comm.get_banner(host.ip, port.number, opts)
  56.  
  57.   if not status then
  58.     local errlvl = { ["EOF"]=3,["TIMEOUT"]=3,["ERROR"]=2 }
  59.     stdnse.print_debug(errlvl[response] or 1, "%s failed for %s on %s port %s. Message: %s",
  60.                filename, host.ip, port.protocol, port.number, response or "No Message." )
  61.     return nil
  62.   end
  63.  
  64.   return response:match("^%s*(.-)%s*$");
  65.  
  66. end
  67.  
  68.  
  69.  
  70. ---
  71. -- Returns a number of milliseconds for use as a socket timeout value (defaults to 5 seconds).
  72. --
  73. -- @return Number of milliseconds.
  74. function get_timeout()
  75.   return 5000
  76. end
  77.  
  78.  
  79.  
  80. ---
  81. -- Formats the banner for printing to the port script result.
  82. --
  83. -- Non-printable characters are hex encoded and the banner is
  84. -- then truncated to fit into the number of lines of output desired.
  85. -- @param out  String banner issued by a listening service.
  86. -- @return     String formatted for output.
  87. function output( out )
  88.  
  89.   if type(out) ~= "string" or out == "" then return nil end
  90.  
  91.   -- convert filename from full filepath to filename -extn
  92.   local filename = filename:match( "[\\/]([^\\/]+)\.nse$" )
  93.   local line_len = 75    -- The character width of command/shell prompt window.
  94.   local fline_offset = 5 -- number of chars excluding script id not available to the script on the first line
  95.  
  96.   -- number of chars available on the first line of output
  97.   -- we'll skip the first line of output if the filename is looong
  98.   local fline_len
  99.   if filename:len() < (line_len-fline_offset) then
  100.     fline_len = line_len -1 -filename:len() -fline_offset
  101.   else
  102.     fline_len = 0
  103.   end
  104.  
  105.   -- number of chars allowed on subsequent lines
  106.   local sline_len = line_len -1 -(fline_offset-2)
  107.  
  108.   -- total number of chars allowed for output (based on verbosity)
  109.   local total_out_chars
  110.   if fline_len > 0 then
  111.     total_out_chars = fline_len + (extra_output()*sline_len)
  112.   else
  113.     -- skipped the first line so we'll have an extra lines worth of chars
  114.     total_out_chars = (1+extra_output())*sline_len
  115.   end
  116.  
  117.   -- replace non-printable ascii chars - no need to do the whole string
  118.   out = replace_nonprint(out, 1+total_out_chars) -- 1 extra char so we can truncate below.
  119.  
  120.   -- truncate banner to total_out_chars ensuring we remove whole hex encoded chars
  121.   if out:len() > total_out_chars then
  122.     while out:len() > total_out_chars do
  123.       if (out:sub(-4,-1)):match("\\x%x%x") then
  124.         out = out:sub(1,-1-4)
  125.       else
  126.         out = out:sub(1,-1-1)
  127.       end
  128.     end
  129.     out = ("%s..."):format(out:sub(1,total_out_chars-3)) -- -3 for ellipsis
  130.   end
  131.  
  132.   -- break into lines - this will look awful if line_len is more than the actual space available on a line...
  133.   local ptr = fline_len
  134.   local t = {}
  135.   while true do
  136.     if out:len() >= ptr then
  137.       t[#t+1] = (ptr > 0 and out:sub(1,ptr)) or " "  -- single space if we skipped the first line
  138.       out = out:sub(ptr+1,-1)
  139.       ptr = sline_len
  140.     else
  141.       t[#t+1] = out
  142.       break
  143.     end
  144.   end
  145.  
  146.   return table.concat(t,"\n")
  147.  
  148. end
  149.  
  150.  
  151.  
  152. ---
  153. -- Replaces characters with ASCII values outside of the range of standard printable
  154. -- characters (decimal 32 to 126 inclusive) with hex encoded equivalents.
  155. --
  156. -- The second parameter dictates the number of characters to return, however, if the
  157. -- last character before the number is reached is one that needs replacing then up to
  158. -- three characters more than this number may be returned.
  159. -- If the second parameter is nil, no limit is applied to the number of characters
  160. -- that may be returned.
  161. -- @param s    String on which to perform substitutions.
  162. -- @param len  Number of characters to return.
  163. -- @return     String.
  164. function replace_nonprint( s, len )
  165.  
  166.   local t = {}
  167.   local count = 0
  168.  
  169.   for c in s:gmatch(".") do
  170.     if c:byte() < 32 or c:byte() > 126 then
  171.       t[#t+1] = ("\\x%s"):format( ("0%s"):format( ( (stdnse.tohex( c:byte() )):upper() ) ):sub(-2,-1) ) -- capiche
  172.       count = count+4
  173.     else
  174.       t[#t+1] = c
  175.       count = count+1
  176.     end
  177.     if type(len) == "number" and count >= len then break end
  178.   end
  179.  
  180.   return table.concat(t)
  181.  
  182. end
  183.  
  184.  
  185.  
  186. ---
  187. -- Returns a number for each level of verbosity specified on the command line.
  188. --
  189. -- Ignores level increases resulting from debugging level.
  190. -- @return Number
  191. function extra_output()
  192.   return (nmap.verbosity()-nmap.debugging()>0 and nmap.verbosity()-nmap.debugging()) or 0
  193. end
  194.