| local comm = require "comm" |
| local ldap = require "ldap" |
| local nmap = require "nmap" |
| local shortport = require "shortport" |
| local stdnse = require "stdnse" |
| local string = require "string" |
| |
| description = [[ |
| Retrieves the LDAP root DSA-specific Entry (DSE) |
| ]] |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| author = "Patrik Karlsson" |
| copyright = "Patrik Karlsson" |
| license = "Same as Nmap--See https://nmap.org/book/man-legal.html" |
| categories = {"discovery", "safe"} |
| dependencies = {"ldap-brute"} |
| |
| |
| |
| DC_FUNCT_ID = {} |
| DC_FUNCT_ID["0"] = "Windows 2000" |
| DC_FUNCT_ID["2"] = "Windows 2003" |
| DC_FUNCT_ID["3"] = "Windows 2008" |
| DC_FUNCT_ID["4"] = "Windows 2008 R2" |
| DC_FUNCT_ID["5"] = "Windows 2012" |
| DC_FUNCT_ID["6"] = "Windows 2012 R2" |
| |
| portrule = shortport.port_or_service({389,636}, {"ldap","ldapssl"},{'tcp','udp'}) |
| |
| function action(host,port) |
| local status, searchResEntries, req, result, opt |
| |
| if port.protocol == 'tcp' then |
| |
| local socket = nmap.new_socket() |
| |
| |
| |
| local ldap_anonymous_bind = "\x30\x0c\x02\x01\x01\x60\x07\x02\x01\x03\x04\x00\x80\x00" |
| local _ |
| socket, _, opt = comm.tryssl( host, port, ldap_anonymous_bind, nil ) |
| |
| if not socket then |
| return |
| end |
| |
| |
| socket:close() |
| status = socket:connect(host, port, opt) |
| socket:set_timeout(10000) |
| |
| |
| |
| |
| req = { baseObject = "", scope = ldap.SCOPE.base, derefPolicy = ldap.DEREFPOLICY.default } |
| status, searchResEntries = ldap.searchRequest( socket, req ) |
| |
| if not status then |
| socket:close() |
| return stdnse.format_output(false, searchResEntries) |
| end |
| |
| |
| if not ldap.extractAttribute( searchResEntries, "namingContexts" ) and |
| not ldap.extractAttribute( searchResEntries, "supportedLDAPVersion" ) then |
| |
| |
| |
| local attribs = {"_domainControllerFunctionality","configurationNamingContext","currentTime","defaultNamingContext", |
| "dnsHostName","domainFunctionality","dsServiceName","forestFunctionality","highestCommittedUSN", |
| "isGlobalCatalogReady","isSynchronized","ldap-get-baseobject","ldapServiceName","namingContexts", |
| "rootDomainNamingContext","schemaNamingContext","serverName","subschemaSubentry", |
| "supportedCapabilities","supportedControl","supportedLDAPPolicies","supportedLDAPVersion", |
| "supportedSASLMechanisms", "altServer", "supportedExtension"} |
| |
| req = { baseObject = "", scope = ldap.SCOPE.base, derefPolicy = ldap.DEREFPOLICY.default, attributes = attribs } |
| status, searchResEntries = ldap.searchRequest( socket, req ) |
| end |
| |
| socket:close() |
| else |
| |
| req = { baseObject = "", scope = ldap.SCOPE.base, derefPolicy = ldap.DEREFPOLICY.default} |
| status, searchResEntries = ldap.udpSearchRequest( host, port, req ) |
| end |
| |
| if not status or not searchResEntries then return stdnse.format_output(false, searchResEntries) end |
| result = ldap.searchResultToTable( searchResEntries ) |
| |
| |
| result.name = "LDAP Results" |
| local scriptResult = stdnse.format_output(true, result ) |
| |
| |
| |
| local serverName = scriptResult:match("serverName: [cC][nN]=([^,]+),[cC][nN]=Servers,[cC][nN]=") |
| if serverName then port.version.hostname = serverName end |
| |
| |
| |
| if string.match(scriptResult,"1.2.840.113556.1.4.800") then |
| port.version.product = 'Microsoft Windows Active Directory LDAP' |
| port.version.name_confidence = 10 |
| |
| |
| if not port.version.ostype or port.version.ostype == 'Windows' then |
| local DC_Func = string.match(scriptResult,"domainControllerFunctionality: (%d)") |
| if DC_FUNCT_ID[DC_Func] then |
| port.version.ostype = DC_FUNCT_ID[DC_Func] |
| else |
| port.version.ostype = 'Windows' |
| stdnse.debug(1,"Unmatched OS lookup for domainControllerFunctionality: %d", DC_Func) |
| end |
| end |
| |
| local siteName = string.match(scriptResult,"serverName: CN=[^,]+,CN=Servers,CN=([^,]+),CN=Sites,") |
| local domainName = string.match(scriptResult,"rootDomainNamingContext: ([^\n]*)") |
| domainName = string.gsub(domainName,",DC=",".") |
| domainName = string.gsub(domainName,"DC=","") |
| if domainName and siteName then |
| port.version.extrainfo = string.format("Domain: %s, Site: %s", domainName, siteName) |
| end |
| end |
| |
| |
| port.version.name = "ldap" |
| nmap.set_port_version(host, port, "hardmatched") |
| nmap.set_port_state(host, port, "open") |
| |
| return scriptResult |
| end |