About 2 weeks ago I finally started looking into Lua, a language that’s been on my radar for a while but that I never really got around to play with so far. Then I realized that the Nmap Scripting Engine uses Lua scripts, so I had an excuse to hack on this stuff for work. Here’s my first small nmap script, which will extract the contents of the generator meta tag if there is one. It’s nothing funky, but it shows how easy it is to write custom scripts for nmap thanks to the provided libraries like http and shortport.
description=[[Displays the contents of the "generator" meta tag if there is one.]]author="Michael Kohl"license="Same as Nmap--See http://nmap.org/book/man-legal.html"categories={"discovery","safe"}-- documentation skippedrequire('http')require('shortport')portrule=shortport.httpaction=function(host,port)localresponse,loc,generatorresponse=http.get(host,port,'/')-- deal with redirectswhileresponse['status-line']:lower():match("^http/1.1 30[12]")doloc=response.header['location']response=http.get_url(loc)endforlineinresponse.body:gmatch("[^\r\n]+")dogenerator=line:match('<meta name="generator" content="(.*)" />')ifgeneratorthenreturngeneratorendendend
Update: Here’s the version that’s now part of nmap, it even made it to the default category.
description=[[Displays the contents of the "generator" meta tag if there is one.]]author="Michael Kohl"license="Same as Nmap--See http://nmap.org/book/man-legal.html"categories={"default","discovery","safe"}-- documentation skippedrequire('http')require('shortport')require('stdnse')-- helper functionlocalfollow_redirects=function(host,port,path,n)localpattern="^[hH][tT][tT][pP]/1.[01] 30[12]"localresponse=http.get(host,port,path)whileresponse['status-line']:match(pattern)andn>0don=n-1loc=response.header['location']response=http.get_url(loc)endreturnresponseendportrule=shortport.httpaction=function(host,port)localresponse,loc,generatorlocalpath=stdnse.get_script_args('http-generator.path')or'/'localredirects=tonumber(stdnse.get_script_args('http-generator.redirects'))or3-- Worst case: <meta name=Generator content="Microsoft Word 11">localpattern='<meta name="?generator"? content="([^\"]*)" ?/?>'-- make pattern case-insensitivepattern=pattern:gsub("%a",function(c)returnstring.format("[%s%s]",string.lower(c),string.upper(c))end)response=follow_redirects(host,port,path,redirects)returnresponse.body:match(pattern)end