summaryrefslogtreecommitdiffstats
path: root/scripts/ldap-novell-getpass.nse
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/ldap-novell-getpass.nse')
-rw-r--r--scripts/ldap-novell-getpass.nse139
1 files changed, 139 insertions, 0 deletions
diff --git a/scripts/ldap-novell-getpass.nse b/scripts/ldap-novell-getpass.nse
new file mode 100644
index 0000000..95c762d
--- /dev/null
+++ b/scripts/ldap-novell-getpass.nse
@@ -0,0 +1,139 @@
+local comm = require "comm"
+local ldap = require "ldap"
+local shortport = require "shortport"
+local stdnse = require "stdnse"
+local table = require "table"
+
+description = [[
+Attempts to retrieve the Novell Universal Password for a user. You
+must already have (and include in script arguments) the username and password for an eDirectory server
+administrative account.
+]]
+
+---
+-- Universal Password enables advanced password policies, including extended
+-- characters in passwords, synchronization of passwords from eDirectory to
+-- other systems, and a single password for all access to eDirectory.
+--
+-- In case the password policy permits administrators to retrieve user
+-- passwords ("Allow admin to retrieve passwords" is set in the password
+-- policy) this script can retrieve the password.
+--
+-- @args ldap-novell-getpass.account The name of the account to retrieve the
+-- password for
+-- @args ldap-novell-getpass.username The LDAP username to use when connecting
+-- to the server
+-- @args ldap-novell-getpass.password The LDAP password to use when connecting
+-- to the server
+--
+-- @usage
+-- nmap -p 636 --script ldap-novell-getpass --script-args \
+-- 'ldap-novell-getpass.username="CN=admin,O=cqure", \
+-- ldap-novell-getpass.password=pass1234, \
+-- ldap-novell-getpass.account="CN=paka,OU=hr,O=cqure"'
+--
+-- @output
+-- PORT STATE SERVICE REASON
+-- 636/tcp open ldapssl syn-ack
+-- | ldap-novell-getpass:
+-- | Account: CN=patrik,OU=security,O=cqure
+-- |_ Password: foobar
+--
+
+-- Version 0.1
+-- Created 05/11/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
+
+
+author = "Patrik Karlsson"
+license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
+categories = {"discovery", "safe"}
+
+
+portrule = shortport.port_or_service({389,636}, {"ldap","ldapssl"})
+
+local function fail (err) return stdnse.format_output(false, err) end
+
+function action(host,port)
+
+ local username = stdnse.get_script_args("ldap-novell-getpass.username")
+ local password = stdnse.get_script_args("ldap-novell-getpass.password") or ""
+ local account = stdnse.get_script_args("ldap-novell-getpass.account")
+
+ if ( not(username) ) then
+ return fail("No username was supplied (ldap-novell-getpass.username)")
+ end
+ if ( not(account) ) then
+ return fail("No account was supplied (ldap-novell-getpass.account)")
+ else
+ -- do some basic account validation
+ if ( not(account:match("^[Cc][Nn]=.*,") ) ) then
+ return fail("The account argument should be specified as: \"CN=name,OU=orgunit,O=org\"")
+ end
+ end
+
+ -- In order to discover what protocol to use (SSL/TCP) we need to send a
+ -- few bytes to the server. An anonymous bind should do it
+ local anon_bind = stdnse.fromhex( "300c020101600702010304008000" )
+ local socket, _, opt = comm.tryssl( host, port, anon_bind, nil )
+ if ( not(socket) ) then
+ return fail("Failed to connect to LDAP server")
+ end
+
+ local status, errmsg = ldap.bindRequest( socket, {
+ version = 3,
+ username = username,
+ password = password
+ }
+ )
+
+ if ( not(status) ) then return errmsg end
+
+ -- Start encoding the NMAS Get Password Request
+ local NMASLDAP_GET_PASSWORD_REQUEST = "2.16.840.1.113719.1.39.42.100.13"
+ local NMASLDAP_GET_PASSWORD_RESPONSE = "2.16.840.1.113719.1.39.42.100.14"
+ -- Add a trailing zero to the account name
+ local data = ldap.encode( account .. '\0' )
+
+ -- The following section could do with more documentation
+ -- It's based on packet dumps from the getpass utility available from Novell Cool Solutions
+ -- encode the account name as a sequence
+ data = ldap.encode( { _ldaptype = '30', stdnse.fromhex( "020101") .. data } )
+ data = ldap.encode( { _ldaptype = '81', data } )
+ data = ldap.encode( { _ldaptype = '80', NMASLDAP_GET_PASSWORD_REQUEST } ) .. data
+ data = ldap.encode( { _ldaptype = '77', data } )
+
+ -- encode the whole extended request as a sequence
+ data = ldap.encode( { _ldaptype = '30', stdnse.fromhex( "020102") .. data } )
+
+ status = socket:send(data)
+ if ( not(status) ) then return fail("Failed to send request") end
+
+ status, data = socket:receive()
+ if ( not(status) ) then return data end
+ socket:close()
+
+ local response = ldap.decode(data)
+
+ -- make sure the result code was a success
+ local rescode = ( #response >= 2 ) and response[2]
+ local respname = ( #response >= 5 ) and response[5]
+
+ if ( rescode ~= 0 ) then
+ local errmsg = ( #response >= 4 ) and response[4] or "An unknown error occurred"
+ return fail(errmsg)
+ end
+
+ -- make sure we get a NMAS Get Password Response back from the server
+ if ( respname ~= NMASLDAP_GET_PASSWORD_RESPONSE ) then return end
+
+ local universal_pw = ( #response >= 6 and #response[6] >= 3 ) and response[6][3]
+
+ if ( universal_pw ) then
+ local output = {}
+ table.insert(output, ("Account: %s"):format(account))
+ table.insert(output, ("Password: %s"):format(universal_pw))
+ return stdnse.format_output(true, output)
+ else
+ return fail("No password was found")
+ end
+end