[prev in list] [next in list] [prev in thread] [next in thread]
List: nmap-dev
Subject: [NSE] http matching library
From: Patrik Karlsson <patrik () cqure ! net>
Date: 2011-04-30 6:49:54
Message-ID: 8C48D434-DB81-496E-A48F-998EBE1218D7 () cqure ! net
[Download RAW message or body]
Given the recent increase in http/web scripts I thought I would put some work out on \
the list I did a while back. I started working on it right about the time Ron did his \
big overhaul of the http-enum script. [1] My idea was to implement what the http-enum \
script does today, but de-couple the probes and matches from each other. The response \
wasn't very positive at the time.
Anyway, I did some more work on it and ended up creating a http-match library which \
pretty much does regexp matching based on rules created on-the-fly or loaded from a \
file. There's a script called http-fp that implements the decoupled probe and match \
approach on something similar to http-enum. It does so by loading all the rules \
(probes and matches) from a file (nselib/data/urls.txt). Once the probes have run \
matchers are used to process the response. Each matcher don't necessarily have to run \
for each probe as they can be restricted by url or category. In addition to regexp's \
a match can contain Lua code that will be executed on the http response received from \
the server.
To be clear, I'm not suggesting we add this script, but seeing the increase of small \
scripts that do different types of matching lately, I think someone may find the \
library useful. Also, please consider the code for what it is (maybe something \
useful) as it is not as well documented as what I usually put out to the list, or as \
finished as I would like. In order to make some of the matches I needed I factored \
the cookie code out from http.lua into cookie.lua. I'm attaching this as well. To \
better understand how it all works, the http-fp script may be useful. In order to get \
it to run you need to drop the url.txt into nselib/data/urls.txt and copy both \
libraries (httpmatch.lua and cookie.lua) into nselib.
Here's a few sample matches in order to give you an idea of how it works:
-- Detect .NET applications
match { status="200", ['header.x-powered-by']="(ASP.NET)", \
['header.x-aspnet-version']="(.*)", type="framework", desc="#header.x-powered-by_1# \
#header.x-aspnet-version_1#" }
-- Output any cookies set by the application
match { status="200", ['header.set-cookie']="(.*)", type="cookie", \
desc="#header.set-cookie_1#" }
-- Detect WordPress
match { status="200", body="\<meta name=\"generator\" content=\"(WordPress.-)\"", \
type="app", desc="#body_1#" }
-- Output contents of robots file
match { path="^/robots.txt$", status="200", type="additional", desc=
function(r)
local tbl = stdnse.strsplit("\r?\n", r.body)
tbl.name = "Robots content"
return tbl
end
}
-- Check whether the session cookie is assigned as HttpOnly
match { status="200", type="debug", ['header.set-cookie']='.*', desc=
function(r)
local cookies = cookie.parse(r.header['set-cookie'] )
local result = {}
for _, cookie in ipairs(cookies) do
if ( cookie:isSessionCookie() and not( cookie:isHttpOnly() ) ) then
table.insert( result, ("OWASP-SM-002: Cookie (%s) is not set as \
HttpOnly"):format( cookie:getName() ) ) end
end
return result
end
}
-- Check if the cookie was assigned with the secure attribute
match { status="200", type="debug", ['header.set-cookie']='.*', desc=
function(r)
local cookies = cookie.parse(r.header['set-cookie'] )
local result = {}
for _, cookie in ipairs(cookies) do
if ( options.ssl and not( cookie:isSecure() ) ) then
table.insert( result, ("OWASP-SM-002: Cookie (%s) is not set as secure"):format( \
cookie:getName() ) ) end
end
return result
end
}
-- Calculate SHA1 hash
match { status="200", type="additional", desc = function(r) return "SHA1 hash: " .. \
select(2, bin.unpack("H20", openssl.sha1(r.body))) end}
//Patrik
[1] http://seclists.org/nmap-dev/2010/q4/112
["http-fp.nse" (http-fp.nse)]
description = [[
Fingerprint web applications
]]
author = "Patrik Karlsson"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"safe", "discovery"}
require 'shortport'
require 'http'
require 'url'
require 'httpmatch'
portrule = shortport.http
local function runProbesPL( host, port, probes )
local all, status = nil, true
for _, probe in ipairs( probes ) do
all = http.pipeline_add(probe.path, nil, ( all or probe ), probe.verb )
end
local responses = http.pipeline_go( host, port, all, nil )
if ( not(responses) ) then status = false end
return status, responses
end
local function runProbes( host, port, probes )
local responses = {}
for _, probe in ipairs( probes ) do
local retries = 3
local response
repeat
response = http.get(host, port, probe.path)
retries = retries - 1
until( response or retries == 0 )
if ( retries == 0 ) then return false, "Error" end
table.insert( responses, response )
end
return true, responses
end
local function matchResponses( responses, probe_items, match_items, match_types )
local matcher = httpmatch.Matcher:new( match_items, match_types )
for i=1, #responses do
matcher:matchHttpResponse( responses[i], probe_items[i] )
end
return matcher:formatOutput()
end
-- check if all urls return 401 authorization required
local function checkAllStatus( host, port, http_status )
local status = true
for i=1, 3 do
local rnd = select(2, bin.unpack("H20", openssl.sha1( openssl.rand_bytes(100) ) ) )
local response = http.get( host, port, "/url_test_" .. rnd )
if ( not( tostring(response.status):match(http_status) ) ) then status = false end
end
return status
end
local function checkAllRedir( host, port )
return checkAllStatus( host, port, "30.")
end
local function checkAllAuth( host, port )
return checkAllStatus( host, port, "401" )
end
local function checkAll200( host, port )
return checkAllStatus( host, port, "200")
end
action = function( host, port )
local urlfile = "nselib/data/urls.txt"
local category = stdnse.get_script_args('http-fp.category')
-- This obviously needs to be handled differently in the future
if ( checkAll200(host, port) ) then
return "All urls report 200 OK aborting ..."
end
local options = {
showredir = false,
debug = false,
reportauth = not( checkAllAuth( host, port ) ),
ssl = ( port.service == "https" ) and true or false
}
local helper = httpmatch.Helper:new()
helper:setOptions(options)
helper:setCategory(category)
local status, fp = helper:loadFpFile( urlfile )
if ( not(status) ) then return fp end
local status, responses = runProbesPL( host, port, fp.probes )
if ( not(status) ) then
stdnse.print_debug("Pipelined requests failed")
return
end
local results = matchResponses( responses, fp.probes, fp.matches, fp.matchtypes ) or {}
return stdnse.format_output(true, results)
end
["cookie.lua" (cookie.lua)]
module(... or "cookie", package.seeall)
Cookie = {
new = function( self, cookie )
local o = {}
setmetatable(o, self)
for k,v in pairs(cookie) do self[k] = v end
self.__index = self
return o
end,
isSessionCookie = function( self )
return not(self['expires'])
end,
getName = function( self ) return self['name'] end,
isHttpOnly = function( self )
return self['HttpOnly'] and true or false
end,
isSecure = function( self )
return self['secure'] and true or false
end,
}
-- Parsing of specific headers. skip_space and the read_* functions return the
-- byte index following whatever they have just read, or nil on error.
-- Skip whitespace (that has already been folded from LWS). See RFC 2616,
-- section 2.2, definition of LWS.
local skip_space = function(s, pos)
local _
_, pos = string.find(s, "^[ \t]*", pos)
return pos + 1
end
-- Get a token starting at offset. See RFC 2616, section 2.2.
-- @return the first index following the token, or nil if no token was found.
-- @return the token.
local function get_token(s, offset)
-- All characters except CTL and separators.
local _, i, token = s:find("^([^()<>@,;:\\\"/%[%]?={} %z\001-\031\127]+)", offset)
if i then
return i + 1, token
else
return nil
end
end
-- See RFC 2616, section 2.2.
local read_token = function(s, pos)
local _, token
pos = skip_space(s, pos)
-- 1*<any CHAR except CTLs or separators>. CHAR is only byte values 0-127.
_, pos, token = string.find(s, "^([^%z\001-\031()<>@,;:\\\"/?={} \t%[%]\127-\255]+)", pos)
if token then
return pos + 1, token
else
return nil
end
end
-- See RFC 2616, section 2.2. Here we relax the restriction that TEXT may not
-- contain CTLs.
local read_quoted_string = function(s, pos)
local chars = {}
if string.sub(s, pos, pos) ~= "\"" then
return nil
end
pos = pos + 1
pos = skip_space(s, pos)
while pos <= string.len(s) and string.sub(s, pos, pos) ~= "\"" do
local c
c = string.sub(s, pos, pos)
if c == "\\" then
if pos < string.len(s) then
pos = pos + 1
c = string.sub(s, pos, pos)
else
return nil
end
end
chars[#chars + 1] = c
pos = pos + 1
end
if pos > string.len(s) or string.sub(s, pos, pos) ~= "\"" then
return nil
end
return pos + 1, table.concat(chars)
end
local read_token_or_quoted_string = function(s, pos)
pos = skip_space(s, pos)
if string.sub(s, pos, pos) == "\"" then
return read_quoted_string(s, pos)
else
return read_token(s, pos)
end
end
parse = function( s )
local cookies
local name, value
local _, pos
cookies = {}
pos = 1
while true do
local cookie = {}
-- Get the NAME=VALUE part.
pos = skip_space(s, pos)
pos, cookie.name = get_token(s, pos)
if not cookie.name then
return nil, "Can't get cookie name."
end
pos = skip_space(s, pos)
if pos > #s or string.sub(s, pos, pos) ~= "=" then
return nil, string.format("Expected '=' after cookie name \"%s\".", cookie.name)
end
pos = pos + 1
pos = skip_space(s, pos)
if string.sub(s, pos, pos) == "\"" then
pos, cookie.value = get_quoted_string(s, pos)
else
_, pos, cookie.value = string.find(s, "([^;]*)[ \t]*", pos)
pos = pos + 1
end
if not cookie.value then
return nil, string.format("Can't get value of cookie named \"%s\".", cookie.name)
end
pos = skip_space(s, pos)
-- Loop over the attributes.
while pos <= #s and string.sub(s, pos, pos) == ";" do
pos = pos + 1
pos = skip_space(s, pos)
pos, name = get_token(s, pos)
if not name then
return nil, string.format("Can't get attribute name of cookie \"%s\".", cookie.name)
end
pos = skip_space(s, pos)
if pos <= #s and string.sub(s, pos, pos) == "=" then
pos = pos + 1
pos = skip_space(s, pos)
if string.sub(s, pos, pos) == "\"" then
pos, value = get_quoted_string(s, pos)
else
if string.lower(name) == "expires" then
-- For version 0 cookies we must allow one comma for "expires".
_, pos, value = string.find(s, "([^,]*,[^;,]*)[ \t]*", pos)
else
_, pos, value = string.find(s, "([^;,]*)[ \t]*", pos)
end
pos = pos + 1
end
if not value then
return nil, string.format("Can't get value of cookie attribute \"%s\".", name)
end
else
value = true
end
cookie[name] = value
pos = skip_space(s, pos)
end
cookies[#cookies + 1] = Cookie:new( cookie )
if pos > #s then
break
end
if string.sub(s, pos, pos) ~= "," then
return nil, string.format("Syntax error after cookie named \"%s\".", cookie.name)
end
pos = pos + 1
pos = skip_space(s, pos)
end
return cookies
end
["httpmatch.lua" (httpmatch.lua)]
module(... or "httpmatch", package.seeall)
require "cookie"
Fp = {
new = function( self, probes, matches, matchtypes )
local o = {}
setmetatable(o, self)
o.probes, o.matches, o.matchtypes = probes, matches, matchtypes
self.__index = self
return o
end,
}
Matcher = {
new = function( self, match_items, match_types )
local o = {}
setmetatable(o, self)
o.match_items, o.match_types = match_items, match_types
o.results = {}
self.__index = self
return o
end,
matchHttpResponse = function( self, response, probe )
local probe = probe or { path="/" }
local result = self.results[probe.path]
local mt_by_type = Util.matchtypesByType( self.match_types )
-- Check responses against all matchlines
for _, match in ipairs( self.match_items ) do
local match_groups = {}
local matched
-- Iterate over all match keys and value
for match_key, match_value in pairs( match ) do
-- handle hiearchical match keys eg. header.server
local x = stdnse.strsplit( "%.", match_key )
local response_value = response
for j=1, #x do response_value = response_value[x[j]] end
-- desc and type are not supposed to be matched so don't check them
-- first attempt to find the match key in the response
-- if this fails fallback to the probe (ie. path, category, etc.)
if ( match_key:upper() ~= "DESC" and match_key:upper() ~= "TYPE" ) then
match_groups[match_key] = { tostring(response_value):match(match_value) }
response_value = response_value and tostring(response_value)
-- fallback to probe
if ( not(response_value) ) then
response_value = probe[match_key] and tostring(probe[match_key])
end
if ( response_value and response_value:match(tostring(match_value)) ) then
matched = true
else
-- if ( not(response_value_uc) ) then
-- stdnse.print_debug("Failed to find match_key %s", match_key )
-- end
matched = false
break
end
end
end
if ( matched ) then
local desc = match.desc
-- if the description is a text, attempt to do some match group replacing
if ( "string" == type(desc) ) then
for w in desc:gmatch("#.-_%d-#") do
local key, no = w:match("#(.*)_(%d)#")
local repl_txt= match_groups[key][tonumber(no)]
if ( repl_txt ) then
local start, stop = desc:find( w, 1, true )
desc = desc:sub( 1, start - 1 ) .. repl_txt .. desc:sub( stop + 1 )
end
end
-- if the desc is a Lua function, run it and collect the response
elseif ( "function" == type(desc) ) then
desc = desc(response)
end
result = result or {}
local multiple = ( mt_by_type[match.type or "unknown"] ) and \
mt_by_type[match.type or "unknown"].multiple or false
if ( not( match.type ) ) then
stdnse.print_debug("ERROR: match (%s) has no type", match.desc )
elseif ( multiple or not( result[match.type] ) ) then
result[match.type] = result[match.type] or {}
table.insert( result[match.type], desc )
end
end
end
if ( result ) then
self.results[probe.path] = result
return true
end
return false
end,
formatOutput = function( self )
-- add all path related data
local output = {}
for path, match in pairs(self.results) do
local result_part = {}
for _, t in pairs( self.match_types ) do
for k, v in pairs(match) do
if ( k == t.type ) then
if ( #v > 1 or "table" == type(v[1]) ) then
table.insert(result_part, { name=t.desc or ("Unknown match type \
(%s)"):format(k), v } ) else
table.insert(result_part, ("%s: %s"):format(t.desc, v[1] or ""))
end
end
end
end
result_part.name = ("path = %s"):format(path)
table.insert( output, result_part )
end
return output
end,
}
Util = {
matchtypesByType = function( mt )
local tbl = {}
for _, v in ipairs(mt) do tbl[v.type] = v end
return tbl
end,
}
Helper = {
new = function( self )
local o = {}
setmetatable(o, self)
self.__index = self
return o
end,
--- Sets a category filter
-- When set, only probes matching the name of the category will be loaded.
--
-- @category string containing the category name
setCategory = function( self, category )
self.category = category
end,
--- Sets any options that can be used by probes or matches
--
-- @param options table containing any number of options
setOptions = function( self, options )
self.options = options
end,
--- Loads a finger print file
-- The file can contain "probe", "match" and "matchtype" definitions
--
-- @param filename the name of the file to load
-- @return status true on success, false on failure
-- @return Fp instance containing probes, matches and matchtypes
-- err string containing error if status is false
loadFpFile = function( self, filename )
local file, err = loadfile(filename)
if ( not(file) ) then
return false, ("ERROR: Failed to load fingerprints:\n%s"):format(err)
end
local probes, matches, matchtypes = {}, {}, {}
setfenv(file, setmetatable({
options = self.options or {};
probe = function(p)
if ( not(self.category) or self.category == p.category ) then
if ( p.suffix ) then
local suffixes = stdnse.strsplit(",", p.suffix )
local path = p.path
for _, s in ipairs(suffixes) do
local new_p = {}
for k, v in pairs( p ) do new_p[k] = v end
new_p.path = path .. "." .. s
table.insert( probes, new_p )
end
else
table.insert(probes, p)
end
end
end;
match = function(m) table.insert(matches, m) end;
matchtype = function(mt) table.insert(matchtypes, mt) end;
}, {__index = _G}))
file()
return true, Fp:new( probes, matches, matchtypes )
end,
}
["urls.txt" (urls.txt)]
-- This is a comment
-- The purpose of the matchtypes is to group information from matches
-- The desc attribute contains the topic under which the information is presented
-- The matcher stops matching if a match is made for a specific type, unless the \
multiple attribute is set to true. matchtype { type="dev", desc="Device" }
matchtype { type="app", desc="Application" }
matchtype { type="ver", desc="Version" }
matchtype { type="framework", desc="Framework" }
matchtype { type="cookie", desc="Cookie" }
matchtype { type="auth", desc="Authentication" }
matchtype { type="additional", desc="Additional information", multiple=true}
matchtype { type="debug", desc="Debug information", multiple=true}
-- A probe consists of a mandatory path attribute
-- The verb attribute is optional and specifies what HTTP verb is supposed to be used
-- The category attribute groups probes into categories which can be used in targeted \
scans
-- General probes
probe { category="admin", path="/admin/", verb="GET" }
probe { path="/", verb="GET" }
-- Directory guesses
probe { path="/demo/" }
probe { path="/services/" }
probe { path="/backup/" }
probe { path="/test/" }
probe { path="/old/" }
probe { path="/new/" }
probe { path="/pics/" }
probe { path="/images/" }
probe { path="/includes/" }
probe { path="/menu/" }
-- Java appserver related folders
probe { path="/WEB-INF/"}
probe { path="/WEB-INF/web.xml"}
-- File guesses
probe { path="/test", suffix="asp,aspx,cf,php,jsp,exe,com,do,html" }
probe { path="/index", suffix="asp,aspx,cf,php,jsp,exe,com,do,html" }
probe { path="/admin", suffix="asp,aspx,cf,php,jsp,exe,com,do,html" }
probe { path="/default", suffix="asp,aspx,cf,php,jsp,exe,com,do,html"}
-- Guess for users when public_html is enabled
probe { path="/~root/" }
probe { path="/~apache/" }
probe { path="/~httpd/" }
-- Mail orientedprobes
probe { category="mail", path="/mail/", verb="GET"}
probe { category="mail", path="/webmail/", verb="GET" }
probe { category="mail", path="/squirrelmail/", verb="GET" }
probe { category="wiki", path="/mediawiki/" }
-- Wordpress orientedprobes
probe{ category="blog", path="/wordpress/", verb="GET" }
probe{ category="blog", path="/wp/", verb="GET" }
-- Frontpage orientedprobes
probe { category="frontpage", path="/_vti_bin/", verb="GET" }
probe { category="frontpage", path="/_vti_cnf/", verb="GET" }
probe { category="frontpage", path="/_vti_log/", verb="GET" }
probe { category="frontpage", path="/_vti_pvt/", verb="GET" }
probe { category="frontpage", path="/_vti_txt/", verb="GET" }
-- Outlook Web Access
probe { category="mail", path="/CookieAuth.dll?GetLogon?curl=/&reason=0&formdir=1", \
verb="GET"} probe { category="mail", path="/owa/auth/logon.aspx", verb="GET"}
-- SquirrelMail
probe { category="mail", path="/squirrelmail/src/login.php", verb="GET"}
-- Cisco IronPort
probe { category="mail", path="/websafe/help", verb="GET" }
-- Oracle
probe { category="oracle", path="/OA_HTML/AppsLocalLogin.jsp", verb="GET"}
-- Domino
probe { category="domino", path="/help/help8_admin.nsf/Main?OpenFrameSet", \
verb="GET"} probe { category="domino", path="/852566C90012664F" }
probe { category="domino", path="/AgentRunner.nsf" }
probe { category="domino", path="/DEASAppDesign.nsf" }
probe { category="domino", path="/DEASLog.nsf" }
probe { category="domino", path="/DEASLog01.nsf" }
probe { category="domino", path="/DEASLog02.nsf" }
probe { category="domino", path="/DEASLog03.nsf" }
probe { category="domino", path="/DEASLog04.nsf" }
probe { category="domino", path="/DEASLog05.nsf" }
probe { category="domino", path="/DEESAdmin.nsf" }
probe { category="domino", path="/a_domlog.nsf" }
probe { category="domino", path="/account.nsf" }
probe { category="domino", path="/accounts.nsf" }
probe { category="domino", path="/activity.nsf" }
probe { category="domino", path="/adm-bin/acls.exe" }
probe { category="domino", path="/adm-bin/alerts.exe" }
probe { category="domino", path="/adm-bin/console.exe" }
probe { category="domino", path="/adm-bin/listdb.exe" }
probe { category="domino", path="/adm-bin/webstats.exe" }
probe { category="domino", path="/admin.nsf" }
probe { category="domino", path="/admin4.nsf" }
probe { category="domino", path="/admin5.nsf" }
probe { category="domino", path="/adminadm0disk.nsf" }
probe { category="domino", path="/adminadm0plog.nsf" }
probe { category="domino", path="/agentrunner.nsf" }
probe { category="domino", path="/alog.nsf" }
probe { category="domino", path="/alog4.nsf" }
probe { category="domino", path="/archive/a_domlog.nsf" }
probe { category="domino", path="/archive/l_domlog.nsf" }
probe { category="domino", path="/billing.nsf" }
probe { category="domino", path="/bookmark.nsf" }
probe { category="domino", path="/bookmarks.nsf" }
probe { category="domino", path="/books.nsf" }
probe { category="domino", path="/busytime.nsf" }
probe { category="domino", path="/calendar.nsf" }
probe { category="domino", path="/catalog.nsf" }
probe { category="domino", path="/cersvr.nsf" }
probe { category="domino", path="/certa.nsf" }
probe { category="domino", path="/certlog.nsf" }
probe { category="domino", path="/certsrv.nsf " }
probe { category="domino", path="/certsrv.nsf" }
probe { category="domino", path="/chatlog.nsf" }
probe { category="domino", path="/clbusy.nsf" }
probe { category="domino", path="/cldbdir.nsf" }
probe { category="domino", path="/clusta4.nsf" }
probe { category="domino", path="/collect4.nsf" }
probe { category="domino", path="/cpa.nsf" }
probe { category="domino", path="/customerdata" }
probe { category="domino", path="/da.nsf" }
probe { category="domino", path="/database.nsf" }
probe { category="domino", path="/db.nsf" }
probe { category="domino", path="/dba4.nsf" }
probe { category="domino", path="/dbdirman.nsf" }
probe { category="domino", path="/dclf.nsf" }
probe { category="domino", path="/decsadm.nsf" }
probe { category="domino", path="/decslog.nsf" }
probe { category="domino", path="/default.nsf" }
probe { category="domino", path="/deslog.nsf" }
probe { category="domino", path="/diiop_ior.txt" }
probe { category="domino", path="/dirassist.nsf" }
probe { category="domino", path="/doc/dspug.nsf" }
probe { category="domino", path="/doc/helpadmn.nsf" }
probe { category="domino", path="/doc/javapg.nsf" }
probe { category="domino", path="/doc/readmec.nsf" }
probe { category="domino", path="/doladmin.nsf" }
probe { category="domino", path="/domadmin.nsf" }
probe { category="domino", path="/domcfg.nsf" }
probe { category="domino", path="/domguide.nsf" }
probe { category="domino", path="/domlog.nsf" }
probe { category="domino", path="/dspug.nsf" }
probe { category="domino", path="/event.nsf" }
probe { category="domino", path="/events.nsf" }
probe { category="domino", path="/events4.nsf" }
probe { category="domino", path="/events5.nsf" }
probe { category="domino", path="/group.nsf" }
probe { category="domino", path="/groups.nsf" }
probe { category="domino", path="/help/decsdoc.nsf" }
probe { category="domino", path="/help/decsdoc6.nsf" }
probe { category="domino", path="/help/dols_help.nsf" }
probe { category="domino", path="/help/help5_admin.nsf" }
probe { category="domino", path="/help/help5_client.nsf" }
probe { category="domino", path="/help/help5_designer.nsf" }
probe { category="domino", path="/help/help65_admin.nsf" }
probe { category="domino", path="/help/help65_client.nsf" }
probe { category="domino", path="/help/help65_designer.nsf" }
probe { category="domino", path="/help/lccon.nsf" }
probe { category="domino", path="/help/lccon6.nsf" }
probe { category="domino", path="/help/lsxlc.nsf" }
probe { category="domino", path="/help/lsxlc6.nsf" }
probe { category="domino", path="/help/readme.nsf" }
probe { category="domino", path="/help4.nsf" }
probe { category="domino", path="/helplt4.nsf" }
probe { category="domino", path="/hidden.nsf" }
probe { category="domino", path="/homepage.nsf" }
probe { category="domino", path="/iNotes/Forms5.nsf" }
probe { category="domino", path="/iNotes/Forms5.nsf/$DefaultNav" }
probe { category="domino", path="/iNotes/Forms6.nsf" }
probe { category="domino", path="/iNotes/help65_iwa_en.nsf" }
probe { category="domino", path="/iNotesForms5.nsf" }
probe { category="domino", path="/jotter.nsf" }
probe { category="domino", path="/l_domlog.nsf" }
probe { category="domino", path="/lccon.nsf" }
probe { category="domino", path="/ldap.nsf" }
probe { category="domino", path="/leiadm.nsf" }
probe { category="domino", path="/leilog.nsf" }
probe { category="domino", path="/leivlt.nsf" }
probe { category="domino", path="/lndfr.nsf" }
probe { category="domino", path="/log.nsf" }
probe { category="domino", path="/log4a.nsf" }
probe { category="domino", path="/loga4.nsf" }
probe { category="domino", path="/lsxlc.nsf" }
probe { category="domino", path="/mab.nsf" }
probe { category="domino", path="/mail.box" }
probe { category="domino", path="/mail/NOMBRE_USUARIO.nsf" }
probe { category="domino", path="/mail/admin.nsf" }
probe { category="domino", path="/mail/pxp.nsf" }
probe { category="domino", path="/mail1.box" }
probe { category="domino", path="/mail10.box" }
probe { category="domino", path="/mail2.box" }
probe { category="domino", path="/mail3.box" }
probe { category="domino", path="/mail4.box" }
probe { category="domino", path="/mail5.box" }
probe { category="domino", path="/mail6.box" }
probe { category="domino", path="/mail7.box" }
probe { category="domino", path="/mail8.box" }
probe { category="domino", path="/mail9.box" }
probe { category="domino", path="/mailw46.nsf" }
probe { category="domino", path="/msdwda.nsf" }
probe { category="domino", path="/mtatbls.nsf" }
probe { category="domino", path="/mtstore.nsf" }
probe { category="domino", path="/names.nsf" }
probe { category="domino", path="/nntp/nd000000.nsf" }
probe { category="domino", path="/nntp/nd000001.nsf" }
probe { category="domino", path="/nntp/nd000002.nsf" }
probe { category="domino", path="/nntp/nd000003.nsf" }
probe { category="domino", path="/nntp/nd000004.nsf" }
probe { category="domino", path="/nntppost.nsf" }
probe { category="domino", path="/notes.nsf" }
probe { category="domino", path="/ntsync4.nsf" }
probe { category="domino", path="/ntsync45.nsf" }
probe { category="domino", path="/patrol41.nsf" }
probe { category="domino", path="/perweb.nsf" }
probe { category="domino", path="/private.nsf" }
probe { category="domino", path="/proghelp/KBCCV11.NSF" }
probe { category="domino", path="/proghelp/KBNV11.NSF" }
probe { category="domino", path="/proghelp/KBSSV11.NSF" }
probe { category="domino", path="/public.nsf" }
probe { category="domino", path="/puserinfo.nsf" }
probe { category="domino", path="/qpadmin.nsf" }
probe { category="domino", path="/qstart.nsf" }
probe { category="domino", path="/quickplace/quickplace/main.nsf" }
probe { category="domino", path="/quickplacequickplacemain.nsf" }
probe { category="domino", path="/quickstart/qstart50.nsf" }
probe { category="domino", path="/quickstart/wwsample.nsf" }
probe { category="domino", path="/readme.nsf" }
probe { category="domino", path="/reports.nsf" }
probe { category="domino", path="/resource.nsf" }
probe { category="domino", path="/sample/faqw46.nsf" }
probe { category="domino", path="/sample/framew46.nsf" }
probe { category="domino", path="/sample/pagesw46.nsf" }
probe { category="domino", path="/sample/siregw46.nsf" }
probe { category="domino", path="/sample/site1w46.nsf" }
probe { category="domino", path="/sample/site2w46.nsf" }
probe { category="domino", path="/sample/site3w46.nsf" }
probe { category="domino", path="/schema.nsf" }
probe { category="domino", path="/schema50.nsf" }
probe { category="domino", path="/secret.nsf" }
probe { category="domino", path="/setup.nsf" }
probe { category="domino", path="/setupweb.nsf" }
probe { category="domino", path="/smbcfg.nsf" }
probe { category="domino", path="/smconf.nsf" }
probe { category="domino", path="/smency.nsf" }
probe { category="domino", path="/smhelp.nsf" }
probe { category="domino", path="/smmsg.nsf" }
probe { category="domino", path="/smquar.nsf" }
probe { category="domino", path="/smsolar.nsf" }
probe { category="domino", path="/smtime.nsf" }
probe { category="domino", path="/smtp.box" }
probe { category="domino", path="/smtp.nsf" }
probe { category="domino", path="/smtpibwq.nsf" }
probe { category="domino", path="/smtpobwq.nsf" }
probe { category="domino", path="/smtptbls.nsf" }
probe { category="domino", path="/smvlog.nsf" }
probe { category="domino", path="/software.nsf" }
probe { category="domino", path="/srvnam.htm" }
probe { category="domino", path="/srvnam.nsf" }
probe { category="domino", path="/statauths.nsf" }
probe { category="domino", path="/statautht.nsf" }
probe { category="domino", path="/statmail.nsf" }
probe { category="domino", path="/statrep.nsf" }
probe { category="domino", path="/stauths.nsf" }
probe { category="domino", path="/stautht.nsf" }
probe { category="domino", path="/stconf.nsf" }
probe { category="domino", path="/stconfig.nsf" }
probe { category="domino", path="/stdnaset.nsf" }
probe { category="domino", path="/stdomino.nsf" }
probe { category="domino", path="/stlog.nsf" }
probe { category="domino", path="/streg.nsf" }
probe { category="domino", path="/stsrc.nsf" }
probe { category="domino", path="/test.nsf" }
probe { category="domino", path="/userreg.nsf" }
probe { category="domino", path="/users.nsf" }
probe { category="domino", path="/vpuserinfo.nsf" }
probe { category="domino", path="/web.nsf" }
probe { category="domino", path="/webadmin.nsf" }
probe { category="domino", path="/welcome.nsf" }
-- Java Application Servers
-- JBoss
probe { category="java", path="/jmx-console/", verb="GET" }
-- Robots txt
probe { category="misc", path="/robots.txt", verb="GET" }
-- SSL VPN
-- Juniper
-- fixa så att followredir funkar som auth check biten
probe { category="vpn", path="/dana-na/"}
-- Citrix Access Gateway
probe { category="vpn", path="/vpn/index.html"}
probe { category="vpn", path="/Citrix/AccessPlatform/auth/login.aspx"}
probe { category="vpn", path="/Citrix/XenApp/auth/login.aspx"}
probe { category="misc", path="/icons/"}
probe { category="misc", path="/api/"}
-- Matches start here
-- The names of the fields in a match correspond to the fields in the http response
-- The desc field contains the message to print if the match is made and can contain \
a regexp match pattern from the other fields
-- * A regexp match pattern that is to be replaced in the desc field should contain \
the name of the field followed by an underscore and a number
-- * Eg. #status_1# - is replaced by the first match in the status field
-- The desc field may also contain a function which is to be executed if a match is \
done
-- * The function receives the response as the first parameter and should return a \
string as a result
-- Framework oriented matches
-- Detect .NET framework and version number
match { status="200", ['header.x-powered-by']="(ASP.NET)", \
['header.x-aspnet-version']="(.*)", type="framework", desc="#header.x-powered-by_1# \
#header.x-aspnet-version_1#" } match { status="200", ['header.x-powered-by']="(.*)", \
type="framework", desc="#header.x-powered-by_1#"} match { \
['header.set-cookie']="PHPSESSID", type="framework", desc="PHP framework" }
-- Application related matches
match { body="\<input.*type=.*password.*", type="auth", desc="Form based \
authentication"} match { status=(options.reportauth and "401" or -1), type="auth", \
desc="Authorization required (401)" }
-- 403 on folders should result in "Directory exists"
match { status="403", type="auth", desc="The request was forbidden (403)"}
match { status="403", type="additional", desc="Directory exists"}
-- Cookie match
match { status="200", ['header.set-cookie']="(.*)", type="cookie", \
desc="#header.set-cookie_1#" }
-- Horde related matches
match { ['header.set-cookie']="Horde\=(.*)\;", type="app", desc="Horde webmail" }
match { ['header.set-cookie']="Horde\=(.*)\;", type="app", desc="Horde Cookie: \
#header.set-cookie_1#" }
-- WordPress related matches
--match { path="/wp/", status="(.*)", desc="HTTP Status Code: #status_1#" }
match { status="200", body="WordPress", type="app", desc="WordPress" }
match { status="200", body="\<meta name=\"generator\" content=\"(WordPress.-)\"", \
type="app", desc="#body_1#" }
-- Joomla related matches
match { status="200", body="\<meta name=\"generator\" content=\"Joomla.*\"", \
type="app", desc="Joomla" }
-- Wiki related matches
match { status="200", body="\<meta name=\"generator\" content=\"(MediaWiki.*)\"", \
type="app", desc="#body_1#"}
-- Matchline for Bubba NAS
match { status="200", body="\<title\>Bubba\|2 %- Home %((.*)%)\<\/title\>", \
type="dev", desc="Excito Bubba|2 NAS" } match { status="200", body="\<title\>Bubba\|2 \
%- Home %((.*)%)\<\/title\>", type="additional", desc="NAS Name: #body_1#" }
-- Outlook Web Access
match { status="200", body="(Microsoft Exchange %- Outlook Web Access)", type="app", \
desc="#body_1#" }
-- Squirrelmail
match { status="200", body="squirrelmail_loginpage_onload", type="app", \
desc="Squirrelmail"} match { status="200", body="SquirrelMail version (.-)\<", \
type="ver", desc="#body_1#"}
-- Cisco IOS stuff
match { path="^/$", status="401", ['header.server']="cisco%-IOS", type="app", \
desc="Cisco IOS"} match { path="^/$", status="401", \
['header.www-authenticate']="level.*15.*access", type="app", desc="Cisco IOS"}
-- VPNs
-- Status could be 200 or 400 invalid path, so leave it empty
match { path="^/dana%-na/$", body="Copyright.*Juniper Networks", type="dev", \
desc="Juniper SSL VPN"}
-- Oracle
match { path="^/OA_HTML/AppsLocalLogin.jsp$", status="200", type="app", desc="Oracle \
E-Business Suite" }
-- IronPort
match { path="^/websafe/help$", status="200", body="IronPort WebSite", type="app", \
desc="IronPort Encryption Appliance"}
-- IBM Lotus Domino
match { category="domino", status="200", type="additional", desc="Found notes \
database" }
-- JBoss
match { category="java", status="401", ['header.www-authenticate']="JBoss JMX \
Console", type="app", desc="JBoss JMX Console"} match { category="java", \
status="401", body="(Apache Tomcat/[^%s]*)", type="ver", desc="#body_1#"} match { \
category="java", status="401", body="(JBoss Web/[^%s]*)", type="ver", \
desc="#body_1#"}
-- Match Citrix
match { status="200", \
body="\<[Tt][Ii][Tt][Ll][Ee]\>%s*(Citrix.*)\<\/[Tt][Ii][Tt][Ll][Ee]\>", type="app", \
desc="#body_1#"} match { status="200", body="\<[Tt][Ii][Tt][Ll][Ee]\>%s*(Citrix \
XenApp).*\<\/[Tt][Ii][Tt][Ll][Ee]\>", type="app", desc="#body_1#"}
match { status="200", body="[Ii]ndex of /.*", type="additional", desc="Directory \
Indexing"}
-- Calculate SHA1 hash
match { status="200", type="additional", desc = function(r) return "SHA1 hash: " .. \
select(2, bin.unpack("H20", openssl.sha1(r.body))) end}
-- robots contents
match { path="^/robots.txt$", status="200", type="additional", desc=
function(r)
local tbl = stdnse.strsplit("\r?\n", r.body)
tbl.name = "Robots content"
return tbl
end
}
-- Error related match lines
match { status="(5.*)", type="additional", desc="An error (#status_1#) occured"}
match { status=(options.showredir and "^30." or "-1"), ['header.location']="(.*)", \
type="additional", desc="Redirect to #header.location_1#"}
match { category="frontpage", status="200", type="additional", desc="FrontPage Server \
Extensions"}
-- debugging
match { status=(options.debug and "([23].*)" or "-1"), ['header.server']="(.*)", \
type="debug", desc= function(r)
local headers = {}
for k, v in pairs(r.header) do
table.insert(headers, ("%s: %s"):format( k, v ) )
end
headers.name = "HTTP Headers"
return headers
end
}
match { status=(options.debug and "([23].*)" or "-1"), ['header.server']="(.*)", \
type="debug", desc="Server information: #header.server_1#" } match { \
status=(options.debug and "([23].*)" or "-1"), ['header.date']="(.*)", type="debug", \
desc="Server Date: #header.date_1#" }
match { status="200", type="debug", ['header.set-cookie']='.*', desc=
function(r)
local cookies = cookie.parse(r.header['set-cookie'] )
local result = {}
for _, cookie in ipairs(cookies) do
if ( cookie:isSessionCookie() and not( cookie:isHttpOnly() ) ) then
table.insert( result, ("OWASP-SM-002: Cookie (%s) is not set as \
HttpOnly"):format( cookie:getName() ) ) end
end
return result
end
}
match { status="200", type="debug", ['header.set-cookie']='.*', desc=
function(r)
local cookies = cookie.parse(r.header['set-cookie'] )
local result = {}
for _, cookie in ipairs(cookies) do
if ( options.ssl and not( cookie:isSecure() ) ) then
table.insert( result, ("OWASP-SM-002: Cookie (%s) is not set as secure"):format( \
cookie:getName() ) ) end
end
return result
end
}
--
Patrik Karlsson
http://www.cqure.net
http://www.twitter.com/nevdull77
_______________________________________________
Sent through the nmap-dev mailing list
http://cgi.insecure.org/mailman/listinfo/nmap-dev
Archived at http://seclists.org/nmap-dev/
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic