diff --git a/nselib/data/http-default-accounts-fingerprints.lua b/nselib/data/http-default-accounts-fingerprints.lua
index fba5211a7..1546599cd 100644
--- a/nselib/data/http-default-accounts-fingerprints.lua
+++ b/nselib/data/http-default-accounts-fingerprints.lua
@@ -13,7 +13,11 @@ local url = require "url"
---- * username - Default username
---- * password - Default password
-- * paths - Paths table containing the possible location of the target
+-- * target_check - Validation function of the target (optional)
-- * login_check - Login function of the target
+--
+-- TODO: Update the functionality of target_check to differentiate
+-- between valid HTTP/200 and a custom error page.
---
---
@@ -59,6 +63,18 @@ local function try_http_post_login(host, port, path, target, failstr, params, fo
end
return false
end
+
+---
+-- Returns authentication realm advertised in an HTTP response
+-- @param response HTTP response object, such as a result from http.get()
+-- @return realm found in response header WWW-Authenticate
+-- (or nil if not present)
+---
+local function http_auth_realm(response)
+ local auth = response.header["www-authenticate"] or ""
+ return auth:match('%srealm="([^"]*)')
+end
+
fingerprints = {}
---
@@ -70,6 +86,9 @@ table.insert(fingerprints, {
paths = {
{path = "/cacti/"}
},
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ end,
login_combos = {
{username = "admin", password = "admin"}
},
@@ -85,6 +104,9 @@ table.insert(fingerprints, {
{path = "/manager/html/"},
{path = "/tomcat/manager/html/"}
},
+ target_check = function (host, port, path, response)
+ return http_auth_realm(response) == "Tomcat Manager Application"
+ end,
login_combos = {
{username = "tomcat", password = "tomcat"},
{username = "admin", password = "admin"},
@@ -104,6 +126,9 @@ table.insert(fingerprints, {
paths = {
{path = "/axis2/axis2-admin/"}
},
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ end,
login_combos = {
{username = "admin", password = "axis2"}
},
@@ -120,6 +145,9 @@ table.insert(fingerprints, {
paths = {
{path = "/logo_t.gif"}
},
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ end,
login_combos = {
{username = "", password = ""}
},
@@ -129,7 +157,7 @@ table.insert(fingerprints, {
})
table.insert(fingerprints, {
- name = "Cisco 2811",
+ name = "Cisco IOS",
category = "routers",
paths = {
{path = "/exec/show/log/CR"},
@@ -137,6 +165,11 @@ table.insert(fingerprints, {
{path = "/level/15/exec/-"},
{path = "/level/15/"}
},
+ target_check = function (host, port, path, response)
+ local realm = http_auth_realm(response) or ""
+ -- Exact PCRE: "^level 15?( or view)? access$"
+ return realm:gsub("_"," "):find("^level 15? .*access$")
+ end,
login_combos = {
{username = "", password = ""},
{username = "cisco", password = "cisco"}
@@ -152,6 +185,9 @@ table.insert(fingerprints, {
paths = {
{path = "/StatusLan.htm"}
},
+ target_check = function (host, port, path, response)
+ return http_auth_realm(response) == "Linksys WAP200"
+ end,
login_combos = {
{username = "admin", password = "admin"}
},
@@ -166,6 +202,9 @@ table.insert(fingerprints, {
paths = {
{path = "/WPA_Preshared.asp"}
},
+ target_check = function (host, port, path, response)
+ return http_auth_realm(response) == "Linksys WAP55AG"
+ end,
login_combos = {
{username = "", password = "admin"}
},
@@ -174,6 +213,23 @@ table.insert(fingerprints, {
end
})
+table.insert(fingerprints, {
+ name = "Nortel VPN Router",
+ category = "routers",
+ paths = {
+ {path = "/manage/bdy_sys.htm"}
+ },
+ target_check = function (host, port, path, response)
+ return http_auth_realm(response) == "Management(1)"
+ end,
+ login_combos = {
+ {username = "admin", password = "setup"}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_basic_login(host, port, path, user, pass, false)
+ end
+})
+
---
--Digital recorders
---
@@ -183,6 +239,9 @@ table.insert(fingerprints, {
paths = {
{path = "/frmpages/index.html"}
},
+ target_check = function (host, port, path, response)
+ return http_auth_realm(response) == "WebPage Configuration"
+ end,
login_combos = {
{username = "dm", password = "web"}
},
diff --git a/scripts/http-default-accounts.nse b/scripts/http-default-accounts.nse
index 2f38d811b..323e5372e 100644
--- a/scripts/http-default-accounts.nse
+++ b/scripts/http-default-accounts.nse
@@ -28,6 +28,11 @@ Remember each fingerprint must have:
* paths - Paths table containing the possible location of the target
* login_check - Login function of the target
+In addition, a fingerprint may have:
+* target_check - Target validation function. If defined, it will be
+ called to validate the target before attempting
+ any logins.
+
Default fingerprint file: /nselib/data/http-default-accounts-fingerprints.lua
This script was based on http-enum.
]]
@@ -48,6 +53,10 @@ This script was based on http-enum.
-- Other useful arguments relevant to this script:
-- http.pipeline Sets max number of petitions in the same request.
-- http.useragent User agent for HTTP requests
+--
+-- Revision History
+-- 2013-08-13 nnposter
+-- * added support for target_check()
---
author = "Paulino Calderon"
@@ -109,6 +118,10 @@ local function validate_fingerprints(fingerprints)
-- Make sure they include the login function
if(type(fingerprint.login_check) ~= "function") then
return "Missing or invalid login_check function in entry #"..i
+ end
+ -- Make sure that the target validation is a function
+ if(fingerprint.target_check and type(fingerprint.target_check) ~= "function") then
+ return "Invalid target_check function in entry #"..i
end
-- Are they missing any fields?
if(fingerprint.category and type(fingerprint.category) ~= "string") then
@@ -269,7 +282,10 @@ action = function(host, port)
if (results[j] and not(credentials_found)) then
local path = basepath .. probe['path']
- if( http.page_exists(results[j], result_404, known_404, path, true) ) then
+ if http.page_exists(results[j], result_404, known_404, path, true)
+ and (not fingerprint.target_check
+ or fingerprint.target_check(host, port, path, results[j]))
+ then
for _, login_combo in ipairs(fingerprint.login_combos) do
stdnse.print_debug(2, "%s: Trying login combo -> %s:%s", SCRIPT_NAME, login_combo["username"], login_combo["password"])
--Check default credentials