Microsoft Security Descriptor(SID)Attributes
On this web page we want to have a look at the directory attributes which are used by Microsoft to store the so called Security Identifiers (SID). These SIDs plays an important role in delegating and granting permissions and in authentication of trustee holders against the system. Examples for such attributes:
| objectSid | User objects Computer objects Group objects |
The Security ID which unambiguously identifies the regarding security principal (=potential permission trustee). |
| sIDHistory | User objects Computer objects Group objects |
Only exists for migrated objects, which was copied (for example with ADMT) from a foreign domain. The list of the security IDs in the sIDHistory attribute allows the regarding account or group to access resources with their old identities - all former permissions are retained this way. |
| tokenGroups | User objects Computer objects |
The SIDs of all groups of the same AD forest where the user is member of. This attribute takes account even of nested groups, that means that both direct and transitive group memberships are included here. A very interesting attribute which can not be searched for, because unfortunately it's a Constructed Attribute. |
| tokenGroupsGlobalAndUniversal | User objects Computer objects |
A subset of the tokenGroups attribute. Only the global and universal group SIDs are included. |
| tokenGroupsNoGCAcceptable | User objects Computer objects |
A subset of the tokenGroups attribute. Only the group SIDs whose evaluation don't require a global catalog are included. |
The basic LDAP attribute data type of these attributes is a Microsoft proprietary LDAP attribute syntax named String(Sid) - basically this is binary data which have to be handled specifically even if you just want to read it from the directory in scripts. You can get more information about this in the SelfADSI tutorial article Object Attributes of type 'Octet String'.
The internal structure of a SID value is described in this Microsoft document: Microsoft Data Type Reference [MSDTY].
1-Byte Revision: An 8-bit unsigned integer that specifies the revision level of the SID structure. This value is always set to 1.
1-Byte SubAuthority Count: An 8-bit unsigned integer that specifies the number of elements in the SubAuthority array (these are a kind of Sub IDs in the SID). The maximum number of these sub authorities allowed is 15.
6-Byte IdentifierAuthority: This 6 bytes indicates the so called Authority Identifier, which indicates the authority under which the SID was created. The following values has been defined so far:
0x00, 0x00, 0x00, 0x00, 0x00, 0x00 : NULL_SID_AUTHORITY
0x00, 0x00, 0x00, 0x00, 0x00, 0x01 : WORLD_SID_AUTHORITY
0x00, 0x00, 0x00, 0x00, 0x00, 0x02 : LOCAL_SID_AUTHORITY
0x00, 0x00, 0x00, 0x00, 0x00, 0x03 : CREATOR_SID_AUTHORITY
0x00, 0x00, 0x00, 0x00, 0x00, 0x04 : NON_UNIQUE_AUTHORITY
0x00, 0x00, 0x00, 0x00, 0x00, 0x05 : NT_AUTHORITY
0x00, 0x00, 0x00, 0x00, 0x00, 0x06 : SECURITY_MANDATORY_LABEL_AUTHORITY
Variable-Length SubAuthority: A variable length array of one or several (the exact count is stored at the beginning of the SID) sub authorities. A sub authority is represented by a unsigned 32-bit integers that is stored in reverse byte order (Big Endian). The SID of a normal Active Directory account consists of 5 sub authorities, the first one has always the value 21, the next three blocks are the SID-'base' of the domain, and the last sub authority block is finally the relative ID of the account (the RID) which is unique in its domain. An example of such an account SID:

Although SIDs are binary values with a variable length (normal Active Directory SIDs have a length of 28 byte), they are normally displayed as a string with a specific notation syntax, for example like these:
S-1-5-7 (Wellknown SID for 'Anonymous Logon')
S-1-5-21-1621763826-2590103247-2238570322-1113
This display form is called SDDL (Security Descriptor Definition Language). The rule for the SDDL notation of a SID is
S-1-IdentifierAuthority-SubAuthority1-SubAuthority2-...-SubAuthorityn
Every Active Directory Configuration Partition stores information about the wellknown standard SIDs which can be used by the regarding system. The according objects are in the CN=WellKnown Security Principals,CN=Configuration,DC=.... organizational unit. This is structure of the wellknown SID 'Everyone' (S-1-1-0):

