1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-23 16:09:02 +00:00

Added support for LDAP over udp to ldap-rootdse.nse.

Also added version detection and information extraction to match the
new LDAP LDAPSearchReq and LDAPSearchReqUDP probes. Closes #362
This commit is contained in:
tomsellers
2016-04-09 21:33:26 +00:00
parent 799048e9fc
commit ee4ed66956
3 changed files with 219 additions and 46 deletions

View File

@@ -6,7 +6,7 @@
--
-- Credit goes out to Martin Swende who provided me with the initial code that got me started writing this.
--
-- Version 0.6
-- Version 0.7
-- Created 01/12/2010 - v0.1 - Created by Patrik Karlsson <patrik@cqure.net>
-- Revised 01/28/2010 - v0.2 - Revised to fit better fit ASN.1 library
-- Revised 02/02/2010 - v0.3 - Revised to fit OO ASN.1 Library
@@ -14,6 +14,7 @@
-- formats
-- Revised 10/29/2011 - v0.5 - Added support for performing wildcard searches via the substring filter.
-- Revised 10/30/2011 - v0.6 - Added support for the ldap extensibleMatch filter type for searches
-- Revised 04/04/2016 - v0.7 - Added support for searchRequest over upd ( udpSearchRequest ) - Tom Sellers
--
local asn1 = require "asn1"
@@ -24,6 +25,7 @@ local os = require "os"
local stdnse = require "stdnse"
local string = require "string"
local table = require "table"
local comm = require "comm"
_ENV = stdnse.module("ldap", stdnse.seeall)
local ldapMessageId = 1
@@ -221,7 +223,7 @@ end
-- @param params table containing at least <code>scope</code>, <code>derefPolicy</code>, <code>baseObject</code>
-- the field <code>maxObjects</code> may also be included to restrict the amount of records returned
-- @return success true or false.
-- @return err string containing error message
-- @return searchResEntries containing results or a string containing error message
function searchRequest( socket, params )
local searchResEntries = { errorMessage="", resultCode = 0}
@@ -334,6 +336,123 @@ function searchRequest( socket, params )
return true, searchResEntries
end
--- Performs an LDAP Search request over UDP
--
-- This function has a concept of softerrors which populates the return tables error information
-- while returning a true status. The reason for this is that LDAP may return a number of records
-- and then finish off with an error like SIZE LIMIT EXCEEDED. We still want to return the records
-- that were received prior to the error. In order to achieve this and not terminating the script
-- by returning a false status a true status is returned together with a table containing all searchentries.
-- This table has the <code>errorMessage</code> and <code>resultCode</code> entries set with the error information.
-- As a <code>try</code> won't catch this error it's up to the script to do so. See ldap-search.nse for an example.
--
-- @param host The host to connect to
-- @param port The port on the host
-- @param params table containing at least <code>scope</code>, <code>derefPolicy</code>, <code>baseObject</code>
-- the field <code>maxObjects</code> may also be included to restrict the amount of records returned
-- @return success true or false.
-- @return searchResEntries containing results or a string containing error message
function udpSearchRequest( host, port, params )
local searchResEntries = { errorMessage="", resultCode = 0}
local catch = function() stdnse.debug1("udpSearchRequest failed") end
local try = nmap.new_try(catch)
local attributes = params.attributes
local request = encode(params.baseObject)
local attrSeq = ''
local requestData, messageSeq, data
local maxObjects = params.maxObjects or -1
local encoder = asn1.ASN1Encoder:new()
local decoder = asn1.ASN1Decoder:new()
encoder:registerTagEncoders(tagEncoder)
decoder:registerTagDecoders(tagDecoder)
request = request .. encode( { _ldap='0A', params.scope } )--scope
request = request .. encode( { _ldap='0A', params.derefPolicy } )--derefpolicy
request = request .. encode( params.sizeLimit or 0)--sizelimit
request = request .. encode( params.timeLimit or 0)--timelimit
request = request .. encode( params.typesOnly or false)--TypesOnly
if params.filter then
request = request .. createFilter( params.filter )
else
request = request .. encode( { _ldaptype='87', "objectclass" } )-- filter : string, presence
end
if attributes~= nil then
for _,attr in ipairs(attributes) do
attrSeq = attrSeq .. encode(attr)
end
end
request = request .. encoder:encodeSeq(attrSeq)
requestData = encodeLDAPOp(APPNO.SearchRequest, true, request)
messageSeq = encode(ldapMessageId)
ldapMessageId = ldapMessageId +1
messageSeq = messageSeq .. requestData
data = encoder:encodeSeq(messageSeq)
local status, response = comm.exchange(host, port, data)
while true do
local len, pos, messageId = 0, 0, -1
local tmp = ""
local _, objectName, attributes, ldapOp
local attributes
local searchResEntry = {}
if ( maxObjects == 0 ) then
break
elseif ( maxObjects > 0 ) then
maxObjects = maxObjects - 1
end
pos, tmp = bin.unpack("C", response, pos)
pos, len = decoder.decodeLength( response, pos )
pos, messageId = decode( response, pos )
pos, tmp = bin.unpack("C", response, pos)
pos, len = decoder.decodeLength( response, pos )
ldapOp = asn1.intToBER( tmp )
searchResEntry = {}
if ldapOp.number == APPNO.SearchResDone then
pos, searchResEntry.resultCode = decode( response, pos )
-- errors may occur after a large amount of response has been received (eg. size limit exceeded)
-- we want to be able to return the response received prior to this error to the user
-- however, we also need to alert the user of the error. This is achieved through "softerrors"
-- softerrors populate the error fields of the table while returning a true status
-- this allows for the caller to output response while still being able to catch the error
if ( searchResEntry.resultCode ~= 0 ) then
local error_msg
pos, searchResEntry.matchedDN = decode( response, pos )
pos, searchResEntry.errorMessage = decode( response, pos )
error_msg = ERROR_MSG[searchResEntry.resultCode]
-- if the table is empty return a hard error
if #searchResEntries == 0 then
return false, string.format("Code: %d|Error: %s|Details: %s", searchResEntry.resultCode, error_msg or "", searchResEntry.errorMessage or "" )
else
searchResEntries.errorMessage = string.format("Code: %d|Error: %s|Details: %s", searchResEntry.resultCode, error_msg or "", searchResEntry.errorMessage or "" )
searchResEntries.resultCode = searchResEntry.resultCode
return true, searchResEntries
end
end
break
end
pos, searchResEntry.objectName = decode( response, pos )
if ldapOp.number == APPNO.SearchResponse then
pos, searchResEntry.attributes = decode( response, pos )
table.insert( searchResEntries, searchResEntry )
end
if response:len() > pos then
response = response:sub(pos)
else
response = ""
end
end
return true, searchResEntries
end
--- Attempts to bind to the server using the credentials given
--