Printout Header
RSS Feed

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].

Structure of a Microsoft SID

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 (Little 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:

An Active Directory 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):


Wellknown SID - Everyone


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

Wellknown SID - System


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 Dim i 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) Dim i 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) 'wandelt reine Binärdaten (Byte-Array) in den entsprechenden LDAP Filter String um. 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:

Screenshot: SID Attributes in the LEX LDAP Browser

LEX Editor for Microsoft SID Attributes:

Screenshots: SID Attribute Editors in LEX - The LDAP Explorer


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:

Screenshot: Integer8 Attribute Conversion with W32TM