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

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

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


Tweet