Linux tsuru-no-tsurugi 5.15.0-186-generic #196-Ubuntu SMP Sat Jun 20 16:09:34 UTC 2026 x86_64
Apache/2.4.52 (Ubuntu)
Server IP : 192.168.0.18 & Your IP : 216.73.217.105
Domains :
Cant Read [ /etc/named.conf ]
User : www-data
Terminal
Auto Root
Create File
Create Folder
Localroot Suggester
Backdoor Destroyer
Readme
/
usr /
share /
nmap /
nselib /
Delete
Unzip
Name
Size
Permission
Date
Action
data
[ DIR ]
drwxr-xr-x
2024-08-29 14:21
afp.lua
71.48
KB
-rw-r--r--
2023-01-13 02:40
ajp.lua
16.77
KB
-rw-r--r--
2023-01-13 02:40
amqp.lua
10.41
KB
-rw-r--r--
2023-01-13 02:40
anyconnect.lua
4.31
KB
-rw-r--r--
2023-01-13 02:40
asn1.lua
14.57
KB
-rw-r--r--
2023-01-13 02:40
base32.lua
7.36
KB
-rw-r--r--
2023-01-13 02:40
base64.lua
5.83
KB
-rw-r--r--
2023-01-13 02:40
bin.lua
12.99
KB
-rw-r--r--
2023-01-13 02:40
bitcoin.lua
18.25
KB
-rw-r--r--
2023-01-13 02:40
bits.lua
2.54
KB
-rw-r--r--
2023-01-13 02:40
bittorrent.lua
35.76
KB
-rw-r--r--
2023-01-13 02:40
bjnp.lua
9.55
KB
-rw-r--r--
2023-01-13 02:40
brute.lua
50.64
KB
-rw-r--r--
2023-01-13 02:40
cassandra.lua
5.48
KB
-rw-r--r--
2023-01-13 02:40
citrixxml.lua
16
KB
-rw-r--r--
2023-01-13 02:40
coap.lua
75.1
KB
-rw-r--r--
2023-01-13 02:40
comm.lua
11.03
KB
-rw-r--r--
2023-01-13 02:40
creds.lua
18.26
KB
-rw-r--r--
2023-01-13 02:40
cvs.lua
3.05
KB
-rw-r--r--
2023-01-13 02:40
datafiles.lua
11.05
KB
-rw-r--r--
2023-01-13 02:40
datetime.lua
7.5
KB
-rw-r--r--
2023-01-13 02:40
dhcp.lua
28.57
KB
-rw-r--r--
2023-01-13 02:40
dhcp6.lua
19.86
KB
-rw-r--r--
2023-01-13 02:40
dns.lua
51.07
KB
-rw-r--r--
2023-01-13 02:40
dnsbl.lua
19.01
KB
-rw-r--r--
2023-01-13 02:40
dnssd.lua
12.61
KB
-rw-r--r--
2023-01-13 02:40
drda.lua
24.08
KB
-rw-r--r--
2023-01-13 02:40
eap.lua
7.54
KB
-rw-r--r--
2023-01-13 02:40
eigrp.lua
12.98
KB
-rw-r--r--
2023-01-13 02:40
formulas.lua
5.68
KB
-rw-r--r--
2023-01-13 02:40
ftp.lua
9.03
KB
-rw-r--r--
2023-01-13 02:40
geoip.lua
1.71
KB
-rw-r--r--
2023-01-13 02:40
giop.lua
18.42
KB
-rw-r--r--
2023-01-13 02:40
gps.lua
3.38
KB
-rw-r--r--
2023-01-13 02:40
http.lua
117.07
KB
-rw-r--r--
2023-01-13 02:40
httpspider.lua
36.65
KB
-rw-r--r--
2023-01-13 02:40
iax2.lua
9.58
KB
-rw-r--r--
2023-01-13 02:40
idna.lua
17.14
KB
-rw-r--r--
2023-01-13 02:40
ike.lua
14.63
KB
-rw-r--r--
2023-01-13 02:40
imap.lua
9.59
KB
-rw-r--r--
2023-01-13 02:40
informix.lua
39.93
KB
-rw-r--r--
2023-01-13 02:40
ipOps.lua
29.06
KB
-rw-r--r--
2023-01-13 02:40
ipmi.lua
8.08
KB
-rw-r--r--
2023-01-13 02:40
ipp.lua
12.57
KB
-rw-r--r--
2023-01-13 02:40
irc.lua
757
B
-rw-r--r--
2023-01-13 02:40
iscsi.lua
21.38
KB
-rw-r--r--
2023-01-13 02:40
isns.lua
14.98
KB
-rw-r--r--
2023-01-13 02:40
jdwp.lua
43.52
KB
-rw-r--r--
2023-01-13 02:40
json.lua
11.62
KB
-rw-r--r--
2023-01-13 02:40
knx.lua
2.42
KB
-rw-r--r--
2023-01-13 02:40
ldap.lua
31.94
KB
-rw-r--r--
2023-01-13 02:40
lfs.luadoc
1.68
KB
-rw-r--r--
2023-01-13 02:40
libssh2-utility.lua
4.69
KB
-rw-r--r--
2023-01-13 02:40
libssh2.luadoc
4.75
KB
-rw-r--r--
2023-01-13 02:40
listop.lua
4.66
KB
-rw-r--r--
2023-01-13 02:40
lpeg-utility.lua
5.64
KB
-rw-r--r--
2023-01-13 02:40
lpeg.luadoc
351
B
-rw-r--r--
2023-01-13 02:40
ls.lua
10.96
KB
-rw-r--r--
2023-01-13 02:40
match.lua
2.05
KB
-rw-r--r--
2023-01-13 02:40
membase.lua
9.93
KB
-rw-r--r--
2023-01-13 02:40
mobileme.lua
8.46
KB
-rw-r--r--
2023-01-13 02:40
mongodb.lua
21.3
KB
-rw-r--r--
2023-01-13 02:40
mqtt.lua
28.86
KB
-rw-r--r--
2023-01-13 02:40
msrpc.lua
190.07
KB
-rw-r--r--
2023-01-13 02:40
msrpcperformance.lua
29.65
KB
-rw-r--r--
2023-01-13 02:40
msrpctypes.lua
168.52
KB
-rw-r--r--
2023-01-13 02:40
mssql.lua
109.43
KB
-rw-r--r--
2023-01-13 02:40
multicast.lua
5.97
KB
-rw-r--r--
2023-01-13 02:40
mysql.lua
16.03
KB
-rw-r--r--
2023-01-13 02:40
natpmp.lua
5.11
KB
-rw-r--r--
2023-01-13 02:40
nbd.lua
16.04
KB
-rw-r--r--
2023-01-13 02:40
ncp.lua
35.64
KB
-rw-r--r--
2023-01-13 02:40
ndmp.lua
11.43
KB
-rw-r--r--
2023-01-13 02:40
netbios.lua
14.44
KB
-rw-r--r--
2023-01-13 02:40
nmap.luadoc
40.34
KB
-rw-r--r--
2023-01-13 02:40
nrpc.lua
4.46
KB
-rw-r--r--
2023-01-13 02:40
nsedebug.lua
3.49
KB
-rw-r--r--
2023-01-13 02:40
omp2.lua
4.78
KB
-rw-r--r--
2023-01-13 02:40
oops.lua
3.61
KB
-rw-r--r--
2023-01-13 02:40
openssl.luadoc
7.5
KB
-rw-r--r--
2023-01-13 02:40
ospf.lua
15.29
KB
-rw-r--r--
2023-01-13 02:40
packet.lua
36.18
KB
-rw-r--r--
2023-01-13 02:40
pcre.luadoc
6.79
KB
-rw-r--r--
2023-01-13 02:40
pgsql.lua
20.6
KB
-rw-r--r--
2023-01-13 02:40
pop3.lua
5.74
KB
-rw-r--r--
2023-01-13 02:40
pppoe.lua
29.42
KB
-rw-r--r--
2023-01-13 02:40
proxy.lua
11.37
KB
-rw-r--r--
2023-01-13 02:40
punycode.lua
11.5
KB
-rw-r--r--
2023-01-13 02:40
rand.lua
2.7
KB
-rw-r--r--
2023-01-13 02:40
rdp.lua
14.73
KB
-rw-r--r--
2023-01-13 02:40
re.lua
8.22
KB
-rw-r--r--
2023-01-13 02:40
redis.lua
3.61
KB
-rw-r--r--
2023-01-13 02:40
rmi.lua
47.77
KB
-rw-r--r--
2023-01-13 02:40
rpc.lua
107.64
KB
-rw-r--r--
2023-01-13 02:40
rpcap.lua
11.08
KB
-rw-r--r--
2023-01-13 02:40
rsync.lua
5.22
KB
-rw-r--r--
2023-01-13 02:40
rtsp.lua
8.62
KB
-rw-r--r--
2023-01-13 02:40
sasl.lua
16.51
KB
-rw-r--r--
2023-01-13 02:40
shortport.lua
11.99
KB
-rw-r--r--
2023-01-13 02:40
sip.lua
30.07
KB
-rw-r--r--
2023-01-13 02:40
slaxml.lua
17.9
KB
-rw-r--r--
2023-01-13 02:40
smb.lua
173.22
KB
-rw-r--r--
2023-01-13 02:40
smb2.lua
15.34
KB
-rw-r--r--
2023-01-13 02:40
smbauth.lua
37.33
KB
-rw-r--r--
2023-01-13 02:40
smtp.lua
19.85
KB
-rw-r--r--
2023-01-13 02:40
snmp.lua
16
KB
-rw-r--r--
2023-01-13 02:40
socks.lua
8.25
KB
-rw-r--r--
2023-01-13 02:40
srvloc.lua
10.99
KB
-rw-r--r--
2023-01-13 02:40
ssh1.lua
9.15
KB
-rw-r--r--
2023-01-13 02:40
ssh2.lua
11.73
KB
-rw-r--r--
2023-01-13 02:40
sslcert.lua
33.35
KB
-rw-r--r--
2023-01-13 02:40
sslv2.lua
9.68
KB
-rw-r--r--
2023-01-13 02:40
stdnse.lua
32.64
KB
-rw-r--r--
2023-01-13 02:40
strbuf.lua
4.52
KB
-rw-r--r--
2023-01-13 02:40
strict.lua
2.53
KB
-rw-r--r--
2023-01-13 02:40
stringaux.lua
4.32
KB
-rw-r--r--
2023-01-13 02:40
stun.lua
11.08
KB
-rw-r--r--
2023-01-13 02:40
tab.lua
3.35
KB
-rw-r--r--
2023-01-13 02:40
tableaux.lua
2.06
KB
-rw-r--r--
2023-01-13 02:40
target.lua
3.93
KB
-rw-r--r--
2023-01-13 02:40
tftp.lua
9.41
KB
-rw-r--r--
2023-01-13 02:40
tls.lua
58.05
KB
-rw-r--r--
2023-01-13 02:40
tn3270.lua
46.99
KB
-rw-r--r--
2023-01-13 02:40
tns.lua
64.16
KB
-rw-r--r--
2023-01-13 02:40
unicode.lua
14.23
KB
-rw-r--r--
2023-01-13 02:40
unittest.lua
12.41
KB
-rw-r--r--
2023-01-13 02:40
unpwdb.lua
10.42
KB
-rw-r--r--
2023-01-13 02:40
upnp.lua
11.18
KB
-rw-r--r--
2023-01-13 02:40
url.lua
16.7
KB
-rw-r--r--
2023-01-13 02:40
versant.lua
8.58
KB
-rw-r--r--
2023-01-13 02:40
vnc.lua
25.54
KB
-rw-r--r--
2023-01-13 02:40
vulns.lua
76.45
KB
-rw-r--r--
2023-01-13 02:40
vuzedht.lua
16.21
KB
-rw-r--r--
2023-01-13 02:40
wsdd.lua
11.98
KB
-rw-r--r--
2023-01-13 02:40
xdmcp.lua
12.09
KB
-rw-r--r--
2023-01-13 02:40
xmpp.lua
15.91
KB
-rw-r--r--
2023-01-13 02:40
zlib.luadoc
4.88
KB
-rw-r--r--
2023-01-13 02:40
Save
Rename
--- -- Library methods for handling LDAP. -- -- @author Patrik Karlsson -- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html -- -- Credit goes out to Martin Swende who provided me with the initial code that got me started writing this. -- -- Version 0.8 -- 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 -- Revised 09/05/2011 - v0.4 - Revised to include support for writing output to file, added decoding certain time -- 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 -- Revised 07/11/2017 - v0.8 - Added support for decoding the objectSID Active Directory attribute - Tom Sellers -- local asn1 = require "asn1" local datetime = require "datetime" local io = require "io" local nmap = require "nmap" local os = require "os" local stdnse = require "stdnse" local string = require "string" local stringaux = require "stringaux" local table = require "table" local comm = require "comm" _ENV = stdnse.module("ldap", stdnse.seeall) local ldapMessageId = 1 ERROR_MSG = {} ERROR_MSG[1] = "Initialization of LDAP library failed." ERROR_MSG[4] = "Size limit exceeded." ERROR_MSG[13] = "Confidentiality required" ERROR_MSG[32] = "No such object" ERROR_MSG[34] = "Invalid DN" ERROR_MSG[49] = "The supplied credential is invalid." ERRORS = { LDAP_SUCCESS = 0, LDAP_SIZELIMIT_EXCEEDED = 4 } --- Application constants -- @class table -- @name APPNO APPNO = { BindRequest = 0, BindResponse = 1, UnbindRequest = 2, SearchRequest = 3, SearchResponse = 4, SearchResDone = 5 } -- Filter operation constants FILTER = { _and = 0, _or = 1, _not = 2, equalityMatch = 3, substrings = 4, greaterOrEqual = 5, lessOrEqual = 6, present = 7, approxMatch = 8, extensibleMatch = 9 } -- Scope constants SCOPE = { base=0, one=1, sub= 2, children=3, default = 0 } -- Deref policy constants DEREFPOLICY = { never=0, searching=1, finding = 2, always=3, default = 0 } -- LDAP specific tag encoders local tagEncoder = {} tagEncoder['table'] = function(self, val) if (val._ldap == '\x0A') then local ival = self.encodeInt(val[1]) local len = self.encodeLength(#ival) return val._ldap .. len .. ival end if (val._ldaptype) then local len if val[1] == nil or #val[1] == 0 then return val._ldaptype .. '\0' else len = self.encodeLength(#val[1]) return val._ldaptype .. len .. val[1] end end local encVal = "" for _, v in ipairs(val) do encVal = encVal .. encode(v) -- todo: buffer? end local tableType = val._snmp or "\x30" return tableType .. self.encodeLength(#encVal) .. encVal end --- -- Encodes a given value according to ASN.1 basic encoding rules for SNMP -- packet creation. -- @param val Value to be encoded. -- @return Encoded value. function encode(val) local encoder = asn1.ASN1Encoder:new() local encValue encoder:registerTagEncoders(tagEncoder) encValue = encoder:encode(val) if encValue then return encValue end return '' end -- LDAP specific tag decoders local tagDecoder = {} tagDecoder["\x0A"] = function( self, encStr, elen, pos ) return self.decodeInt(encStr, elen, pos) end tagDecoder["\x8A"] = function( self, encStr, elen, pos ) return string.unpack("c" .. elen, encStr, pos) end -- null decoder tagDecoder["\x31"] = function( self, encStr, elen, pos ) return nil, pos end --- -- Decodes an LDAP packet or a part of it according to ASN.1 basic encoding -- rules. -- @param encStr Encoded string. -- @param pos Current position in the string. -- @return The decoded value(s). -- @return The position after decoding function decode(encStr, pos) -- register the LDAP specific tag decoders local decoder = asn1.ASN1Decoder:new() decoder:registerTagDecoders( tagDecoder ) return decoder:decode( encStr, pos ) end --- -- Decodes a sequence according to ASN.1 basic encoding rules. -- @param encStr Encoded string. -- @param len Length of sequence in bytes. -- @param pos Current position in the string. -- @return The decoded sequence as a table. -- @return The position after decoding. local function decodeSeq(encStr, len, pos) local seq = {} local sPos = 1 if #encStr - pos + 1 < len then return seq, nil end local sStr, newpos = string.unpack("c" .. len, encStr, pos) while (sPos < len) do local newSeq newSeq, sPos = decode(sStr, sPos) table.insert(seq, newSeq) end return seq, newpos end -- Encodes an LDAP Application operation and its data as a sequence -- -- @param appno LDAP application number -- @see APPNO -- @param isConstructed boolean true if constructed, false if primitive -- @param data string containing the LDAP operation content -- @return string containing the encoded LDAP operation function encodeLDAPOp( appno, isConstructed, data ) local encoded_str = "" local asn1_type = asn1.BERtoInt( asn1.BERCLASS.Application, isConstructed, appno ) encoded_str = encode( { _ldaptype = string.pack("B", asn1_type), data } ) return encoded_str end --- Performs an LDAP Search request -- -- 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 socket socket already connected to the ldap server -- @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 searchRequest( socket, params ) local searchResEntries = { errorMessage="", resultCode = 0} local catch = function() socket:close() stdnse.debug1("SearchRequest 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='\x0A', params.scope } )--scope request = request .. encode( { _ldap='\x0A', 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='\x87', "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) try( socket:send( data ) ) data = "" while true do local len, pos, messageId = 0, 2, -1 local tmp = "" local _, objectName, attributes, ldapOp local attributes local searchResEntry = {} if ( maxObjects == 0 ) then break elseif ( maxObjects > 0 ) then maxObjects = maxObjects - 1 end if data:len() > 6 then len, pos = decoder.decodeLength( data, pos ) else data = data .. try( socket:receive() ) len, pos = decoder.decodeLength( data, pos ) end -- pos should be at the right position regardless if length is specified in 1 or 2 bytes while ( len + pos - 1 > data:len() ) do data = data .. try( socket:receive() ) end messageId, pos = decode( data, pos ) tmp, pos = string.unpack("B", data, pos) len, pos = decoder.decodeLength( data, pos ) ldapOp = asn1.intToBER( tmp ) searchResEntry = {} if ldapOp.number == APPNO.SearchResDone then searchResEntry.resultCode, pos = decode( data, pos ) -- errors may occur after a large amount of data has been received (eg. size limit exceeded) -- we want to be able to return the data 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 data while still being able to catch the error if ( searchResEntry.resultCode ~= 0 ) then local error_msg searchResEntry.matchedDN, pos = decode( data, pos ) searchResEntry.errorMessage, pos = decode( data, 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 searchResEntry.objectName, pos = decode( data, pos ) if ldapOp.number == APPNO.SearchResponse then searchResEntry.attributes, pos = decode( data, pos ) table.insert( searchResEntries, searchResEntry ) end if data:len() > pos then data = data:sub(pos) else data = "" end end 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='\x0A', params.scope } )--scope request = request .. encode( { _ldap='\x0A', 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='\x87', "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 tmp, pos = string.unpack("B", response, pos) len, pos = decoder.decodeLength( response, pos ) messageId, pos = decode( response, pos ) tmp, pos = string.unpack("B", response, pos) len, pos = decoder.decodeLength( response, pos ) ldapOp = asn1.intToBER( tmp ) searchResEntry = {} if ldapOp.number == APPNO.SearchResDone then searchResEntry.resultCode, pos = 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 searchResEntry.matchedDN, pos = decode( response, pos ) searchResEntry.errorMessage, pos = 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 searchResEntry.objectName, pos = decode( response, pos ) if ldapOp.number == APPNO.SearchResponse then searchResEntry.attributes, pos = 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 -- -- @param socket socket already connected to the ldap server -- @param params table containing <code>version</code>, <code>username</code> and <code>password</code> -- @return success true or false -- @return err string containing error message function bindRequest( socket, params ) local catch = function() socket:close() stdnse.debug1("bindRequest failed") end local try = nmap.new_try(catch) local ldapAuth = encode( { _ldaptype = '\x80', params.password } ) local bindReq = encode( params.version ) .. encode( params.username ) .. ldapAuth local ldapMsg = encode(ldapMessageId) .. encodeLDAPOp( APPNO.BindRequest, true, bindReq ) local packet local pos, packet_len, resultCode, tmp, len, _ local response = {} local encoder = asn1.ASN1Encoder:new() local decoder = asn1.ASN1Decoder:new() encoder:registerTagEncoders(tagEncoder) decoder:registerTagDecoders(tagDecoder) packet = encoder:encodeSeq( ldapMsg ) ldapMessageId = ldapMessageId +1 try( socket:send( packet ) ) packet = try( socket:receive() ) packet_len, pos = decoder.decodeLength( packet, 2 ) response.messageID, pos = decode( packet, pos ) tmp, pos = string.unpack("B", packet, pos) len, pos = decoder.decodeLength( packet, pos ) response.protocolOp = asn1.intToBER( tmp ) if response.protocolOp.number ~= APPNO.BindResponse then return false, string.format("Received incorrect Op in packet: %d, expected %d", response.protocolOp.number, APPNO.BindResponse) end response.resultCode, pos = decode( packet, pos ) if ( response.resultCode ~= 0 ) then local error_msg response.matchedDN, pos = decode( packet, pos ) response.errorMessage, pos = decode( packet, pos ) error_msg = ERROR_MSG[response.resultCode] return false, string.format("\n Error: %s\n Details: %s", error_msg or "Unknown error occurred (code: " .. response.resultCode .. ")", response.errorMessage or "" ) else return true, "Success" end end --- Performs an LDAP Unbind -- -- @param socket socket already connected to the ldap server -- @return success true or false -- @return err string containing error message function unbindRequest( socket ) local ldapMsg, packet local catch = function() socket:close() stdnse.debug1("bindRequest failed") end local try = nmap.new_try(catch) local encoder = asn1.ASN1Encoder:new() encoder:registerTagEncoders(tagEncoder) ldapMessageId = ldapMessageId +1 ldapMsg = encode( ldapMessageId ) .. encodeLDAPOp( APPNO.UnbindRequest, false, nil) packet = encoder:encodeSeq( ldapMsg ) try( socket:send( packet ) ) return true, "" end --- Creates an ASN1 structure from a filter table -- -- @param filter table containing the filter to be created -- @return string containing the ASN1 byte sequence function createFilter( filter ) local asn1_type = asn1.BERtoInt( asn1.BERCLASS.ContextSpecific, true, filter.op ) local filter_str = "" if type(filter.val) == 'table' then for _, v in ipairs( filter.val ) do filter_str = filter_str .. createFilter( v ) end else local obj = encode( filter.obj ) local val = '' if ( filter.op == FILTER['substrings'] ) then local tmptable = stringaux.strsplit('*', filter.val) local tmp_result = '' if (#tmptable <= 1 ) then -- 0x81 = 10000001 = 10 0 00001 -- hex binary Context Primitive value Field: Sequence Value: 1 (any / any position in string) tmp_result = string.pack('Bs1', 0x81, filter.val) else for indexval, substr in ipairs(tmptable) do if (indexval == 1) and (substr ~= '') then -- 0x81 = 10000000 = 10 0 00000 -- hex binary Context Primitive value Field: Sequence Value: 0 (initial / match at start of string) tmp_result = '\x80' .. string.char(#substr) .. substr end if (indexval ~= #tmptable) and (indexval ~= 1) and (substr ~= '') then -- 0x81 = 10000001 = 10 0 00001 -- hex binary Context Primitive value Field: Sequence Value: 1 (any / match in any position in string) tmp_result = tmp_result .. string.pack('Bs1', 0x81, substr) end if (indexval == #tmptable) and (substr ~= '') then -- 0x82 = 10000010 = 10 0 00010 -- hex binary Context Primitive value Field: Sequence Value: 2 (final / match at end of string) tmp_result = tmp_result .. string.pack('Bs1', 0x82, substr) end end end val = asn1.ASN1Encoder:encodeSeq( tmp_result ) elseif ( filter.op == FILTER['extensibleMatch'] ) then local tmptable = stringaux.strsplit(':=', filter.val) local tmp_result = '' local OID, bitmask if ( tmptable[1] ~= nil ) then OID = tmptable[1] else return false, ("ERROR: Invalid extensibleMatch query format") end if ( tmptable[2] ~= nil ) then bitmask = tmptable[2] else return false, ("ERROR: Invalid extensibleMatch query format") end tmp_result = string.pack('Bs1 Bs1 Bs1 Bs1', -- Format and create matchingRule using OID -- 0x81 = 10000001 = 10 0 00001 -- hex binary Context Primitive value Field: matchingRule Value: 1 0x81, OID, -- Format and create type using ldap attribute -- 0x82 = 10000010 = 10 0 00010 -- hex binary Context Primitive value Field: Type Value: 2 0x82, filter.obj, -- Format and create matchValue using bitmask -- 0x83 = 10000011 = 10 0 00011 -- hex binary Context Primitive value Field: matchValue Value: 3 0x83, bitmask, -- Format and create dnAttributes, defaulting to false -- 0x84 = 10000100 = 10 0 00100 -- hex binary Context Primitive value Field: dnAttributes Value: 4 -- 0x00 = boolean value, in this case false 0x84, '\x00') -- Format the overall extensibleMatch block -- 0xa9 = 10101001 = 10 1 01001 -- hex binary Context Constructed Field: Filter Value: 9 (extensibleMatch) return '\xa9' .. asn1.ASN1Encoder.encodeLength(#tmp_result) .. tmp_result else val = encode( filter.val ) end filter_str = filter_str .. obj .. val end return encode( { _ldaptype=string.pack("B", asn1_type), filter_str } ) end --- Converts a search result as received from searchRequest to a "result" table -- -- Does some limited decoding of LDAP attributes -- -- TODO: Add decoding of missing attributes -- TODO: Add decoding of userParameters -- TODO: Add decoding of loginHours -- -- @param searchEntries table as returned from searchRequest -- @return table suitable for <code>stdnse.format_output</code> function searchResultToTable( searchEntries ) local result = {} for _, v in ipairs( searchEntries ) do local result_part = {} if v.objectName and v.objectName:len() > 0 then result_part.name = string.format("dn: %s", v.objectName) else result_part.name = "<ROOT>" end local attribs = {} if ( v.attributes ~= nil ) then for _, attrib in ipairs( v.attributes ) do for i=2, #attrib do -- do some additional Windows decoding if ( attrib[1] == "objectSid" ) then table.insert( attribs, string.format( "%s: %s", attrib[1], convertObjectSid( attrib[i] ) ) ) elseif ( attrib[1] == "objectGUID") then local o = {string.unpack(("B"):rep(16), attrib[i] )} table.insert( attribs, string.format( "%s: %x%x%x%x-%x%x-%x%x-%x%x-%x%x%x%x%x%x", attrib[1], o[4], o[3], o[2], o[1], table.unpack(o, 5, 16))) elseif ( attrib[1] == "lastLogon" or attrib[1] == "lastLogonTimestamp" or attrib[1] == "pwdLastSet" or attrib[1] == "accountExpires" or attrib[1] == "badPasswordTime" ) then table.insert( attribs, string.format( "%s: %s", attrib[1], convertADTimeStamp(attrib[i]) ) ) elseif ( attrib[1] == "whenChanged" or attrib[1] == "whenCreated" or attrib[1] == "dSCorePropagationData" ) then table.insert( attribs, string.format( "%s: %s", attrib[1], convertZuluTimeStamp(attrib[i]) ) ) else table.insert( attribs, string.format( "%s: %s", attrib[1], attrib[i] ) ) end end end table.insert( result_part, attribs ) end table.insert( result, result_part ) end return result end --- Saves a search result as received from searchRequest to a file -- -- Does some limited decoding of LDAP attributes -- -- TODO: Add decoding of missing attributes -- TODO: Add decoding of userParameters -- TODO: Add decoding of loginHours -- -- @param searchEntries table as returned from searchRequest -- @param filename the name of a save to save results to -- @return table suitable for <code>stdnse.format_output</code> function searchResultToFile( searchEntries, filename ) local f = io.open( filename, "w") if ( not(f) ) then return false, ("ERROR: Failed to open file (%s)"):format(filename) end -- Build table structure. Using a multi pass approach ( build table then populate table ) -- because the objects returned may not necessarily have the same number of attributes -- making single pass CSV output generation problematic. -- Unfortunately the searchEntries table passed to this function is not organized in a -- way that make particular attributes for a given hostname directly addressable. -- -- At some point restructuring the searchEntries table may be a good optimization target -- build table of attributes local attrib_table = {} for _, v in ipairs( searchEntries ) do if ( v.attributes ~= nil ) then for _, attrib in ipairs( v.attributes ) do for i=2, #attrib do if ( attrib_table[attrib[1]] == nil ) then attrib_table[attrib[1]] = '' end end end end end -- build table of hosts local host_table = {} for _, v in ipairs( searchEntries ) do if v.objectName and v.objectName:len() > 0 then local host = {} if v.objectName and v.objectName:len() > 0 then -- use a copy of the table here, assigning attrib_table into host_table -- links the values so setting it for one host changes the specific attribute -- values for all hosts. host_table[v.objectName] = {attributes = copyTable(attrib_table) } end end end -- populate the host table with values for each attribute that has valid data for _, v in ipairs( searchEntries ) do if ( v.attributes ~= nil ) then for _, attrib in ipairs( v.attributes ) do for i=2, #attrib do -- do some additional Windows decoding if ( attrib[1] == "objectSid" ) then host_table[string.format("%s", v.objectName)].attributes[attrib[1]] = string.format( "%s", convertObjectSid(attrib[i])) elseif ( attrib[1] == "objectGUID") then local o = {string.unpack(("B"):rep(16), attrib[i] )} host_table[string.format("%s", v.objectName)].attributes[attrib[1]] = string.format( "%s: %x%x%x%x-%x%x-%x%x-%x%x-%x%x%x%x%x%x", attrib[1], o[4], o[3], o[2], o[1], table.unpack(o, 5, 16)) elseif ( attrib[1] == "lastLogon" or attrib[1] == "lastLogonTimestamp" or attrib[1] == "pwdLastSet" or attrib[1] == "accountExpires" or attrib[1] == "badPasswordTime" ) then host_table[string.format("%s", v.objectName)].attributes[attrib[1]] = convertADTimeStamp(attrib[i]) elseif ( attrib[1] == "whenChanged" or attrib[1] == "whenCreated" or attrib[1] == "dSCorePropagationData" ) then host_table[string.format("%s", v.objectName)].attributes[attrib[1]] = convertZuluTimeStamp(attrib[i]) else host_table[v.objectName].attributes[attrib[1]] = string.format( "%s", attrib[i] ) end end end end end -- write the new, fully populated table out to CSV -- initialize header row local output = "\"name\"" for attribute, value in pairs(attrib_table) do output = output .. ",\"" .. attribute .. "\"" end output = output .. "\n" -- gather host data from fields, add to output. for name, attribs in pairs(host_table) do output = output .. "\"" .. name .. "\"" local host_attribs = attribs.attributes for attribute, value in pairs(attrib_table) do output = output .. ",\"" .. host_attribs[attribute] .. "\"" end output = output .. "\n" end -- write the output to file if ( not(f:write( output .."\n" ) ) ) then f:close() return false, ("ERROR: Failed to write file (%s)"):format(filename) end f:close() return true end --- Extract naming context from a search response -- -- @param searchEntries table containing searchEntries from a searchResponse -- @param attributeName string containing the attribute to extract -- @return table containing the attribute values function extractAttribute( searchEntries, attributeName ) local attributeTbl = {} for _, v in ipairs( searchEntries ) do if ( v.attributes ~= nil ) then for _, attrib in ipairs( v.attributes ) do local attribType = attrib[1] for i=2, #attrib do if ( attribType:upper() == attributeName:upper() ) then table.insert( attributeTbl, attrib[i]) end end end end end return ( #attributeTbl > 0 and attributeTbl or nil ) end --- Convert Microsoft Active Directory timestamp format to a human readable form -- These values store time values in 100 nanoseconds segments from 01-Jan-1601 -- -- @param timestamp Microsoft Active Directory timestamp value -- @return string containing human readable form function convertADTimeStamp(timestamp) local result = 0 local base_time = tonumber(os.time({year=1601, month=1, day=1, hour=0, minute=0, sec =0})) timestamp = tonumber(timestamp) if (timestamp and timestamp > 0) then -- The result value was 3036 seconds off what Microsoft says it should be. -- I have been unable to find an explanation for this, and have resorted to -- manually adjusting the formula. result = ( timestamp // 10000000 ) - 3036 result = result + base_time result = datetime.format_timestamp(result, 0) else result = 'Never' end return result end --- Converts a non-delimited Zulu timestamp format to a human readable form -- For example 20110904003302.0Z becomes 2001/09/04 00:33:02 UTC -- -- -- @param timestamp in Zulu format without separators -- @return string containing human readable form function convertZuluTimeStamp(timestamp) if ( type(timestamp) == 'string' and string.sub(timestamp,-3) == '.0Z' ) then local year = string.sub(timestamp,1,4) local month = string.sub(timestamp,5,6) local day = string.sub(timestamp,7,8) local hour = string.sub(timestamp,9,10) local mins = string.sub(timestamp,11,12) local secs = string.sub(timestamp,13,14) local result = year .. "/" .. month .. "/" .. day .. " " .. hour .. ":" .. mins .. ":" .. secs .. " UTC" return result else return 'Invalid date format' end end --- Converts the objectSid Active Directory attribute -- from hex to a human readable string -- -- Example: 1-5-21-542885397-2936741293-3965599772-500 -- -- @param hex string form of objectSid from LDAP response -- @return string containing human readable form function convertObjectSid(data) local pos, revision, auth, sub_auth_size, sub_auth, result revision, pos = string.unpack('I1', data, 1) sub_auth_size, pos = string.unpack('I1', data, pos) auth, pos = string.unpack('>I6', data, pos) sub_auth = '' local tmp local cnt = 0 while (cnt < sub_auth_size) do tmp, pos = string.unpack('<I4', data, pos) sub_auth = sub_auth .. '-' .. tmp cnt = cnt + 1 end result = revision .. '-' .. auth .. sub_auth return result end --- Creates a copy of a table -- -- -- @param targetTable table object to copy -- @return table object containing copy of original function copyTable(targetTable) local temp = { } for key, val in pairs(targetTable) do temp[key] = val end return setmetatable(temp, getmetatable(targetTable)) end return _ENV;