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