1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-06 12:41:29 +00:00

[NSE] Added http-devframework (along with its fingerprints file) that tries to find out the technology behind the target website.

This commit is contained in:
sophron
2013-09-05 19:31:40 +00:00
parent ca3ceecbf3
commit 27f241e20f
2 changed files with 508 additions and 0 deletions

View File

@@ -0,0 +1,363 @@
local http = require "http"
local table = require "table"
local url = require "url"
---
-- http-devframework-fingerprints.lua
-- This file contains fingerprint data for http-devframework.nse
--
-- STRUCTURE:
-- * <code>name</code> - Descriptive name
-- * <code>rapidDetect</code> - Callback function that is called in the beginning
-- of detection process. It takes the host and port of the target website as
-- arguments.
-- * <code>consumingDetect</code> - Callback function that is called for each
-- spidered page. It takes the body of the response (HTML source code) and the
-- requested path as arguments.
---
tools = { Django = { rapidDetect = function(host, port)
-- Check if the site gives that familiar Django admin login page.
response = http.get(host, port, "/admin/")
if response.body then
if string.find(response.body, "Log in | Django site admin") or
string.find(response.body, "this_is_the_login_form") or
string.find(response.body, "csrfmiddlewaretoken") then
return "Django detected. Found Django admin login page on /admin/"
end
end
-- In Django, the cookie sessionid is being set when you log in
-- and forms will probably set a cookie called csrftoken.
if response.cookies then
for _, c in pairs(response.cookies) do
if c.name == "csrftoken" then
return "Django detected. Found sessionid cookie which means the contrib.auth package for authentication is enabled."
elseif c.name == "sessionid" then
return "Django detected. Found csrftoken cookie."
end
end
end
-- See if DEBUG mode still happens to be true.
response = http.get(host, port, "/random404page/")
if response.body then
if string.find(response.body, "<code>DEBUG = True</code>") then
return "Django detected. Found Django error page on /random404page/"
end
end
end,
consumingDetect = function(page, path)
if page then
if string.find(page, "csrfmiddlewaretoken") then
return "Django detected. Found csrfmiddlewaretoken on " .. path
end
if string.find(page, "id=\"id_") then
return "Django detected. Found id_ preffix in id attribute name on " .. path
end
if string.find(page, "%-TOTAL%-FORMS") or string.find(page, "%-DELETE") then
return "Django detected. Found -TOTAL-FORMS and -DELETE hidden inputs, which means there is a Django formset on " .. path
end
end
end
},
RubyOnRails = { rapidDetect = function(host, port)
response = http.get(host, port, "/")
-- Check for Mongrel or Passenger in the "Server" or "X-Powered-By" header
for h, v in pairs(response.header) do
if h == "x-powered-by" or h == "server" then
local vl = v:lower()
local m = vl:match("mongrel") or vl:match("passenger")
if m then
return "RoR detected. Found '" .. m .. "' in " .. h .. " header sent by the server."
end
end
end
-- /rails/info/propertires shows project info when in development mode
response = http.get(host, port, "/rails/info/properties")
if response.body then
if string.find(response.body, "Ruby version") then
return "RoR detected. Found properties file on /rails/info/properties/"
end
end
-- Make up a bad path and match the error page
response = http.get(host, port, "/random404page/")
if response.body then
if string.find(response.body, "Routing Error") then
return "RoR detected. Found RoR routing error page on /random404page/"
end
end
end,
consumingDetect = function(page, path)
-- Check the source and look for csrf patterns.
if page then
if string.find(page, "csrf%-param") or string.find(page, "csrf%-token") then
return "RoR detected. Found csrf field on" .. path
end
end
end
},
ASPdotNET = { rapidDetect = function(host, port)
response = http.get(host, port, "/")
-- Look for an ASP.NET header.
for h, v in pairs(response.header) do
vl = v:lower()
if h == "x-aspnet-version" or string.find(vl, "asp") then
return "ASP.NET detected. Found related header."
end
end
if response.cookies then
for _, c in pairs(response.cookies) do
if c.name == "aspnetsessionid" then
return "ASP.NET detected. Found aspnetsessionid cookie."
end
end
end
end,
consumingDetect = function(page, path)
-- Check the source and look for common traces.
if page then
if string.find(page, " __VIEWSTATE") or
string.find(page, "__EVENT") or
string.find(page, "__doPostBack") or
string.find(page, "aspnetForm") or
string.find(page, "ctl00_") then
return "ASP.NET detected. Found common traces on" .. path
end
end
end
},
CodeIgniter = { rapidDetect = function(host, port)
-- Match default error page.
response = http.get(host, port, "/random404page/")
if response.body then
if string.find(response.body, "#990000") and
string.find(response.body, "404 Page Not Found") then
return "CodeIgniter detected. Found CodeIgniter default error page on /random404page/"
end
end
end,
consumingDetect = function(page, path)
return
end
},
CakePHP = { rapidDetect = function(host, port)
-- Find CAKEPHP header.
response = http.get(host, port, "/")
for h, v in pairs(response.header) do
vl = v:lower()
if string.find(vl, "cakephp") then
return "CakePHP detected. Found related header."
end
end
end,
consumingDetect = function(page, path)
return
end
},
Symfony = { rapidDetect = function(host, port)
-- Find Symfony header.
response = http.get(host, port, "/")
for h, v in pairs(response.header) do
vl = v:lower()
if string.find(vl, "symfony") then
return "Symfony detected. Found related header."
end
end
end,
consumingDetect = function(page, path)
return
end
},
Wordpress = { rapidDetect = function(host, port)
-- Check for common traces in the source code.
response = http.get(host, port, "/")
if response.body then
if string.find(response.body, "content=[\"']WordPress") or
string.find(response.body, "wp%-content") then
return "Wordpress detected. Found common traces on /"
end
end
-- Check if the default login page exists.
response = http.get(host, port, "/wp%-login")
if response.status == "200" then
return "Wordpress detected. Found WP login page on /wp-login"
end
end,
consumingDetect = function(page, path)
if page then
if string.find(page, "content=[\"']WordPress") or
string.find(page, "wp-content") then
return "Wordpress detected. Found common traces on " .. page
end
end
end
},
Joomla = { rapidDetect = function(host, port)
-- Check for common traces in the source code.
response = http.get(host, port, "/")
if response.body then
if string.find(response.body, "content=[\"']Joomla!") then
return "Joomla detected. Found common traces on /"
end
end
-- Check if the default login page exists.
response = http.get(host, port, "/administrator")
if response.body and string.find(response.body, "Joomla") then
return "Joomla detected. Found Joomla login page on /administrator/"
end
end,
consumingDetect = function(page, path)
if page and string.find(page, "content=[\"']Joomla!") then
return "Joomla detected. Found common traces on " .. page
end
end
},
Drupal = { rapidDetect = function(host, port)
-- Check for common traces in the source code.
response = http.get(host, port, "/")
if response.body then
if string.find(response.body, "content=[\"']Drupal") then
return "Drupal detected. Found common traces on /"
end
end
end,
consumingDetect = function(page, path)
if page and string.find(page, "content=[\"']Drupal") then
return "Drupal detected. Found common traces on " .. page
end
end
},
MediaWiki = { rapidDetect = function(host, port)
-- Check for common traces in the source code.
response = http.get(host, port, "/")
if response.body then
if string.find(response.body, "content=[\"']MediaWiki") or
string.find(response.body, "/mediawiki/") then
return "MediaWiki detected. Found common traces on /"
end
end
end,
consumingDetect = function(page, path)
if page and string.find(page, "content=[\"']MediaWiki") or
string.find(page, "/mediawiki/") then
return "MediaWiki detected. Found common traces on " .. page
end
end
},
ColdFusion = { rapidDetect = function(host, port)
response = http.get(host, port, "/")
if response.cookies then
for _, c in pairs(response.cookies) do
if c.name == "cfid" or c.name == "cftoken" then
return "ColdFusion detected. Found " .. c.name .. " cookie."
end
end
end
end,
consumingDetect = function(page, path)
return
end
},
Broadvision = { rapidDetect = function(host, port)
response = http.get(host, port, "/")
if response.cookies then
for _, c in pairs(response.cookies) do
if string.find(c.name, "bv_") then
return "Broadvision detected. Found " .. c.name .. " cookie."
end
end
end
end,
consumingDetect = function(page, path)
return
end
},
WebSphereCommerce = { rapidDetect = function(host, port)
response = http.get(host, port, "/")
if response.cookies then
for _, c in pairs(response.cookies) do
if string.find(c.name, "wc_") then
return "WebSphere Commerce detected. Found " .. c.name .. " cookie."
end
end
end
end,
consumingDetect = function(page, path)
return
end
},
}

