diff --git a/CHANGELOG b/CHANGELOG index d417f617d..1cd6ec17c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ # Nmap Changelog ($Id$); -*-text-*- +o [NSE] Added script http-ls. Parses web server directory index pages with + optional recursion. [Pierre Lalet] + o [NSE] [GH#106] Added a new NSE module, ls.lua, for accumulating and outputting file and directory listings. The afp-ls, nfs-ls, and smb-ls scripts have been converted to use this module. [Pierre Lalet] diff --git a/scripts/http-ls.nse b/scripts/http-ls.nse new file mode 100644 index 000000000..17c36cc53 --- /dev/null +++ b/scripts/http-ls.nse @@ -0,0 +1,193 @@ +local http = require "http" +local shortport = require "shortport" +local stdnse = require "stdnse" +local string = require "string" +local openssl = require "openssl" +local ls = require "ls" + +description = [[ +Shows the content of an "index" Web page. + +TODO: + - add support for more page formats +]] + +author = "Pierre Lalet" +license = "Same as Nmap--See http://nmap.org/book/man-legal.html" +categories = {"default", "discovery", "safe"} + +--- +-- @usage +-- nmap -n -p 80 --script http-ls test-debit.free.fr +-- +-- @args http-ls.checksum compute a checksum for each listed file +-- (default: false) +-- @args http-ls.url base URL path to use (default: /) +-- +-- @output +-- PORT STATE SERVICE +-- 80/tcp open http +-- | http-ls: +-- | Volume / +-- | maxfiles limit reached (10) +-- | SIZE TIME FILENAME +-- | 524288 02-Oct-2013 18:26 512.rnd +-- | 1048576 02-Oct-2013 18:26 1024.rnd +-- | 2097152 02-Oct-2013 18:26 2048.rnd +-- | 4194304 02-Oct-2013 18:26 4096.rnd +-- | 8388608 02-Oct-2013 18:26 8192.rnd +-- | 16777216 02-Oct-2013 18:26 16384.rnd +-- | 33554432 02-Oct-2013 18:26 32768.rnd +-- | 67108864 02-Oct-2013 18:26 65536.rnd +-- | 1073741824 03-Oct-2013 16:46 1048576.rnd +-- | 188 03-Oct-2013 17:15 README.html +-- |_ +-- +-- @xmloutput +-- +--
+-- / +--
+--
+-- 524288 +-- 02-Oct-2013 18:26 +-- 512.rnd +--
+-- +-- 1048576 +-- 02-Oct-2013 18:26 +-- 1024.rnd +--
+-- +-- 2097152 +-- 02-Oct-2013 18:26 +-- 2048.rnd +--
+-- +-- 4194304 +-- 02-Oct-2013 18:26 +-- 4096.rnd +--
+-- +-- 8388608 +-- 02-Oct-2013 18:26 +-- 8192.rnd +--
+-- +-- 16777216 +-- 02-Oct-2013 18:26 +-- 16384.rnd +--
+-- +-- 33554432 +-- 02-Oct-2013 18:26 +-- 32768.rnd +--
+-- +-- 67108864 +-- 02-Oct-2013 18:26 +-- 65536.rnd +--
+-- +-- 1073741824 +-- 03-Oct-2013 16:46 +-- 1048576.rnd +--
+-- +-- 188 +-- 03-Oct-2013 17:15 +-- README.html +--
+-- +-- +-- maxfiles limit reached (10) +--
+-- +-- +-- +-- 10 +-- 1207435452 +--
+ +portrule = shortport.http + +local function isdir(fname, size) + -- we consider a file is (probably) a directory if its name + -- terminates with a '/' or if the string representing its size is + -- either empty or a single dash ('-'). + if string.sub(fname, -1, -1) == '/' then + return true + end + if size == '' or size == '-' then + return true + end + return false +end + +local function list_files(host, port, url, output, maxdepth, basedir) + basedir = basedir or "" + + local resp = http.get(host, port, url) + + if resp.location or not resp.body then + return true + end + + if not string.match(resp.body, "<[Tt][Ii][Tt][Ll][Ee][^>]*> *[Ii][Nn][Dd][Ee][Xx] +[Oo][Ff]") then + return true + end + + local patterns = { + '<[Aa] [Hh][Rr][Ee][Ff]="([^"]+)">[^<]+<[Tt][Dd][^>]*> *([0-9]+-[A-Za-z0-9]+-[0-9]+ [0-9]+:[0-9]+) *<[Tt][Dd][^>]*> *([^<]+)', + '<[Aa] [Hh][Rr][Ee][Ff]="([^"]+)">[^<]+ *([0-9]+-[A-Za-z0-9]+-[0-9]+ [0-9]+:[0-9]+) *([^ \n]+)', + } + for _, pattern in ipairs(patterns) do + for fname, date, size in string.gmatch(resp.body, pattern) do + local continue = true + local directory = isdir(fname, size) + if ls.config('checksum') and not directory then + local checksum = "" + local resp = http.get(host, port, url .. fname) + if not resp.location and resp.body then + checksum = stdnse.tohex(openssl.sha1(resp.body)) + end + continue = ls.add_file(output, {size, date, basedir .. fname, checksum}) + else + continue = ls.add_file(output, {size, date, basedir .. fname}) + end + if not continue then + return false + end + if directory then + if string.sub(fname, -1, -1) ~= "/" then fname = fname .. '/' end + continue = true + if maxdepth > 0 then + continue = list_files(host, port, url .. fname, output, maxdepth - 1, + basedir .. fname) + elseif maxdepth < 0 then + continue = list_files(host, port, url .. fname, output, -1, + basedir .. fname) + end + if not continue then + return false + end + end + end + end + return true +end + +action = function(host, port) + local url = stdnse.get_script_args(SCRIPT_NAME .. '.url') or "/" + + local output = ls.new_listing() + ls.new_vol(output, url, false) + local continue = list_files(host, port, url, output, ls.config('maxdepth')) + if not continue then + ls.report_info( + output, + string.format("maxfiles limit reached (%d)", ls.config('maxfiles'))) + end + ls.end_vol(output) + return ls.end_listing(output) +end diff --git a/scripts/script.db b/scripts/script.db index ba5ec94f2..3398774f2 100644 --- a/scripts/script.db +++ b/scripts/script.db @@ -161,8 +161,8 @@ Entry { filename = "http-devframework.nse", categories = { "discovery", "intrusi Entry { filename = "http-dlink-backdoor.nse", categories = { "exploit", "vuln", } } Entry { filename = "http-dombased-xss.nse", categories = { "exploit", "intrusive", "vuln", } } Entry { filename = "http-domino-enum-passwords.nse", categories = { "auth", "intrusive", } } -Entry { filename = "http-drupal-enum.nse", categories = { "discovery", "intrusive", } } Entry { filename = "http-drupal-enum-users.nse", categories = { "discovery", "intrusive", } } +Entry { filename = "http-drupal-enum.nse", categories = { "discovery", "intrusive", } } Entry { filename = "http-enum.nse", categories = { "discovery", "intrusive", "vuln", } } Entry { filename = "http-errors.nse", categories = { "discovery", "intrusive", } } Entry { filename = "http-exif-spider.nse", categories = { "intrusive", } } @@ -186,6 +186,7 @@ Entry { filename = "http-iis-short-name-brute.nse", categories = { "brute", "int Entry { filename = "http-iis-webdav-vuln.nse", categories = { "intrusive", "vuln", } } Entry { filename = "http-joomla-brute.nse", categories = { "brute", "intrusive", } } Entry { filename = "http-litespeed-sourcecode-download.nse", categories = { "exploit", "intrusive", "vuln", } } +Entry { filename = "http-ls.nse", categories = { "default", "discovery", "safe", } } Entry { filename = "http-majordomo2-dir-traversal.nse", categories = { "exploit", "intrusive", "vuln", } } Entry { filename = "http-malware-host.nse", categories = { "malware", "safe", } } Entry { filename = "http-method-tamper.nse", categories = { "auth", "vuln", } } @@ -495,7 +496,7 @@ Entry { filename = "whois-domain.nse", categories = { "discovery", "external", " Entry { filename = "whois-ip.nse", categories = { "discovery", "external", "safe", } } Entry { filename = "wsdd-discover.nse", categories = { "default", "discovery", "safe", } } Entry { filename = "x11-access.nse", categories = { "auth", "default", "safe", } } -Entry { filename = "xmlrpc-methods.nse", categories = { "default", "discovery", "safe", } } Entry { filename = "xdmcp-discover.nse", categories = { "discovery", "safe", } } +Entry { filename = "xmlrpc-methods.nse", categories = { "default", "discovery", "safe", } } Entry { filename = "xmpp-brute.nse", categories = { "brute", "intrusive", } } Entry { filename = "xmpp-info.nse", categories = { "default", "discovery", "safe", "version", } }