summaryrefslogtreecommitdiffstats
path: root/nselib/data/http-default-accounts-fingerprints.lua
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 07:42:04 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 07:42:04 +0000
commit0d47952611198ef6b1163f366dc03922d20b1475 (patch)
tree3d840a3b8c0daef0754707bfb9f5e873b6b1ac13 /nselib/data/http-default-accounts-fingerprints.lua
parentInitial commit. (diff)
downloadnmap-0d47952611198ef6b1163f366dc03922d20b1475.tar.xz
nmap-0d47952611198ef6b1163f366dc03922d20b1475.zip
Adding upstream version 7.94+git20230807.3be01efb1+dfsg.upstream/7.94+git20230807.3be01efb1+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'nselib/data/http-default-accounts-fingerprints.lua')
-rw-r--r--nselib/data/http-default-accounts-fingerprints.lua1738
1 files changed, 1738 insertions, 0 deletions
diff --git a/nselib/data/http-default-accounts-fingerprints.lua b/nselib/data/http-default-accounts-fingerprints.lua
new file mode 100644
index 0000000..c5a5030
--- /dev/null
+++ b/nselib/data/http-default-accounts-fingerprints.lua
@@ -0,0 +1,1738 @@
+local base64 = require "base64"
+local http = require "http"
+local json = require "json"
+local math = require "math"
+local shortport = require "shortport"
+local stdnse = require "stdnse"
+local table = require "table"
+local tableaux = require "tableaux"
+local url = require "url"
+local have_openssl, openssl = pcall(require, 'openssl')
+
+---
+-- http-default-accounts-fingerprints.lua
+-- This file contains fingerprint data for http-default-accounts.nse
+--
+-- STRUCTURE:
+-- * <code>name</code> - Descriptive name
+-- * <code>cpe</code> - Official CPE Dictionary entry (optional)
+-- * <code>category</code> - Category
+-- * <code>login_combos</code> - Table of default credential pairs
+---- * <code>username</code>
+---- * <code>password</code>
+-- * <code>paths</code> - Table of likely locations (paths) of the target
+-- * <code>target_check</code> - Validation function of the target (optional)
+-- * <code>login_check</code> - Login function of the target
+---
+
+---
+-- Requests given path using http.get() but disabling cache and redirects.
+-- @param host The host to connect to
+-- @param port The port to connect to
+-- @param path The path to retrieve
+-- @param options [optional] A table of HTTP request options
+-- @return A response table (see library http.lua for description)
+---
+local function http_get_simple (host, port, path, options)
+ local opts = tableaux.tcopy(options or {})
+ opts.bypass_cache = true
+ opts.no_cache = true
+ opts.redirect_ok = false
+ return http.get(host, port, path, opts)
+end
+
+---
+-- Requests given path using http.post() but disabling cache and redirects.
+-- (The current implementation of http.post() does not use either; this is
+-- a defensive wrapper to guard against future problems.)
+-- @param host The host to connect to
+-- @param port The port to connect to
+-- @param path The path to retrieve
+-- @param options [optional] A table of HTTP request options
+-- @param postdata A string or a table of data to be posted
+-- @return A response table (see library http.lua for description)
+---
+local function http_post_simple (host, port, path, options, postdata)
+ local opts = tableaux.tcopy(options or {})
+ opts.no_cache = true
+ opts.redirect_ok = false
+ return http.post(host, port, path, opts, nil, postdata)
+end
+
+---
+-- Requests given path using basic authentication.
+-- @param host Host table
+-- @param port Port table
+-- @param path Path to request
+-- @param user Username for Basic Auth
+-- @param pass Password for Basic Auth
+-- @param digest_auth Digest Authentication
+-- @return True if login in was successful
+---
+local function try_http_basic_login(host, port, path, user, pass, digest_auth)
+ local credentials = {username = user, password = pass, digest = digest_auth}
+ local resp = http_get_simple(host, port, path, {auth=credentials})
+ return resp.status
+ and resp.status ~= 400
+ and resp.status ~= 401
+ and resp.status ~= 403
+ and resp.status ~= 404
+end
+
+---
+-- Tries to login with a http post, if the FAIL string is not found
+-- we assume login in was successful
+-- @param host Host table
+-- @param port Port table
+-- @param target Target file
+-- @param failstr String shown when login in fails
+-- @param params Post parameters
+-- @param follow_redirects True if you want redirects to be followed
+-- @return True if login in was successful
+---
+local function try_http_post_login(host, port, path, target, failstr, params, follow_redirects)
+ local resp = http_post_simple(host, port, url.absolute(path, target), nil, params)
+ if not resp.status then return false end
+ local status = tonumber(resp.status) or 0
+ if follow_redirects and ( status > 300 and status < 400 ) then
+ resp = http_get_simple(host, port, url.absolute(path, resp.header.location))
+ end
+ if resp.status and resp.status ~= 404 and not(http.response_contains(resp, failstr)) then
+ return true
+ 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%s*=%s*"([^"]*)')
+end
+
+---
+-- Tests whether an HTTP response sets a named cookie with a given value
+-- @param response a standard HTTP response object
+-- @param name a case-insensitive cookie name that must be set
+-- @param pattern to validate the cookie value
+-- @return cookie value if such a cookie is found
+---
+local function sets_cookie(response, name, pattern)
+ name = name:lower()
+ for _, ck in ipairs(response.cookies or {}) do
+ if ck.name:lower() == name then
+ return (not pattern or ck.value:find(pattern)) and ck.value
+ end
+ end
+ return false
+end
+
+---
+-- Generates default scheme, host, and port components for a parsed URL.
+--
+-- This filter function generates the scheme, host, and port components from
+-- the standard <code>host</code> and <code>port</code> script objects. These
+-- components can then be passed onto function <code>url.build</code>.
+--
+-- As an example, the following code generates a URL for path "/test/"
+-- on the current host and port:
+-- <code>
+-- local testurl = url.build(url_build_defaults(host, port, {path = "/test/"}))
+-- </code>
+-- or, alternatively, when not used as a filter:
+-- <code>
+-- local parsed = url_build_defaults(host, port)
+-- parsed.path = "/test/"
+-- local testurl = url.build(parsed)
+-- </code>
+--
+-- @param host The host the URL is intended for.
+-- @param port The port the URL is intended for.
+-- @param parsed Parsed URL, as typically returned by <code>url.parse</code>,
+-- or nil. The table can be be missing the scheme, host, and port components.
+-- @return A clone of the parsed URL, with any missing scheme, host, and port
+-- components added.
+-- @see url.parse
+-- @see url.build
+---
+local function url_build_defaults (host, port, parsed)
+ local parts = tableaux.tcopy(parsed or {})
+ parts.host = parts.host or stdnse.get_hostname(host, port)
+ parts.scheme = parts.scheme or shortport.ssl(host, port) and "https" or "http"
+ if not parts.port and port.number ~= url.get_default_port(parts.scheme) then
+ parts.port = port.number
+ end
+ return parts
+end
+
+---
+-- Encodes a string to make it safe for embedding into XML/HTML.
+--
+-- @param s The string to be encoded.
+-- @return A string with unsafe characters encoded
+---
+local function xmlencode (s)
+ return s:gsub("%W", function (c) return ("&#x%x;"):format(c:byte()) end)
+end
+
+fingerprints = {}
+
+---
+--WEB
+---
+table.insert(fingerprints, {
+ -- Version 0.8.8a
+ name = "Cacti",
+ cpe = "cpe:/a:cacti:cacti",
+ category = "web",
+ paths = {
+ {path = "/"},
+ {path = "/cacti/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ and (sets_cookie(response, "Cacti") or sets_cookie(response, "CactiEZ"))
+ end,
+ login_combos = {
+ {username = "admin", password = "admin"}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_post_login(host, port, path, "index.php",
+ "%sname%s*=%s*(['\"]?)login_password%1[%s>]",
+ {action="login", login_username=user, login_password=pass})
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 2.0.6
+ name = "Zabbix",
+ cpe = "cpe:/a:zabbix:zabbix",
+ category = "web",
+ paths = {
+ {path = "/zabbix/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 200 and sets_cookie(response, "zbx_sessionid")
+ end,
+ login_combos = {
+ {username = "admin", password = "zabbix"}
+ },
+ login_check = function (host, port, path, user, pass)
+ local resp = http_post_simple(host, port, url.absolute(path, "index.php"), nil,
+ {request="", name=user, password=pass, enter="Sign in"})
+ return resp.status == 302 and resp.header["location"] == "dashboard.php"
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 0.7, 1.0.1
+ name = "Xplico",
+ category = "web",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 302 and sets_cookie(response, "Xplico")
+ end,
+ login_combos = {
+ {username = "admin", password = "xplico"},
+ {username = "xplico", password = "xplico"}
+ },
+ login_check = function (host, port, path, user, pass)
+ local lurl = url.absolute(path, "users/login")
+ -- harvest all hidden fields from the login form
+ local resp1 = http_get_simple(host, port, lurl)
+ if resp1.status ~= 200 then return false end
+ local html = (resp1.body or ""):match('<form%s+action%s*=%s*"[^"]*/users/login".->(.-)</form>')
+ if not html then return false end
+ local form = {}
+ for n, v in html:gmatch('<input%s+type%s*=%s*"hidden"%s+name%s*=%s*"(.-)"%s+value%s*=%s*"(.-)"') do
+ form[n] = v
+ end
+ -- add username and password to the form and submit it
+ form["data[User][username]"] = user
+ form["data[User][password]"] = pass
+ local resp2 = http_post_simple(host, port, lurl, {cookies=resp1.cookies}, form)
+ local loc = resp2.header["location"] or ""
+ return resp2.status == 302
+ and (loc:find("/admins$") or loc:find("/pols/index$"))
+ end
+})
+
+table.insert(fingerprints, {
+ --Version 5.3.1.1944 on EH6000
+ name = "ExtraHop Web UI",
+ category = "web",
+ paths = {
+ {path = "/extrahop/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ and response.body
+ and response.body:find("csrfmiddlewaretoken", 1, true)
+ and response.body:lower():find("<title>extrahop login", 1, true)
+ end,
+ login_combos = {
+ {username = "admin", password = "admin"}
+ },
+ login_check = function (host, port, path, user, pass)
+ -- obtain cookies and a CSRF token
+ local resp1 = http_get_simple(host, port, path)
+ if not (resp1.status == 200 and resp1.body) then return false end
+ local tname, tvalue = resp1.body:match("<input%s+type%s*=%s*'hidden'%s+name%s*=%s*'(csrfmiddlewaretoken)'%s+value%s*=%s*'(.-)'")
+ if not tname then return false end
+ local form = {[tname]=tvalue,
+ next=path,
+ username=user,
+ password=pass}
+ -- Referer header is mandatory
+ local header = {["Referer"]=url.build(url_build_defaults(host, port, {path=path}))}
+ local resp2 = http_post_simple(host, port, path,
+ {cookies=resp1.cookies, header=header}, form)
+ return resp2.status == 302
+ and (resp2.header["location"] or ""):find("/extrahop/$")
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 3.2.3, 3.4.4, 4.0.8
+ name = "Nagios",
+ cpe = "cpe:/a:nagios:nagios",
+ category = "web",
+ paths = {
+ {path = "/"},
+ {path = "/nagios/"}
+ },
+ target_check = function (host, port, path, response)
+ return http_auth_realm(response) == "Nagios Access"
+ end,
+ login_combos = {
+ {username = "nagiosadmin", password = "nagios"},
+ {username = "nagiosadmin", password = "nagiosadmin"},
+ -- IBM PurePower Integrated Manager
+ {username = "nagiosadmin", password = "PASSW0RD"},
+ -- CactiEZ
+ {username = "nagiosadmin", password = "CactiEZ"}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_basic_login(host, port, path, user, pass, false)
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 3.1.1, 4.0.2, 4.1.2
+ name = "Grafana",
+ category = "web",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 302 and sets_cookie(response, "grafana_sess")
+ end,
+ login_combos = {
+ {username = "admin", password = "admin"}
+ },
+ login_check = function (host, port, path, user, pass)
+ local header = {["Accept"] = "application/json, text/plain, */*",
+ ["Content-Type"] = "application/json;charset=utf-8"}
+ local jin = {user=user, email="", password=pass}
+ json.make_object(jin)
+ local resp = http_post_simple(host, port, url.absolute(path, "login"),
+ {header=header}, json.generate(jin))
+ return resp.status == 200 and sets_cookie(resp, "grafana_user") == user
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 8.1, 9.2, 10.3.4, 10.3.6, 12.1.2
+ name = "WebLogic Server Console",
+ cpe = "cpe:/a:bea:weblogic_server",
+ category = "web",
+ paths = {
+ {path = "/console/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 302
+ and (response.header["location"] or ""):find("/console/login/LoginForm%.jsp%f[;\0]")
+ end,
+ login_combos = {
+ -- WebLogic 9.x
+ {username = "weblogic", password = "weblogic"},
+ -- WebLogic 10.x, 12.x
+ {username = "weblogic", password = "weblogic1"},
+ {username = "weblogic", password = "welcome1"},
+ -- Adobe LiveCycle ES
+ {username = "weblogic", password = "password"},
+ -- PeopleSoft
+ {username = "system", password = "Passw0rd"}
+ },
+ login_check = function (host, port, path, user, pass)
+ local resp = http_post_simple(host, port,
+ url.absolute(path, "j_security_check"), nil,
+ {j_username=user,j_password=pass,j_character_encoding="UTF-8"})
+ -- WebLogic 8.x, 9.x
+ if resp.status == 403 then return false end
+ -- WebLogic 10.x, 12.x
+ if resp.status == 302
+ and (resp.header["location"] or ""):find("/console/login/LoginForm%.jsp$") then
+ return false
+ end
+ return true
+ end
+})
+
+table.insert(fingerprints, {
+ name = "Apache Tomcat",
+ cpe = "cpe:/a:apache:tomcat",
+ category = "web",
+ paths = {
+ {path = "/manager/html/"},
+ {path = "/manager/status/"},
+ {path = "/manager/text/"},
+ {path = "/tomcat/manager/html/"},
+ {path = "/tomcat/manager/status/"},
+ {path = "/tomcat/manager/text/"},
+ {path = "/cognos_express/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"},
+ -- https://cve.mitre.org/cgi-bin/cvename.cgi?name=2009-3548
+ {username = "admin", password = ""},
+ -- https://github.com/seshendra/vagrant-ubuntu-tomcat7/
+ {username = "admin", password = "tomcat"},
+ -- https://github.com/apache/tomcat/blob/2b8f9665dbfb89c78878784cd9b63d2b976ba623/webapps/manager/WEB-INF/jsp/403.jsp#L66
+ {username = "tomcat", password = "s3cret"},
+ -- https://cve.mitre.org/cgi-bin/cvename.cgi?name=2010-4094
+ {username = "ADMIN", password = "ADMIN"},
+ -- https://cve.mitre.org/cgi-bin/cvename.cgi?name=2009-4189
+ {username = "ovwebusr", password = "OvW*busr1"},
+ -- https://cve.mitre.org/cgi-bin/cvename.cgi?name=2009-4188
+ {username = "j2deployer", password = "j2deployer"},
+ -- https://cve.mitre.org/cgi-bin/cvename.cgi?name=2010-0557
+ {username = "cxsdk", password = "kdsxc"},
+ -- XAMPP https://www.apachefriends.org/index.html
+ {username = "xampp", password = "xampp"},
+ -- QLogic QConvergeConsole http://www.qlogic.com/
+ {username = "QCC", password = "QLogic66"},
+ -- HAPI FHIR http://hapifhir.io/
+ {username = "fhir", password = "FHIRDefaultPassword"}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_basic_login(host, port, path, user, pass, false)
+ end
+})
+
+table.insert(fingerprints, {
+ name = "Apache Tomcat Host Manager",
+ cpe = "cpe:/a:apache:tomcat",
+ category = "web",
+ paths = {
+ {path = "/host-manager/html/"},
+ {path = "/host-manager/text/"},
+ {path = "/tomcat/host-manager/html/"},
+ {path = "/tomcat/host-manager/text/"}
+ },
+ target_check = function (host, port, path, response)
+ return http_auth_realm(response) == "Tomcat Host Manager Application"
+ end,
+ login_combos = {
+ {username = "tomcat", password = "tomcat"},
+ {username = "admin", password = "admin"},
+ -- https://cve.mitre.org/cgi-bin/cvename.cgi?name=2009-3548
+ {username = "admin", password = ""},
+ -- https://github.com/seshendra/vagrant-ubuntu-tomcat7/
+ {username = "admin", password = "tomcat"},
+ -- https://github.com/apache/tomcat/blob/2b8f9665dbfb89c78878784cd9b63d2b976ba623/webapps/manager/WEB-INF/jsp/403.jsp#L66
+ {username = "tomcat", password = "s3cret"},
+ -- https://cve.mitre.org/cgi-bin/cvename.cgi?name=2010-4094
+ {username = "ADMIN", password = "ADMIN"},
+ -- https://cve.mitre.org/cgi-bin/cvename.cgi?name=2009-4189
+ {username = "ovwebusr", password = "OvW*busr1"},
+ -- https://cve.mitre.org/cgi-bin/cvename.cgi?name=2009-4188
+ {username = "j2deployer", password = "j2deployer"},
+ -- https://cve.mitre.org/cgi-bin/cvename.cgi?name=2010-0557
+ {username = "cxsdk", password = "kdsxc"},
+ -- XAMPP https://www.apachefriends.org/index.html
+ {username = "xampp", password = "xampp"},
+ -- QLogic QConvergeConsole http://www.qlogic.com/
+ {username = "QCC", password = "QLogic66"},
+ -- HAPI FHIR http://hapifhir.io/
+ {username = "fhir", password = "FHIRDefaultPassword"}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_basic_login(host, port, path, user, pass, false)
+ end
+})
+
+table.insert(fingerprints, {
+ name = "Apache Felix OSGi Management Console",
+ category = "web",
+ paths = {
+ {path = "/system/console"},
+ {path = "/lc/system/console"}
+ },
+ target_check = function (host, port, path, response)
+ return http_auth_realm(response) == "OSGi Management Console"
+ end,
+ login_combos = {
+ {username = "admin", password = "admin"},
+ {username = "karaf", password = "karaf"},
+ {username = "smx", password = "smx"}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_basic_login(host, port, path, user, pass, false)
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 1.4.1, 1.5.2, 1.5.3, 1.6.0, 1.6.1
+ name = "Apache Axis2",
+ cpe = "cpe:/a:apache:axis2",
+ category = "web",
+ paths = {
+ {path = "/axis2/axis2-admin/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ and response.body
+ and response.body:lower():find("<title>login to axis2 :: administration page</title>", 1, true)
+ end,
+ login_combos = {
+ {username = "admin", password = "axis2"}
+ },
+ login_check = function (host, port, path, user, pass)
+ local resp = http_post_simple(host, port, url.absolute(path, "login"), nil,
+ {userName=user,password=pass,submit=" Login "})
+ return resp.status == 200
+ and (resp.body or ""):lower():find("<a%s+href%s*=%s*(['\"])axis2%-admin/logout%1")
+ end
+})
+
+table.insert(fingerprints, {
+ name = "Plumtree Portal",
+ category = "web",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 302
+ and (response.header["location"] or ""):find("/portal/server%.pt$")
+ end,
+ login_combos = {
+ {username = "Administrator", password = ""}
+ },
+ login_check = function (host, port, path, user, pass)
+ local form = {in_hi_space="Login",
+ in_hi_spaceID="0",
+ in_hi_control="Login",
+ in_hi_dologin="true",
+ in_tx_username=user,
+ in_pw_userpass=pass,
+ in_se_authsource=""}
+ local resp = http_post_simple(host, port,
+ url.absolute(path, "portal/server.pt"),
+ nil, form)
+ return resp.status == 302
+ and (resp.header["location"] or ""):find("/portal/server%.pt[;?]")
+ and sets_cookie(resp, "plloginoccured") == "true"
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 0.4.4.6.1 on SamuraiWTF 2.6, 0.4.7.0 on Kali 2016.2
+ name = "BeEF",
+ category = "web",
+ paths = {
+ {path = "/ui/authentication/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ and response.body
+ and response.body:find("BeEF", 1, true)
+ and response.body:lower():find("<title>beef authentication</title>", 1, true)
+ end,
+ login_combos = {
+ {username = "beef", password = "beef"}
+ },
+ login_check = function (host, port, path, user, pass)
+ local resp = http_post_simple(host, port, url.absolute(path, "login"), nil,
+ {["username-cfrm"]=user, ["password-cfrm"]=pass})
+ return resp.status == 200
+ and (resp.body or ""):find("{%s*success%s*:%s*true%s*}")
+ end
+})
+
+table.insert(fingerprints, {
+ name = "Pure Storage",
+ category = "web",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ and response.body:find("<title>Pure Storage Login</title>", 1, true)
+ end,
+ login_combos = {
+ {username = "pureuser", password = "pureuser"}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_post_login(host, port, path, "login",
+ "{%s*Welcome%s*:%s*Invalid%s*}",
+ {["username"]=user, ["password"]=pass})
+ end
+})
+
+table.insert(fingerprints, {
+ name = "Active MQ",
+ category = "web",
+ paths = {
+ {path = "/admin"}
+ },
+ target_check = function (host, port, path, response)
+ return http_auth_realm(response) == "ActiveMQRealm"
+ end,
+ login_combos = {
+ {username = "admin", password = "admin"}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_basic_login(host, port, path, user, pass, false)
+ end
+})
+---
+--ROUTERS
+---
+table.insert(fingerprints, {
+ name = "Arris 2307",
+ category = "routers",
+ paths = {
+ {path = "/logo_t.gif"}
+ },
+ login_combos = {
+ {username = "", password = ""}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_post_login(host, port, path, "login.cgi", "Login Error !!", {action="submit", page="", logout="", pws=pass})
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 12.2SE on Catalyst 3750, 3845, CBS3020, 12.3 on Aironet 1300
+ name = "Cisco IOS",
+ cpe = "cpe:/o:cisco:ios",
+ category = "routers",
+ paths = {
+ {path = "/"},
+ -- TODO: Remove these paths completely unless a bug gets filed (9/1/2016)
+ -- (The paths are likely redundant. "/" should be covering all the cases.)
+ -- {path = "/exec/show/log/CR"},
+ -- {path = "/level/15/exec/-/configure/http"},
+ -- {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?%f[ ].* access$")
+ end,
+ login_combos = {
+ {username = "", password = ""},
+ {username = "cisco", password = "cisco"},
+ {username = "Cisco", password = "Cisco"}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_basic_login(host, port, path, user, pass, false)
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version (see below)
+ name = "Cisco Linksys",
+ cpe = "cpe:/h:linksys:*",
+ category = "routers",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ -- realm="Linksys WAP200", "Linksys WAP55AG", "Linksys E2000"
+ return (http_auth_realm(response) or ""):find("^Linksys %u[%u%d]+$")
+ end,
+ login_combos = {
+ -- WAP55AG, version 1.07.01
+ -- E2000, version 1.0.03 (any username is valid)
+ {username = "", password = "admin"},
+ -- WAP200, version 1.0.22
+ {username = "admin", password = "admin"},
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_basic_login(host, port, path, user, pass, false)
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version ESIP-12-v302r125573-131230c_upc on EPC3925
+ -- ES-16-E138-c3220r55103-150810 on EPC3928AD
+ name = "Cisco EPC39xx",
+ cpe = "cpe:/h:cisco:epc39*",
+ category = "routers",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ and response.body
+ and response.body:find("Docsis", 1, true)
+ and response.body:find("window%.location%.href%s*=%s*(['\"])Docsis_system%.asp%1")
+ end,
+ login_combos = {
+ {username = "", password = ""},
+ {username = "admin", password = "admin"}
+ },
+ login_check = function (host, port, path, user, pass)
+ local form = {username_login=user,
+ password_login=pass,
+ LanguageSelect="en",
+ Language_Submit="0",
+ login="Log In"}
+ local resp = http_post_simple(host, port,
+ url.absolute(path, "goform/Docsis_system"),
+ nil, form)
+ local loc = resp.header["location"] or ""
+ return resp.status == 302
+ and (loc:find("/Quick_setup%.asp$") or loc:find("/Administration%.asp$"))
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 1.0.1.3 on RT-N10U, RT-N66U
+ name = "ASUS RT",
+ cpe = "cpe:/h:asus:rt-*",
+ category = "routers",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ -- realm="RT-N10U", "RT-N66U"
+ return (http_auth_realm(response) or ""):find("^RT%-%u[%u%d]+$")
+ end,
+ login_combos = {
+ {username = "admin", password = "admin"}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_basic_login(host, port, path, user, pass, false)
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 5.00.12 on F5D7234-4
+ name = "Belkin G Wireless Router",
+ cpe = "cpe:/h:belkin:f5d7234-4",
+ category = "routers",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return have_openssl
+ and response.status == 200
+ and response.body
+ and response.body:find("setup_top.htm", 1, true)
+ and response.body:find("status.stm", 1, true)
+ end,
+ login_combos = {
+ {username = "", password = ""}
+ },
+ login_check = function (host, port, path, user, pass)
+ local resp = http_post_simple(host, port,
+ url.absolute(path, "cgi-bin/login.exe"), nil,
+ -- this should be local time, not UTC
+ {totalMSec = stdnse.clock_ms()/1000,
+ pws = stdnse.tohex(openssl.md5(pass))})
+ return resp.status == 302
+ and (resp.header["location"] or ""):find("/index%.htm$")
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 1.00.12 on F9K1001 v1
+ name = "Belkin N150",
+ cpe = "cpe:/h:belkin:n150_f9k1001",
+ category = "routers",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return have_openssl
+ and response.status == 200
+ and response.body
+ and response.body:find("Belkin", 1, true)
+ and response.body:find("isAPmode", 1, true)
+ and response.body:lower():find("showmenu.js", 1, true)
+ end,
+ login_combos = {
+ {username = "", password = ""}
+ },
+ login_check = function (host, port, path, user, pass)
+ local form = {page="",
+ logout="",
+ action="submit",
+ pws=base64.enc(pass),
+ itsbutton1="Submit",
+ h_language="en",
+ is_parent_window="1"}
+ local resp = http_post_simple(host, port, url.absolute(path, "login.cgi"),
+ nil, form)
+ return resp.status == 200
+ and resp.body
+ and resp.body:find("index.html", 1, true)
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version H131-310CTU-C07_R01_4.5.5.27
+ name = "Comtrend NexusLink-5631",
+ category = "routers",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return http_auth_realm(response) == "DSL Router"
+ end,
+ login_combos = {
+ {username = "apuser", password = "apuser"},
+ {username = "root", password = "12345"}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_basic_login(host, port, path, user, pass, false)
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 1.0.1-00 on model 5554
+ name = "Zoom ADSL X5",
+ category = "routers",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 301
+ and (response.header["server"] or ""):find("^Nucleus/%d+%.")
+ and (response.header["location"] or ""):find("/hag/pages/home%.htm$")
+ end,
+ login_combos = {
+ {username = "admin", password = "zoomadsl"}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_basic_login(host, port,
+ url.absolute(path, "hag/pages/home.htm"),
+ user, pass, false)
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 2.3, 2.4 on FVS318
+ name = "Netgear ProSafe Firewall FVS318",
+ cpe = "cpe:/h:netgear:fvs318",
+ category = "routers",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ and response.header["server"] == "Netgear"
+ and response.body
+ and response.body:lower():find("<frame%s+src%s*=%s*(['\"]?)top.html%1%s")
+ end,
+ login_combos = {
+ {username = "admin", password = "password"}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_basic_login(host, port, url.absolute(path, "top.html"),
+ user, pass, false)
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 2.00.03, .05, .07, .08 on GS108Ev3, GS108PEv3
+ name = "Netgear ProSafe Plus Switch",
+ cpe = "cpe:/h:netgear:gs108*",
+ category = "routers",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ and response.body
+ and response.body:find("loginTData", 1, true)
+ and response.body:lower():find("<title>netgear ", 1, true)
+ end,
+ login_combos = {
+ {username = "", password = "password"}
+ },
+ login_check = function (host, port, path, user, pass)
+ local resp = http_post_simple(host, port, url.absolute(path, "login.cgi"),
+ nil, {password=pass})
+ return resp.status == 200 and sets_cookie(resp, "GS108SID", ".")
+ end
+})
+
+table.insert(fingerprints, {
+ -- AP6521, AP6522, AP7522, AP7532
+ name = "Motorola AP",
+ category = "routers",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ and (response.header["server"] or ""):find("^lighttpd/%d+%.")
+ and response.body
+ and response.body:lower():find("<title>motorola solutions</title>", 1, true)
+ end,
+ login_combos = {
+ {username = "admin", password = "motorola"}
+ },
+ login_check = function (host, port, path, user, pass)
+ local form = {_dc = math.floor(stdnse.clock_ms()),
+ username = user,
+ password = pass}
+ local lurl = url.absolute(path, "rest.fcgi/services/rest/login?" .. url.build_query(form))
+ local resp = http_get_simple(host, port, lurl)
+ if not (resp.status == 200 and resp.body) then return false end
+ local jstatus, jout = json.parse(resp.body)
+ return jstatus and jout.status
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 3.3.2, 4.3.1, 4.4.0, 4.4.1 on RFS6000
+ name = "Motorola RF Switch",
+ category = "routers",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ and (response.header["server"] or ""):find("^thttpd/%d+%.")
+ and response.body
+ and response.body:lower():find("<title>motorola wireless network management</title>", 1, true)
+ end,
+ login_combos = {
+ {username = "admin", password = "superuser"}
+ },
+ login_check = function (host, port, path, user, pass)
+ local login = ("J20K34NMMT89XPIJ34S login %s %s"):format(stdnse.tohex(user), stdnse.tohex(pass))
+ local lurl = url.absolute(path, "usmCgi.cgi/?" .. url.escape(login))
+ local resp = http_get_simple(host, port, lurl)
+ return resp.status == 200
+ and (resp.body or ""):find("^login 0 ")
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 3.4.5.1 on Aruba800
+ name = "ArubaOS WebUI",
+ cpe = "cpe:/o:arubanetworks:arubaos",
+ category = "routers",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 401
+ and response.body
+ and response.body:find("/images/arubalogo.gif", 1, true)
+ and response.body:find("/screens/wms/wms.login", 1, true)
+ end,
+ login_combos = {
+ {username = "admin", password = "admin"}
+ },
+ login_check = function (host, port, path, user, pass)
+ local resp = http_post_simple(host, port,
+ url.absolute(path, "screens/wms/wms.login"),
+ nil,
+ {opcode="login", url="/", needxml="0",
+ uid=user, passwd=pass})
+ return resp.status == 200
+ and (resp.body or ""):find("/screens/wmsi/monitor.summary.html", 1, true)
+ end
+})
+
+table.insert(fingerprints, {
+ name = "Aruba AirWave",
+ cpe = "cpe:/a:arubanetworks:airwave",
+ category = "routers",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 401
+ and response.body
+ and response.body:find("/noauth/theme/airwave/favicon.ico", 1, true)
+ and response.body:find("/api/user_prefs.json", 1, true)
+ end,
+ login_combos = {
+ {username = "admin", password = "admin"}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_post_login(host, port, path, "LOGIN",
+ "403 Forbidden",
+ {credential_0=user, credential_1=pass,
+ destination=url.absolute(path, "index.html")})
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 08.05.100 on NVR 1750D
+ name = "Nortel VPN Router",
+ cpe = "cpe:/h:nortel:vpn_router_*",
+ category = "routers",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ and response.header["server"] == "HTTP Server"
+ and response.body
+ and response.body:lower():find("<title>nortel vpn router</title>", 1, true)
+ end,
+ login_combos = {
+ {username = "admin", password = "setup"}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_basic_login(host, port,
+ url.absolute(path, "manage/bdy_sys.htm"),
+ user, pass, false)
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 5.0.0r8 on NetScreen 5XT
+ name = "ScreenOS",
+ cpe = "cpe:/o:juniper:netscreen_screenos",
+ category = "routers",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ and (response.header["server"] or ""):find("^Virata%-EmWeb/R%d+_")
+ and response.body
+ and response.body:lower():find("admin_pw", 1, true)
+ end,
+ login_combos = {
+ {username = "netscreen", password = "netscreen"}
+ },
+ login_check = function (host, port, path, user, pass)
+ local form = {admin_id="",
+ admin_pw="",
+ time=tostring(math.floor(stdnse.clock_ms())):sub(5),
+ un=base64.enc(user),
+ pw=base64.enc(pass)}
+ local resp = http_post_simple(host, port, url.absolute(path, "index.html"),
+ nil, form)
+ return resp.status == 303
+ and (resp.header["location"] or ""):find("/nswebui.html?", 1, true)
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 11.4.1, 11.5.3
+ name = "F5 TMOS",
+ cpe = "cpe:/o:f5:tmos",
+ category = "routers",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ and response.body
+ and response.body:find("F5 Networks", 1, true)
+ and response.body:find("BIG-IP", 1, true)
+ and response.body:find("/tmui/tmui/system/settings/redirect.jsp", 1, true)
+ end,
+ login_combos = {
+ {username = "admin", password = "admin"}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_post_login(host, port, path, "tmui/logmein.html",
+ "login%.jsp%?msgcode=1",
+ {username=user, passwd=pass})
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 10.5 on MPX 8005
+ name = "Citrix NetScaler",
+ cpe = "cpe:/a:citrix:netscaler",
+ category = "routers",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ and response.body
+ and response.body:find("NetScaler", 1, true)
+ and response.body:lower():find("<title>citrix login</title>", 1, true)
+ end,
+ login_combos = {
+ {username = "nsroot", password = "nsroot"}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_post_login(host, port, path, "login/do_login",
+ "Invalid username or password",
+ {username=user, password=pass, url="", timezone_offset="0"},
+ false)
+ end
+})
+
+---
+--Digital recorders
+---
+table.insert(fingerprints, {
+ -- UI Version 03.2 (4.8), 03.2 (5.5)
+ name = "DM Digital Sprite 2",
+ category = "security",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ and response.body
+ and response.body:find("Dedicated Micros", 1, true)
+ and response.body:find("webpages/index.shtml", 1, true)
+ and response.body:lower():find('<meta%s+name%s*=%s*"author"%s+content%s*=%s*"dedicated micros ')
+ end,
+ login_combos = {
+ {username = "dm", password = "web"}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_basic_login(host, port,
+ url.absolute(path, "frmpages/index.html"),
+ user, pass, true)
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version SD32L30, ECS116/A
+ name = "DM NetVu",
+ category = "security",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ and response.body
+ and response.body:find("Dedicated Micros", 1, true)
+ and response.body:find("/gui/gui_outer_frame.shtml", 1, true)
+ and response.body:lower():find('<meta%s+name%s*=%s*"author"%s+content%s*=%s*"dedicated micros ')
+ end,
+ login_combos = {
+ {username = "", password = ""}
+ },
+ login_check = function (host, port, path, user, pass)
+ local lurl = url.absolute(path, "gui/frmpages/gui_system.shtml")
+ -- Check if authentication is required at all
+ local resp = http_get_simple(host, port, lurl)
+ if resp.status == 200 then
+ return (resp.body or ""):find('top.render_table("System Page"', 1, true)
+ end
+ return try_http_basic_login(host, port, lurl, user, pass, true)
+ end
+})
+
+table.insert(fingerprints, {
+ name = "AXIS Network Camera (M Series)",
+ category = "security",
+ paths = {
+ {path = "/view/viewer_index.shtml"},
+ },
+ target_check = function (host, port, path, response)
+ local realm = http_auth_realm(response) or ""
+ return realm:find("AXIS")
+ end,
+ login_combos = {
+ {username = "admin", password = "admin"},
+ {username = "admin", password = ""},
+ {username = "root", password = ""},
+ {username = "root", password = "root"}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_basic_login(host, port, path, user, pass, false)
+ end
+})
+
+table.insert(fingerprints, {
+ name = "Hikvision DS-XXX Network Camera",
+ category = "security",
+ paths = {
+ {path = "/PSIA/Custom/SelfExt/userCheck"},
+ },
+ target_check = function (host, port, path, response)
+ return response.header["server"] == "App-webs/"
+
+ end,
+ login_combos = {
+ {username = "admin", password = "12345"},
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_basic_login(host, port, path, user, pass, false)
+ end
+})
+
+table.insert(fingerprints, {
+ name = "NUOO DVR",
+ category = "security",
+ paths = {
+ {path = "/"},
+ },
+ target_check = function (host, port, path, response)
+ return response.header['server'] and response.header["server"]:find("lighttpd")
+ and response.body and response.body:lower():find("<title>network video recorder login</title>")
+ end,
+ login_combos = {
+ {username = "admin", password = "admin"},
+ },
+ login_check = function (host, port, path, user, pass)
+ local resp = http_post_simple(host, port,
+ url.absolute(path, "login.php"), nil,
+ {language="en", user=user, pass=pass,submit="Login"})
+ if resp.status == 302 and not(resp.body:find("loginfail")) then return true end
+ end
+})
+
+---
+--Industrial systems
+---
+table.insert(fingerprints, {
+ -- Version 2.1.2, 2.2.0 on TSX ETY Port, 1.0.4, 2.2.0 on TSX ETY410
+ name = "Schneider Modicon Web",
+ category = "industrial",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 302
+ and (response.header["server"] or ""):find("^Schneider%-WEB/V%d+%.")
+ and (response.header["location"] or ""):find("/index%.htm$")
+ end,
+ login_combos = {
+ {username = "USER", password = "USER"}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_basic_login(host, port,
+ url.absolute(path, "secure/system/globaldata.htm?Language=English"),
+ user, pass, false)
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 06.05.00/6.0.1 on QD2040 HW 675
+ name = "TCS Basys Controls Communication Center",
+ category = "industrial",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return http_auth_realm(response) == "Private"
+ and (response.header["server"] or ""):find("^lighttpd/%d+%.")
+ end,
+ login_combos = {
+ {username = "admin", password = "password"}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_basic_login(host, port, path, user, pass, false)
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 01.01
+ name = "Riello UPS NetMan 204",
+ category = "industrial",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ and (response.header["server"] or ""):find("^mini_httpd/%d+%.")
+ and response.body
+ and response.body:lower():find("<title>netman 204 login</title>", 1, true)
+ end,
+ login_combos = {
+ {username = "admin", password = "admin"},
+ {username = "fwupgrade", password = "fwupgrade"},
+ {username = "user", password = "user"},
+ {username = "eurek", password = "eurek"}
+ },
+ login_check = function (host, port, path, user, pass)
+ local resp = http_post_simple(host, port,
+ url.absolute(path, "cgi-bin/login.cgi"),
+ nil, {username=user, password=pass})
+ return resp.status == 200
+ and resp.body
+ and (resp.body:find(">window.location.replace(", 1, true)
+ or resp.body:find("Another user is logged in", 1, true))
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 3.2.5 on AP9606, 2.5.0, 2.6.4 on AP9617, AP9619, 1.1.6 on AP7900
+ name = "APC Management Card (basic auth)",
+ category = "industrial",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return http_auth_realm(response) == "APC Management Card"
+ end,
+ login_combos = {
+ {username = "apc", password = "apc"}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_basic_login(host, port, path, user, pass, false)
+ end
+})
+
+---
+--Printers
+---
+table.insert(fingerprints, {
+ -- Version 61.17.5Z on ZTC GK420d
+ name = "Zebra Printer",
+ category = "printer",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ and response.body
+ and response.body:find("Zebra Technologies", 1, true)
+ and response.body:lower():find('<a href="config.html">view printer configuration</a>', 1, true)
+ end,
+ login_combos = {
+ {username = "", password = "1234"}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_post_login(host, port, path, "authorize",
+ "incorrect password", {["0"]=pass})
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 61.17.5Z on ZTC GK420d, 1.01.4
+ name = "Zebra Print Server",
+ category = "printer",
+ paths = {
+ {path = "/server/TCPIPGEN.htm"}
+ },
+ target_check = function (host, port, path, response)
+ return http_auth_realm(response) == "Network Print Server"
+ end,
+ login_combos = {
+ {username = "admin", password = "1234"}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_basic_login(host, port, path, user, pass, false)
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 1.04.9 on RICOH MP C4503, 1.05 on MP 5054, 1.12 on MP C5000
+ name = "RICOH Web Image Monitor",
+ category = "printer",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ and (response.header["server"] or ""):find("^Web%-Server/%d+%.")
+ and response.body
+ and response.body:find("/websys/webArch/mainFrame.cgi", 1, true)
+ end,
+ login_combos = {
+ {username = "admin", password = ""},
+ {username = "supervisor", password = ""}
+ },
+ login_check = function (host, port, path, user, pass)
+ -- determine proper login path by locale
+ local resp0 = http.get(host, port, path)
+ if resp0.status ~= 200 then return false end
+ local lurl = (resp0.body or ""):match('location%.href="(/[^"]+/)mainFrame%.cgi"')
+ if not lurl then return false end
+ -- harvest the login form token
+ local resp1 = http_get_simple(host, port, url.absolute(lurl, "authForm.cgi"),
+ {cookies="cookieOnOffChecker=on"})
+ if resp1.status ~= 200 then return false end
+ local token = (resp1.body or ""):match('<input%s+type%s*=%s*"hidden"%s+name%s*=%s*"wimToken"%s+value%s*=%s*"(.-)"')
+ if not token then return false end
+ -- build the login form and submit it
+ local form = {wimToken = token,
+ userid_work = "",
+ userid = base64.enc(user),
+ password_work = "",
+ password = base64.enc(pass),
+ open = ""}
+ local resp2 = http_post_simple(host, port, url.absolute(lurl, "login.cgi"),
+ {cookies=resp1.cookies}, form)
+ return resp2.status == 302
+ and (resp2.header["location"] or ""):find("/mainFrame%.cgi$")
+ and sets_cookie(resp2, "wimsesid", "^%d+$")
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 071.*, 072.* on WorkCentre 7835, 7845, ColorQube 8900X
+ name = "Xerox WorkCentre/ColorQube",
+ cpe = "cpe:/h:xerox:workcentre",
+ category = "printer",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ and response.body
+ and response.body:find('SuppliesType != "InkStick"', 1, true)
+ and response.body:find("XEROX WORKCENTRE", 1, true)
+ end,
+ login_combos = {
+ {username = "admin", password = "1111"}
+ },
+ login_check = function (host, port, path, user, pass)
+ local form = {_fun_function="HTTP_Authenticate_fn",
+ NextPage=url.absolute(path, "properties/authentication/luidLogin.php"),
+ webUsername=user,
+ webPassword=pass,
+ frmaltDomain="default"}
+ return try_http_post_login(host, port, path, "userpost/xerox.set",
+ "/login%.php%?invalid=t", form)
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 1.1, 1.1 SP7
+ name = "EFI Fiery Webtools",
+ category = "printer",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ and (response.header["content-location"] or ""):find("^redirect%.html%.")
+ and response.body
+ and response.body:lower():find('content="0;url=wt2parser.cgi?home_', 1, true)
+ end,
+ login_combos = {
+ {username = "Administrator", password = ""},
+ {username = "Administrator", password = "Fiery.1"}
+ },
+ login_check = function (host, port, path, user, pass)
+ -- sessionId normally includes the client IP, not the target,
+ -- but this would be too revealing
+ local sessionid = host.ip
+ .. "_"
+ .. math.floor(stdnse.clock_ms())
+ .. math.random(100000, 999999)
+ local encpass = xmlencode(pass)
+ local header = {["Content-Type"]="text/xml", ["SOAPAction"]='""'}
+ local soapmsg = [[
+ <?xml version='1.0' encoding='UTF-8'?>
+ <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ <SOAP-ENV:Body>
+ <ns1:doLogin xmlns:ns1="urn:FierySoapService" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ <sessionId xsi:type="xsd:string">__SESS__</sessionId>
+ <in xsi:type="ns1:Login">
+ <fieldsMask xsi:type="xsd:int">0</fieldsMask>
+ <password xsi:type="xsd:string">__PASS__</password>
+ <timeout xsi:type="xsd:int">30</timeout>
+ <userName xsi:type="xsd:string" xsi:nil="true"/>
+ </in>
+ </ns1:doLogin>
+ </SOAP-ENV:Body>
+ </SOAP-ENV:Envelope>
+ ]]
+ -- strip off indentation
+ soapmsg = soapmsg:gsub("%f[^\0\n]%s+", "")
+ -- username is not injected into the payload because it is implied
+ soapmsg = soapmsg:gsub("__%w+__", {__SESS__=sessionid, __PASS__=encpass})
+ local resp = http_post_simple(host, port, url.absolute(path, "soap"),
+ {header=header}, soapmsg)
+ return resp.status == 200
+ and (resp.body or ""):find('<result xsi:type="xsd:boolean">true</result>', 1, true)
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 3.6/4
+ name = "Lantronix ThinWeb Manager",
+ category = "printer",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return have_openssl
+ and response.status == 200
+ and (response.header["server"] or ""):find("^Gordian Embedded")
+ and response.body
+ and response.body:lower():find("<title>lantronix thinweb manager", 1, true)
+ end,
+ login_combos = {
+ {username = "", password = "system"}
+ },
+ login_check = function (host, port, path, user, pass)
+ local lurl = url.absolute(path, "server_eps.html")
+ -- obtain login nonce
+ local resp1 = http_get_simple(host, port, lurl)
+ local nonce = resp1.status == 403 and sets_cookie(resp1, "SrvrNonce", ".")
+ if not nonce then return false end
+ -- credential is the MD5 hash of the nonce and the password (in upper case)
+ local creds = stdnse.tohex(openssl.md5(nonce .. ":" .. pass:upper()))
+ local cookies = ("SrvrNonce=%s; SrvrCreds=%s"):format(nonce, creds)
+ local resp2 = http_get_simple(host, port, lurl, {cookies=cookies})
+ return resp2.status == 200
+ end
+})
+
+---
+--Storage
+---
+table.insert(fingerprints, {
+ -- Version TS200R021 on MSA 2000 G3
+ name = "HP Storage Management Utility",
+ category = "storage",
+ paths = {
+ {path = "/api/id/"}
+ },
+ -- TODO: Change the probe path to "/" and use the following target_check
+ -- once the http library adds support for gzip encoding. Don't forget
+ -- to change url.absolute() argument from "../" to "api/" in login_check.
+ --target_check = function (host, port, path, response)
+ -- return have_openssl
+ -- and response.status == 200
+ -- and response.body
+ -- and response.body:find("brandStrings", 1, true)
+ -- and response.body:find("checkAuthentication", 1, true)
+ -- and response.body:find("hp stuff init", 1, true)
+ --end,
+ target_check = function (host, port, path, response)
+ return have_openssl
+ and response.status == 200
+ and response.header["command-status"]
+ and response.header["command-status"]:find("^0 %({%s*systemName:.*,%s*controller:.*}%)")
+ end,
+ login_combos = {
+ {username = "monitor", password = "!monitor"},
+ {username = "manage", password = "!manage"},
+ {username = "admin", password = "!admin"}
+ },
+ login_check = function (host, port, path, user, pass)
+ local creds = stdnse.tohex(openssl.md5(user .. "_" .. pass))
+ local header = {["Content-Type"] = "application/x-www-form-urlencoded",
+ ["datatype"] = "json"}
+ local resp = http_post_simple(host, port, url.absolute(path, "../"),
+ {header=header}, "/api/login/" .. creds)
+ return resp.status == 200
+ and (resp.header["command-status"] or ""):find("^1 ")
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 7.5.0.3 on 2072-24C
+ name = "IBM Storwize V3700",
+ cpe = "cpe:/a:ibm:storwize_v3700_software",
+ category = "storage",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ and response.body
+ and response.body:find("V3700", 1, true)
+ and response.body:lower():find("<title>[^<]-%sibm storwize v3700%s*</title>")
+ end,
+ login_combos = {
+ {username = "superuser", password = "passw0rd"}
+ },
+ login_check = function (host, port, path, user, pass)
+ local form = {login=user,
+ password=pass,
+ newPassword="",
+ confirmPassword="",
+ tzoffset="0", -- present twice in the original form
+ nextURL="", -- present twice in the original form
+ licAccept=""}
+ local resp = http_post_simple(host, port, url.absolute(path, "login"),
+ nil, form)
+ return resp.status == 302
+ and (resp.header["location"] or ""):find("/gui$")
+ end
+})
+
+---
+--Virtualization systems
+---
+table.insert(fingerprints, {
+ -- Version 5.0.0
+ name = "VMware ESXi",
+ cpe = "cpe:/o:vmware:esxi",
+ category = "virtualization",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ and response.body
+ and response.body:find("ID_EESX_Welcome", 1, true)
+ and response.body:find("/folder?dcPath=ha-datacenter", 1, true)
+ end,
+ login_combos = {
+ {username = "root", password = ""}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_basic_login(host, port,
+ url.absolute(path, "folder?dcPath=ha-datacenter"),
+ user, pass, false)
+ end
+})
+
+table.insert(fingerprints, {
+ -- Version 4.0.0
+ name = "PCoIP Zero Client",
+ cpe = "cpe:/a:teradici:pcoip_host_software",
+ category = "virtualization",
+ paths = {
+ {path = "/login.html"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ and response.body
+ and response.body:find("PCoIP&#174 Zero Client", 1, true)
+ and response.body:find("password_value", 1, true)
+ end,
+ login_combos = {
+ {username = "", password = "Administrator"}
+ },
+ login_check = function (host, port, path, user, pass)
+ local resp = http_post_simple(host, port, url.absolute(path, "cgi-bin/login"),
+ nil, {password_value=pass, idle_timeout=60})
+ -- successful login is a 302-redirect that sets a session cookie with hex
+ -- value; failed login is the same but the cookie contains an error message
+ return resp.status == 302 and sets_cookie(resp, "session_id", "^%x+$")
+ end
+})
+
+---
+--Remote consoles
+---
+table.insert(fingerprints, {
+ -- Version 5.5, 6.1, 6.2, 7.2 on SLC16, SLC32, SLC48, SLC 8016
+ name = "Lantronix SLC",
+ category = "console",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ and (response.header["server"] or ""):find("^mini_httpd/%d+%.")
+ and response.body
+ and response.body:find("lantronix", 1, true)
+ and response.body:find("slcpassword", 1, true)
+ end,
+ login_combos = {
+ {username = "sysadmin", password = "PASS"}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_post_login(host, port, path, "./",
+ "%sname%s*=%s*(['\"]?)slcpassword%1[%s>]",
+ {slclogin=user, slcpassword=pass})
+ end
+})
+
+table.insert(fingerprints, {
+ --Version 1.10.12, 1.80
+ name = "Dell iDRAC6",
+ cpe = "cpe:/o:dell:idrac6_firmware",
+ category = "console",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 301
+ and (response.header["server"] or ""):find("^Mbedthis%-Appweb/%d+%.")
+ and (response.header["location"] or ""):find("/start%.html$")
+ end,
+ login_combos = {
+ {username = "root", password = "calvin"}
+ },
+ login_check = function (host, port, path, user, pass)
+ return try_http_post_login(host, port, path, "data/login",
+ "<authResult>1</authResult>",
+ {user=user, password=pass})
+ end
+})
+
+table.insert(fingerprints, {
+ name = "Dell iDRAC9",
+ cpe = "cpe:/o:dell:idrac9_firmware",
+ category = "console",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ -- analyze response for 1st request to "/"
+ if not (response.status == 302 and (response.header["location"] or ""):find("/restgui/start%.html$")) then return false end
+
+ -- check with 2nd request to "/restgui/start.html" to be sure
+ local resp = http_get_simple(host, port, url.absolute(path, "restgui/start.html"))
+ return resp.status == 200
+ and resp.body
+ and resp.body:find("idrac-start-screen", 1, true)
+ end,
+ login_combos = {
+ {username = "root", password = "calvin"}
+ },
+ login_check = function (host, port, path, user, pass)
+ local headers = {
+ ["user"]='"'..user..'"',
+ ["password"]='"'..pass..'"'
+ }
+ local resp = http_post_simple(host, port, url.absolute(path, "sysmgmt/2015/bmc/session"),
+ {header=headers})
+ local body = resp.body or ""
+
+ return (resp.status == 201 and (
+ body:find('"authResult":0') -- standard login success
+ or body:find('"authResult":7') -- login success with default credentials
+ or body:find('"authResult":9') -- login success with password reset required
+ )
+ )
+ end
+})
+
+table.insert(fingerprints, {
+ --Version 1.1 on Supermicro X7SB3
+ name = "Supermicro WPCM450",
+ category = "console",
+ paths = {
+ {path = "/"}
+ },
+ target_check = function (host, port, path, response)
+ return response.status == 200
+ and response.body
+ and response.body:find("ATEN International", 1, true)
+ and response.body:find("/cgi/login.cgi", 1, true)
+ end,
+ login_combos = {
+ {username = "ADMIN", password = "ADMIN"}
+ },
+ login_check = function (host, port, path, user, pass)
+ local resp = http_post_simple(host, port, url.absolute(path, "cgi/login.cgi"),
+ nil, {name=user, pwd=pass})
+ return resp.status == 200
+ and (resp.body or ""):find("../cgi/url_redirect.cgi?url_name=mainmenu", 1, true)
+ end
+})