View File

@@ -0,0 +1,145 @@
description = [[
Tries to find out the technology behind the target website.
The script checks for certain defaults that might not have been changed, like
common headers or URLs or HTML content.
While the script does some guessing, note that overall there's no way to
determine what technologies a given site is using.
You can help improve this script by adding new entries to
nselib/data/http-tools-fingerprints.lua
Each entry must have:
* <code>rapidDetect</code> - Callback function that is called in the beginning
of detection process. It takes the host and port of target website as arguments.
* <code>consumingDetect</code> - Callback function that is called for each
spidered page. It takes the body of the response (HTML code) and the requested
path as arguments.
Note that the <code>consumingDetect</code> callback will not take place only if
<code>rapid</code> option is enabled.
]]
---
-- @usage nmap -p80 --script http-devframework.nse <target>
--
-- @args http-errors.rapid boolean value that determines if a rapid detection
-- should take place. The main difference of a rapid vs a lengthy detection
-- is that second one requires crawling through the website. Default: false
-- (lengthy detection is performed)
--
-- @output
-- PORT STATE SERVICE REASON
-- 80/tcp open http syn-ack
-- |_http-devframework: Django detected. Found Django admin login page on /admin/
---
categories = {"discovery", "intrusive"}
author = "George Chatzisofroniou"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
local http = require "http"
local shortport = require "shortport"
local stdnse = require "stdnse"
local table = require "table"
local string = require "string"
local httpspider = require "httpspider"
portrule = shortport.port_or_service( {80, 443}, {"http", "https"}, "tcp", "open")
local function loadFingerprints(filename, cat)
local file, fingerprints
-- Find the file
filename = nmap.fetchfile('nselib/data/' .. filename) or filename
-- Load the file
stdnse.print_debug(1, "%s: Loading fingerprints: %s", SCRIPT_NAME, filename)
local env = setmetatable({fingerprints = {}}, {__index = _G});
file = loadfile(filename, "t", env)
if( not(file) ) then
stdnse.print_debug(1, "%s: Couldn't load the file: %s", SCRIPT_NAME, filename)
return
end
file()
fingerprints = env.tools
return fingerprints
end
action = function(host, port)
local tools = stdnse.get_script_args("http-devframework.fingerprintfile") or loadFingerprints("nselib/data/http-devframework-fingerprints.lua")
local rapid = stdnse.get_script_args("http-devframework.rapid")
local d
-- Run rapidDetect() callbacks.
for f, method in pairs(tools) do
d = method["rapidDetect"](host, port)
if d then
return d
end
end
local crawler = httpspider.Crawler:new(host, port, '/', { scriptname = SCRIPT_NAME,
maxpagecount = 40,
maxdepth = -1,
withinhost = 1
})
if rapid then
return "Couldn't determine the underlying framework or CMS. Try turning off 'rapid' mode."
end
crawler.options.doscraping = function(url)
if crawler:iswithinhost(url)
and not crawler:isresource(url, "js")
and not crawler:isresource(url, "css") then
return true
end
end
crawler:set_timeout(10000)
while (true) do
local response, path
status, r = crawler:crawl()
-- if the crawler fails it can be due to a number of different reasons
-- most of them are "legitimate" and should not be reason to abort
if (not(status)) then
if (r.err) then
return stdnse.format_output(true, ("ERROR: %s"):format(r.reason))
else
break
end
end
response = r.response
path = tostring(r.url)
if (response.body) then
-- Run consumingDetect() callbacks.
for f, method in pairs(tools) do
d = method["consumingDetect"](response.body, path)
if d then
return d
end
end
end
return "Couldn't determine the underlying framework or CMS. Try increasing 'httpspider.maxpagecount' value to spider more pages."
end
end