1local http = require "http" 2local shortport = require "shortport" 3local stdnse = require "stdnse" 4local string = require "string" 5local ls = require "ls" 6local have_ssl, openssl = pcall(require,'openssl') 7 8description = [[ 9Shows the content of an "index" Web page. 10 11TODO: 12 - add support for more page formats 13]] 14 15author = "Pierre Lalet" 16license = "Same as Nmap--See https://nmap.org/book/man-legal.html" 17categories = {"default", "discovery", "safe"} 18 19--- 20-- @usage 21-- nmap -n -p 80 --script http-ls test-debit.free.fr 22-- 23-- @args http-ls.checksum compute a checksum for each listed file. Requires OpenSSL. 24-- (default: false) 25-- @args http-ls.url base URL path to use (default: /) 26-- 27-- @output 28-- PORT STATE SERVICE 29-- 80/tcp open http 30-- | http-ls: 31-- | Volume / 32-- | maxfiles limit reached (10) 33-- | SIZE TIME FILENAME 34-- | 524288 02-Oct-2013 18:26 512.rnd 35-- | 1048576 02-Oct-2013 18:26 1024.rnd 36-- | 2097152 02-Oct-2013 18:26 2048.rnd 37-- | 4194304 02-Oct-2013 18:26 4096.rnd 38-- | 8388608 02-Oct-2013 18:26 8192.rnd 39-- | 16777216 02-Oct-2013 18:26 16384.rnd 40-- | 33554432 02-Oct-2013 18:26 32768.rnd 41-- | 67108864 02-Oct-2013 18:26 65536.rnd 42-- | 1073741824 03-Oct-2013 16:46 1048576.rnd 43-- | 188 03-Oct-2013 17:15 README.html 44-- |_ 45-- 46-- @xmloutput 47-- <table key="volumes"> 48-- <table> 49-- <elem key="volume">/</elem> 50-- <table key="files"> 51-- <table> 52-- <elem key="size">524288</elem> 53-- <elem key="time">02-Oct-2013 18:26</elem> 54-- <elem key="filename">512.rnd</elem> 55-- </table> 56-- <table> 57-- <elem key="size">1048576</elem> 58-- <elem key="time">02-Oct-2013 18:26</elem> 59-- <elem key="filename">1024.rnd</elem> 60-- </table> 61-- <table> 62-- <elem key="size">2097152</elem> 63-- <elem key="time">02-Oct-2013 18:26</elem> 64-- <elem key="filename">2048.rnd</elem> 65-- </table> 66-- <table> 67-- <elem key="size">4194304</elem> 68-- <elem key="time">02-Oct-2013 18:26</elem> 69-- <elem key="filename">4096.rnd</elem> 70-- </table> 71-- <table> 72-- <elem key="size">8388608</elem> 73-- <elem key="time">02-Oct-2013 18:26</elem> 74-- <elem key="filename">8192.rnd</elem> 75-- </table> 76-- <table> 77-- <elem key="size">16777216</elem> 78-- <elem key="time">02-Oct-2013 18:26</elem> 79-- <elem key="filename">16384.rnd</elem> 80-- </table> 81-- <table> 82-- <elem key="size">33554432</elem> 83-- <elem key="time">02-Oct-2013 18:26</elem> 84-- <elem key="filename">32768.rnd</elem> 85-- </table> 86-- <table> 87-- <elem key="size">67108864</elem> 88-- <elem key="time">02-Oct-2013 18:26</elem> 89-- <elem key="filename">65536.rnd</elem> 90-- </table> 91-- <table> 92-- <elem key="size">1073741824</elem> 93-- <elem key="time">03-Oct-2013 16:46</elem> 94-- <elem key="filename">1048576.rnd</elem> 95-- </table> 96-- <table> 97-- <elem key="size">188</elem> 98-- <elem key="time">03-Oct-2013 17:15</elem> 99-- <elem key="filename">README.html</elem> 100-- </table> 101-- </table> 102-- <table key="info"> 103-- <elem>maxfiles limit reached (10)</elem> 104-- </table> 105-- </table> 106-- </table> 107-- <table key="total"> 108-- <elem key="files">10</elem> 109-- <elem key="bytes">1207435452</elem> 110-- </table> 111 112portrule = shortport.http 113 114local function isdir(fname, size) 115 -- we consider a file is (probably) a directory if its name 116 -- terminates with a '/' or if the string representing its size is 117 -- either empty or a single dash ('-'). 118 if string.sub(fname, -1, -1) == '/' then 119 return true 120 end 121 if size == '' or size == '-' then 122 return true 123 end 124 return false 125end 126 127local function list_files(host, port, url, output, maxdepth, basedir) 128 basedir = basedir or "" 129 130 local resp = http.get(host, port, url) 131 132 if resp.location or not resp.body then 133 return true 134 end 135 136 if not string.match(resp.body, "<[Tt][Ii][Tt][Ll][Ee][^>]*> *[Ii][Nn][Dd][Ee][Xx] +[Oo][Ff]") then 137 return true 138 end 139 140 local patterns = { 141 '<[Aa] [Hh][Rr][Ee][Ff]="([^"]+)">[^<]+</[Aa]></[Tt][Dd]><[Tt][Dd][^>]*> *([0-9]+-[A-Za-z0-9]+-[0-9]+ [0-9]+:[0-9]+) *</[Tt][Dd]><[Tt][Dd][^>]*> *([^<]+)</[Tt][Dd]>', 142 '<[Aa] [Hh][Rr][Ee][Ff]="([^"]+)">[^<]+</[Aa]> *([0-9]+-[A-Za-z0-9]+-[0-9]+ [0-9]+:[0-9]+) *([^ \r\n]+)', 143 } 144 for _, pattern in ipairs(patterns) do 145 for fname, date, size in string.gmatch(resp.body, pattern) do 146 local continue = true 147 local directory = isdir(fname, size) 148 if have_ssl and ls.config('checksum') and not directory then 149 local checksum = "" 150 local resp = http.get(host, port, url .. fname) 151 if not resp.location and resp.body then 152 checksum = stdnse.tohex(openssl.sha1(resp.body)) 153 end 154 continue = ls.add_file(output, {size, date, basedir .. fname, checksum}) 155 else 156 continue = ls.add_file(output, {size, date, basedir .. fname}) 157 end 158 if not continue then 159 return false 160 end 161 if directory then 162 if string.sub(fname, -1, -1) ~= "/" then fname = fname .. '/' end 163 continue = true 164 if maxdepth > 0 then 165 continue = list_files(host, port, url .. fname, output, maxdepth - 1, 166 basedir .. fname) 167 elseif maxdepth < 0 then 168 continue = list_files(host, port, url .. fname, output, -1, 169 basedir .. fname) 170 end 171 if not continue then 172 return false 173 end 174 end 175 end 176 end 177 return true 178end 179 180action = function(host, port) 181 local url = stdnse.get_script_args(SCRIPT_NAME .. '.url') or "/" 182 183 local output = ls.new_listing() 184 ls.new_vol(output, url, false) 185 local continue = list_files(host, port, url, output, ls.config('maxdepth')) 186 if not continue then 187 ls.report_info( 188 output, 189 string.format("maxfiles limit reached (%d)", ls.config('maxfiles'))) 190 end 191 ls.end_vol(output) 192 return ls.end_listing(output) 193end 194