And this is the structure of the wellknown sid 'System' (S-1-5-18):

Converting binary SID data to readable SDDL strings
If you want to read SID attributes from directory with an LDAP script, there is one question: How to convert the binary data into a readable SDDL string?
As we've seen in the discussion about the data structure of a SID attribute, we face two obstacles to overcome. First the pure binary data has to be converted into a hex string: This is performed by the function OctetToHexStr. Then we can convert this into a SDDL notation by transferring the authority blocks according to the correct byte order to long integer values. This is done with the function HexStrToSID. We need a few additional helper functions here which can be found at the end of the script:
'you have to use other names and credentials from you own environment here!
Set obj = GetObject("LDAP://cn=Foeckeler,cn=Users,dc=cerrotorre,dc=de")
pureSidData = OctetToHexStr(obj.objectSid)
sDDLSidStr = HexStrToSID(pureSidData)
WScript.Echo obj.cn
WScript.Echo pureSidData
WScript.Echo sDDLSidStr
Function HexStrToSID(strSid)
'converts a raw SID hex string to the according SID string (SDDL)
Dim i, data, offset
ReDim data(Len(strSid)/2 - 1)
For i = 0 To UBound(data)
data(i) = CInt("&H" & Mid(strSid, 2*i + 1, 2))
Next
HexStrToSID = "S-" & data(0) & "-" & Byte6ToLong(data(2), data(3), data(4), data(5), data(6), data(7))
blockCount = data(1)
For i = 0 To blockCount - 1
offset = 8 + 4*i
HexStrToSID = HexStrToSID & "-" & Byte4ToLong(data(offset+3), data(offset+2), data(offset+1), data(offset))
Next
End Function
'_________________________________________________________________________________________ helper functions
Function OctetToHexStr(var_octet)
'converts pure binary data to a string with the according hexadecimal values
OctetToHexStr = ""
For n = 1 To lenb(var_octet)
OctetToHexStr = OctetToHexStr & Right("0" & hex(ascb(midb(var_octet, n, 1))), 2)
Next
End Function
Function Byte4ToLong(ByVal b1, ByVal b2, ByVal b3, ByVal b4)
'converts 4 bytes to the according lang integer value
Byte4ToLong = b1
Byte4ToLong = Byte4ToLong * 256 + b2
Byte4ToLong = Byte4ToLong * 256 + b3
Byte4ToLong = Byte4ToLong * 256 + b4
End Function
Function Byte6ToLong(ByVal b1, ByVal b2, ByVal b3, ByVal b4, ByVal b5, ByVal b6)
'converts 6 bytes to the according lang integer value
Byte6ToLong = b1
Byte6ToLong = Byte6ToLong * 256 + b2
Byte6ToLong = Byte6ToLong * 256 + b3
Byte6ToLong = Byte6ToLong * 256 + b4
Byte6ToLong = Byte6ToLong * 256 + b5
Byte6ToLong = Byte6ToLong * 256 + b6
End Function
Converting readable SID Strings to the according hex string
Now the other way round: How to convert a given SID string (in readable SDDL form) into the according binary data (as a hex string). We need this binary data for example when we want to build an LDAP filter which can search for a certain SID attribute, or if we want to write directly into any ACL structure.
Now we need functions which can built the correct hexadecimal strings from given long integer values. This helper functions are called in the central conversion function SIDToHexStr then:
wellKnownSIDEveryOne = "S-1-1-0"
wellKnownSIDCreatorOwner = "S-1-3-0"
wellKnownSIDAnonymous = "S-1-5-7"
wellKnownSIDAuthenticatedUsers= "S-1-5-11"
wellKnownSIDSystem = "S-1-5-18"
wellKnownSIDExampleUser = "S-1-5-21-120346000-1731507311-1141323324-1104"
WScript.Echo wellKnownSIDEveryOne & " : " & SIDToHexStr(wellKnownSIDEveryOne)
WScript.Echo wellKnownSIDCreatorOwner & " : " & SIDToHexStr(wellKnownSIDCreatorOwner)
WScript.Echo wellKnownSIDAnonymous & " : " & SIDToHexStr(wellKnownSIDAnonymous)
WScript.Echo wellKnownSIDAuthenticatedUsers & " : " & SIDToHexStr(wellKnownSIDAuthenticatedUsers)
WScript.Echo wellKnownSIDSystem & " : " & SIDToHexStr(wellKnownSIDSystem)
WScript.Echo wellKnownSIDExampleUser & " : " & SIDToHexStr(wellKnownSIDExampleUser)
Function SIDToHexStr(ByVal strSID)
'convert SID string value (SSDL) to hex string
Dim subStrings
subStrings = Split(strSID, "-")
SIDToHexStr = IntToHex1(subStrings(1))
SIDToHexStr = SIDToHexStr & IntToHex1(UBound(subStrings) - 2)
SIDToHexStr = SIDToHexStr & LongToHex6(subStrings(2))
For i = 3 To UBound(subStrings)
SIDToHexStr = SIDToHexStr & LongToReverseHex4(subStrings(i))
Next
End Function
'________________________________________________________________________________ helper functions
Function IntToHex1(ByVal intVar)
'converts an unsigned long to the according hex string
IntToHex1 = Right("0" & Hex(intVar), 2)
End Function
Function LongToHex6(ByVal longVar)
'converts an unsigned long to the according hex string
If (longVar > &H7FFFFFFF) Then longVar = longVar - 4294967296
LongToHex6 = Right("00000000000" & Hex(longVar), 12)
End Function
Function LongToHex4(ByVal longVar)
'converts an unsigned long to the according hex string
If (longVar > &H7FFFFFFF) Then longVar = longVar - 4294967296
LongToHex4 = Right("0000000" & Hex(longVar), 8)
End Function
Function LongToReverseHex4(ByVal longVar)
'converts an unsigned long to the accoring hex string (low order byte first)
longHex = LongToHex4(longVar)
LongToReverseHex4 = ""
For i = 0 To Len(longHex)/2 - 1
LongToReverseHex4 = Mid(longHex, 2*i + 1, 2) & LongToReverseHex4
Next
End Function
SIDs in LDAP Filters
If you want to perform an LDAP search to find an object with a certain SID value in one attribute, you have to built a special LDAP filter. This filter has to contain the pure binary data in hexadecimal notation, therefore such filter contains the single bytes of the data, divided with backslash (\) characters. So we need the old function SIDToHexStr again to use it to built this filter strings.
The example is based on a normal ADO LDAP search which is described in detail in the SelfADSI tutorial article 'Searching Objects with specific Criteria'. We read the tokenGroups attribute from a user object and search for the regarding group objects (in the global catalog). So we can determine all the groups where the user is direct or nested member of:
'you have to use other names and credentials from you own environment here!
Set obj = GetObject("LDAP://cn=Foeckeler,cn=Users,dc=cerrotorre,dc=de")
obj.GetInfoEx Array("tokenGroups"), 0 'tokenGroups is an operational attribute and has to be requested explicitely
groupListRaw = obj.GetEx("tokenGroups")
Set ado = CreateObject("ADODB.Connection") 'create new ADO connection
ado.Provider = "ADSDSOObject" 'use the ADSI interface for the search
ado.Open "ADS-Search" 'give it a name
Set gcContainer = GetObject("GC:") 'get the Global Catalog search base
For Each gcObj In gcContainer
gcBase = gcObj.ADsPath
Next
WScript.Echo obj.cn
For i = 0 To UBound(groupListRaw)
filterStr = "(objectSid=" & OctetToFilterStr(groupListRaw(i)) & ")"
Set adoCmd = CreateObject("ADODB.Command") 'create new ADO command
adoCmd.ActiveConnection = ado 'assign this to the existing ADO connection
adoCmd.CommandText = "<" & gcBase & ">;" & filterStr & ";distinguishedName;subtree"
Set objectList = adoCmd.Execute 'perform search
If (objectList.RecordCount = 1) Then
WScript.Echo "---> " & objectList.Fields("distinguishedName")
Else
WScript.Echo "???? " & HexStrToSID(OctetToHexStr(groupListRaw(i)))
End If
Next
Function OctetToFilterStr(var_octet)
'converts pure binary data into the according LDAP filter string
Dim n
OctetToFilterStr = ""
For n = 1 To lenb(var_octet)
OctetToFilterStr = OctetToFilterStr & "\" & Right("0" & hex(ascb(midb(var_octet, n, 1))), 2)
Next
OctetToFilterStr = Mid(OctetToFilterStr, 1)
End Function
'_________________________________________________________________________________________ helper functions
Function HexStrToSID(strSid)
'converts a raw SID hex string to the according SID string (SDDL)
Dim data, i, offset
ReDim data(Len(strSid)/2 - 1)
For i = 0 To UBound(data)
data(i) = CInt("&H" & Mid(strSid, 2*i + 1, 2))
Next
HexStrToSID = "S-" & data(0) & "-" & Byte6ToLong(data(2), data(3), data(4), data(5), data(6), data(7))
blockCount = data(1)
For i = 0 To blockCount - 1
offset = 8 + 4*i
HexStrToSID = HexStrToSID & "-" & Byte4ToLong(data(offset+3), data(offset+2), data(offset+1), data(offset))
Next
End Function
Function OctetToHexStr(var_octet)
'converts pure binary data to a string with the according hexadecimal values
Dim n
OctetToHexStr = ""
For n = 1 To lenb(var_octet)
OctetToHexStr = OctetToHexStr & Right("0" & hex(ascb(midb(var_octet, n, 1))), 2)
Next
End Function
Function Byte4ToLong(ByVal b1, ByVal b2, ByVal b3, ByVal b4)
'converts 4 bytes to the according lang integer value
Byte4ToLong = b1
Byte4ToLong = Byte4ToLong * 256 + b2
Byte4ToLong = Byte4ToLong * 256 + b3
Byte4ToLong = Byte4ToLong * 256 + b4
End Function
Function Byte6ToLong(ByVal b1, ByVal b2, ByVal b3, ByVal b4, ByVal b5, ByVal b6)
'converts 6 bytes to the according lang integer value
Byte6ToLong = b1
Byte6ToLong = Byte6ToLong * 256 + b2
Byte6ToLong = Byte6ToLong * 256 + b3
Byte6ToLong = Byte6ToLong * 256 + b4
Byte6ToLong = Byte6ToLong * 256 + b5
Byte6ToLong = Byte6ToLong * 256 + b6
End Function
LDAP Bind with SIDs instead of object DNs
A very interesting variation for the search for objects with a certain SID: In an Active Directory environment, you can bind to an LDAP object in scripts with the SDDL SID string. You just have to use a specific notation with <...> like this one:
<SID=S-1-5-21-34672221-56910222-80333210-57321189-511> (example)
Even the pure hexstrings of SIDs can be used in LDAP Binds:
<SID=0105000000000005150000001b0e683dbf16479eb5a59ec158040000> (example)
So we could use this to identify the object with a certain SID without having to perform an LDAP search with the special filter from the last example - this is much more elegant than the last example script.
'you have to use other names and credentials from you own environment here!
Set obj = GetObject("LDAP://cn=Foeckeler,cn=Users,dc=cerrotorre,dc=de")
obj.GetInfoEx Array("tokenGroups"), 0 'tokenGroups is an operational attribute and has to be requested explicitely
groupListRaw = obj.GetEx("tokenGroups")
WScript.Echo obj.cn
For i = 0 To UBound(groupListRaw)
sidHex = OctetToHexStr(groupListRaw(i))
Set obj = GetObject("LDAP://<SID=" & sidHex & ">")
WScript.Echo "---> " & obj.distinguishedName
Next
Function OctetToHexStr(var_octet)
'converts pure binary data to a string with the according hexadecimal values
Dim n
OctetToHexStr = ""
For n = 1 To lenb(var_octet)
OctetToHexStr = OctetToHexStr & Right("0" & hex(ascb(midb(var_octet, n, 1))), 2)
Next
End Function
Tools for displaying and converting SIDs
There are several other tools for displaying a Microsoft String(SID) attribute and for converting such attributes into a readable SDDL-SID-String.
LEX - The LDAP Explorer
LEX - The LDAP Explorer (a commercial powerful LDAP browser I developed) has special attribute editors for SID values and can show them as SDDL strings as well as raw data:

LEX Editor for Microsoft SID Attributes:

PSGETSID
This tool is included in the wellknown SysInternals PSTools suite and can be downloaded for free at Microsoft website. You can use it to resolve SIDs into account names and vice versa:


