11

How can I find a user in my AD when I have his/her SID. I don't want to rely on other attributes, since I am trying to detect changes to these. Example: I get a message about a change to user record containing:

Message: User Account Changed:

    Target Account Name:    test12

    Target Domain:  DOMAIN

    Target Account ID:  %{S-1-5-21-3968247570-3627839482-368725868-1110}

    Caller User Name:   Administrator

    Caller Domain:  DOMAIN

    Caller Logon ID:    (0x0,0x62AB1)

    Privileges: -

I want to notify the user about the change. So I need their account-information from AD.

er4z0r
  • 151
  • 1
  • 1
  • 6
  • Are you a sys admin trying to do this, or are you trying to get this progmatically? If progmatically then can I suggest the question gets moved back to SO? – pipTheGeek Mar 08 '10 at 17:43
  • It's a bit of an edge case - if you want help figuring out LDAP\ADSI scripting then it might sit here but it does sound more programming\Directory Services API related to me. If it stays I'll post an LDAP\ADSI answer. – Helvick Mar 08 '10 at 18:21
  • O.K. I am actually trying to do this via VBScript but I would be interested in a way to do this via other commandline tools like dsquery – er4z0r Mar 08 '10 at 19:41

4 Answers4

24

Fire up Windows PowerShell and run:

$strSID="S-1-5-21-500000003-1000000000-1000000003-1001"
$uSid = [ADSI]"LDAP://<SID=$strSID>"
echo $uSid

The output should look something like this,

distinguishedName : {CN=John Doe,OU=Domain Admins,OU=People,OU=xxx,DC=xxx}
Path              : LDAP://<SID=S-1-5-21-500000003-1000000000-1000000003-1001>
Peter Mortensen
  • 2,319
  • 5
  • 23
  • 24
Tim Abell
  • 1,311
  • 3
  • 10
  • 21
5

The "LDAP way" to do this would be to retrieve the base object with the GUID (or SID), which will retrieve only the base object and not have additional class data attached. However, from this base object you can retrieve the actual "distinguishedName" for the user object. Retrieving the user object using the "distinguishedName" attribute will return a DirectoryEntry object (.Net/C#/PowerShell) or a iadsUser object (VBScript) with full class data and allow you to get whatever other attribute data you need.

The issue is retrieving the initial object with the GUID (or SID). Some sources will say that you must convert the string format GUID (i.e., {28c67c50-9778-47a4-a77a-bf56f238a0c4}) into a string representation of the byte-array (i.e., "\50\7c\c6\28\78\97\a4\47\7a\a7\bf\56\f2\38\a0\c4") to pass to LDAP. According to Microsoft documentation this is not the case. A simple string representation of the GUID/SID is sufficient.

Here's a sample of how you can bind to the object via the GUID then retrieve the actual user object with full class data. Powershell actually pulls the complete object if you bind with the GUID. If you use VBScript, then you would need to do the two step process.

Also, please note that although the Microsoft docs say that multiple GUID-string formats are acceptable, the only one I have been able to successfully use is to strip the {}- characters. ALSO, please note this is NOT a correct "byte-array" string, but simply the GUID string stripped of special characters.

$strGUID = "{28c67c50-9778-47a4-a77a-bf56f238a0c4}" -replace '-|{|}',''
$guid = [ADSI]"LDAP://<GUID=$strGUID>"
$user = [ADSI]$guid.distinguishedName

The same process can be used for a SID bind. The MSDN page describing this says there are several fstring formats available, but the most common will be the s-1-5-...-...-...-... format.

#Powershell
$strSID="S-1-5-21-500000003-1000000000-1000000003-1001"
$uSid = [ADSI]"LDAP://<SID=$uSid>"
$user = [ADSI]$user.distinguishedName

* QUERYING *

If you are going to perform an LDAP query to find the object (e.g. by comparing 'objectGUID' to a byte-array or 'objectSID' to a byte-array), that is when you will need to do the "correct" byte-array conversion. It is important to note that the byte-array has a different order than the string representation, as it is stored as DWORD-WORD-WORD-WORD-BYTES for GUID, and DOES take endian order into consideration. Converting the byte-array for a SID has similar condierations.

There are several different ways to accomplish the conversion, Technet has a simple vbScript algorithm. You could also do something fancier with C#/VB.Net using the System.Guid, or via a simple script in PowerShell (gotta love PowerShell!):

#Powershell
#   Creates a new System.GUID object from the supplied string.
#   Only need for this example.
$guid = [system.guid]"{28c67c50-9778-47a4-a77a-bf56f238a0c4}" 
$out=""
#Formats the array of integers as a backslash-delimited string of Hex values
$guid.ToByteArray() | %{ $out += $("\{0:x2}" -f $_) }

You should then be able to query for the object using a standard LDAP filter:

(&(objectClass=User)(objectGUID=\50\7c\c6\28\78\97\a4\47\a7\7a\bf\56\f2\38\a0\c4))

... or whatever else you may be querying for. This should work for a SID as well.

Ryan Fisher
  • 2,218
  • 16
  • 13
  • WOW! Thanks for the extensive answer! I'll give it a try with ADSI and see if it works there as well. Never did powershell before. – er4z0r Mar 09 '10 at 07:29
  • My first stab at it was not successful. Please have a look at my edit. – er4z0r Mar 09 '10 at 08:41
  • Sorry, I don't have time for a full answer but your problem is that the ID that you have in the event data is a SID, not a GUID. You won't be able to bind directly to an object using the SID but you can search the directory using the objectSid attribute. – pipTheGeek Mar 09 '10 at 12:35
  • hi pipTheGeek. Turns out I actually can bind using a SID. See answer for more. – er4z0r Mar 09 '10 at 13:40
  • 1
    Cool. I like learning, I don't like being wrong, I learn most when I am wrong... :) – pipTheGeek Mar 09 '10 at 20:46
  • You should not put anything except sid or guid into query because both sid and guid must be unique – filimonic Apr 09 '21 at 19:34
2

OK. I found a way to do this via Active Directory. For compeleteness here is the code:

REM Converts the SID into a format, that can be processed by ADSI or WMI
Function NormalizeSid(strSidToNormalize)
  Dim regEx,strReplace
  strReplace=""
  ' Create regular expression.
  Set regEx = New RegExp
  regEx.Global  = True
  regEx.Pattern = "(%|{|})"
  regEx.IgnoreCase = True

  ' Make replacement.
  NormalizeSid = regEx.Replace(strSidToNormalize, strReplace)
End Function

REM Searches for a SID the in the Message that was passed as argument
REM SID returned will be of the  form %{S-1-5-21-3968247570-3627839482-368725868-1110}
REM NOTE: Neither WMI nor ADSI will accept this. Use NormalizeSid like in FindUser
Function FindSidInMessage(Message)
    Dim strAccountRegex
    Dim objRegex
    Dim objMatch
    Dim strSID

    strAccountRegex = "(\%\{S\-[,0-9,\-]*\})"
    Set objRegex    = new RegExp
    objRegex.Pattern= strAccountRegex

    for each objMatch in objRegex.Execute(Message)
            REM Wscript.StdOut.writeLine "Found an Account ID: " & objMatch.value
            strSID=objMatch.value
    next

    FindSidInMessage=strSID
End Function 

REM Searches Directory for the User matching the SID passed as parameter
Function FindUser(userSID)
    Dim normalizedSID
    Dim objUser

    normalizedSID=NormalizeSid(userSID)
    Wscript.Echo "SID after escaping: " & normalizedSID

    Wscript.StdOut.writeLine "Querying AD to retrieve user-data" 
    Set objUser = GetObject("LDAP://<SID="& normalizedSID & ">")
    FindUser=objUser.EmailAddress
End Function

Hope this will be useful to others.

er4z0r
  • 151
  • 1
  • 1
  • 6
2

Use PS:

$SID = "S-X-X-XX-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX-XXXX"
Get-ADObject -IncludeDeletedObjects -Filter * -Properties * | where{$_.objectSid -eq $SID}
Ahtyam
  • 1
  • 1