Lucene search

K
nmapPatrik KarlssonNMAP:LDAP-NOVELL-GETPASS.NSE
HistoryMay 28, 2011 - 8:48 a.m.

ldap-novell-getpass NSE Script

2011-05-2808:48:26
Patrik Karlsson
nmap.org
275

9.8 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

10 High

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

COMPLETE

Integrity Impact

COMPLETE

Availability Impact

COMPLETE

AV:N/AC:L/Au:N/C:C/I:C/A:C

0.973 High

EPSS

Percentile

99.8%

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.

Script Arguments

ldap-novell-getpass.password

The LDAP password to use when connecting to the server

ldap-novell-getpass.account

The name of the account to retrieve the password for

ldap-novell-getpass.username

The LDAP username to use when connecting to the server

Example 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"'

Script Output

PORT    STATE SERVICE REASON
636/tcp open  ldapssl syn-ack
| ldap-novell-getpass:
|   Account: CN=patrik,OU=security,O=cqure
|_  Password: foobar

Requires


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 <[email protected]>


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

9.8 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

10 High

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

COMPLETE

Integrity Impact

COMPLETE

Availability Impact

COMPLETE

AV:N/AC:L/Au:N/C:C/I:C/A:C

0.973 High

EPSS

Percentile

99.8%

Related for NMAP:LDAP-NOVELL-GETPASS.NSE