From 0d47952611198ef6b1163f366dc03922d20b1475 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 09:42:04 +0200 Subject: Adding upstream version 7.94+git20230807.3be01efb1+dfsg. Signed-off-by: Daniel Baumann --- scripts/citrix-brute-xml.nse | 163 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 scripts/citrix-brute-xml.nse (limited to 'scripts/citrix-brute-xml.nse') diff --git a/scripts/citrix-brute-xml.nse b/scripts/citrix-brute-xml.nse new file mode 100644 index 0000000..021fe9e --- /dev/null +++ b/scripts/citrix-brute-xml.nse @@ -0,0 +1,163 @@ +local citrixxml = require "citrixxml" +local nmap = require "nmap" +local shortport = require "shortport" +local stdnse = require "stdnse" +local table = require "table" +local unpwdb = require "unpwdb" + +description = [[ +Attempts to guess valid credentials for the Citrix PN Web Agent XML +Service. The XML service authenticates against the local Windows server +or the Active Directory. + +This script makes no attempt of preventing account lockout. If the +password list contains more passwords than the lockout-threshold +accounts will be locked. +]] + +--- +-- @usage +-- nmap --script=citrix-brute-xml --script-args=userdb=,passdb=,ntdomain= -p 80,443,8080 +-- +-- @output +-- PORT STATE SERVICE REASON +-- 8080/tcp open http-proxy syn-ack +-- | citrix-brute-xml: +-- | Joe:password => Must change password at next logon +-- | Luke:summer => Login was successful +-- |_ Jane:secret => Account is disabled + +-- Version 0.2 + +-- Created 11/30/2009 - v0.1 - created by Patrik Karlsson +-- Revised 12/02/2009 - v0.2 - Use stdnse.format_ouput for output + + +author = "Patrik Karlsson" +license = "Same as Nmap--See https://nmap.org/book/man-legal.html" +categories = {"intrusive", "brute"} + + +portrule = shortport.portnumber({8080,80,443}, "tcp") + +--- Verifies if the credentials (username, password and domain) are valid +-- +-- @param host string or host table against which to perform +-- @param port number or port table of the XML service +-- @param username string, the username to authenticate as +-- @param password string, the password to authenticate with +-- @param domain string, the Windows domain to authenticate against +-- +-- @return success, message +-- +function verify_password( host, port, username, password, domain ) + + local response = citrixxml.request_validate_credentials(host, port, {Credentials={Domain=domain, Password=password, UserName=username}}) + local cred_status = citrixxml.parse_validate_credentials_response(response) + + local account = {} + + account.username = username + account.password = password + account.domain = domain + + if cred_status.ErrorId then + if cred_status.ErrorId == "must-change-credentials" then + account.valid = true + account.message = "Must change password at next logon" + elseif cred_status.ErrorId == "account-disabled" then + account.valid = true + account.message = "Account is disabled" + elseif cred_status.ErrorId == "account-locked-out" then + account.valid = false + account.message = "Account Locked Out" + elseif cred_status.ErrorId == "failed-credentials" then + account.valid = false + account.message = "Incorrect Password" + elseif cred_status.ErrorId == "unspecified" then + account.valid = false + account.message = "Unspecified" + else + stdnse.debug1("UNKNOWN response: " .. response) + account.valid = false + account.message = "failed" + end + else + account.message = "Login was successful" + account.valid = true + end + + return account + +end + +--- Formats the result from the table of valid accounts +-- +-- @param accounts table containing accounts (tables) +-- @return string containing the result +function create_result_from_table(accounts) + local result = {} + + for i, account in ipairs(accounts) do + result[i] = ("\n %s:%s => %s"):format(account.username, account.password, account.message) + end + + return table.concat(result) +end + +action = function(host, port) + + local status, nextUser, nextPass + local username, password + local args = nmap.registry.args + local ntdomain = args.ntdomain + local valid_accounts = {} + + if not ntdomain then + return "FAILED: No domain specified (use ntdomain argument)" + end + + status, nextUser = unpwdb.usernames() + + if not status then + return + end + + status, nextPass = unpwdb.passwords() + + if not status then + return + end + + username = nextUser() + + -- iterate over userlist + while username do + password = nextPass() + + -- iterate over passwordlist + while password do + local result = "Trying " .. username .. "/" .. password .. " " + local account = verify_password(host, port, username, password, ntdomain) + + if account.valid then + + table.insert(valid_accounts, account) + + if account.valid then + stdnse.debug1("Trying %s/%s => Login Correct, Info: %s", username, password, account.message) + else + stdnse.debug1("Trying %s/%s => Login Correct", username, password) + end + else + stdnse.debug1("Trying %s/%s => Login Failed, Reason: %s", username, password, account.message) + end + password = nextPass() + end + + nextPass("reset") + username = nextUser() + end + + return create_result_from_table(valid_accounts) +end -- cgit v1.2.3