My last post titled “Event 10 Mystery Solved” (found here.) left me with a question about the binary version of the SID, a returned value of CreatorSID: 1,5,0,0,0,0,0,5,21,0,0,0,190,118,173,34,87,198,105,19,239,226,7,24,244,1,0,0.
I started searching the net to see if anyone has posted a conversion tool to go from the the Binary SID: The Administrators SID in array of bytes format:
CreatorSID = {1,2,0,0,0,0,0,5,32,0,0,0,32,2,0,0};
to a String version:
Administrators group S-1–5-32-544
So far, I have not found a formula to do a conversion (that works) but I am getting closer, and learning a few things in the process.
I ran across a blog article (located here) that used PowerShell to search classes by Key Phrase and return a list of Classes that it was located in. The default search location is Cimv2
(System default).
That gave me several locations to search for answers.
The Win32_SID
does not return any instances. You have to create an instance, as you will see below in the script. It does show BinaryRepresentation
as a property.
Then I ran across this Web Page.
How to convert the SDDL form of an SID to a SAM account name (located here and another KB article here on conversion) which the code appears to be in VB6 version. The code still didn’t do what I wanted. They all required you to access the system for some form of information. It did tell me that the SID is actually stored in the binary form to start with, so all I had to do is figure out how to get at it, which brings me to the next web page.
This page is a script that I found at the Microsoft Script Center (located here) called, “Create Shared Folder and Set Access Permissions”.
After looking through the code, I noted that it had a place where the binary version of the SID was placed into a variable, which was what I wanted.
Foldername="c:\KS"
sharename="KS_Share"
strDesc="Test Share"
strUser="mike"
Set Services = GetObject("winmgmts:{impersonationLevel=impersonate,(Security)}!\\.\root\cimv2")
Set SecDescClass = Services.Get("Win32_SecurityDescriptor")
Set SecDesc = SecDescClass.SpawnInstance_()
Set colWinAcc = Services.ExecQuery("SELECT *
_FROM Win32_ACCOUNT WHERE Name='" & strUser & "'")
If colWinAcc.Count < 1 Then
Wscript.echo("User " & strUser & "Not Found - quitting")
wscript.quit
End If
For Each refItem in colWinAcc
Set refSID = Services.Get("Win32_SID='" & refItem.SID & "'")
Next
Set refTrustee = Services.Get("Win32_Trustee").spawnInstance_()
With refTrustee
.Domain = refSID.ReferencedDomainName
.Name = refSID.AccountName
.SID = refSID.BinaryRepresentation
.SidLength = refSID.SidLength
.SIDString = refSID.SID
End With
Set ACE = Services.Get("Win32_Ace").SpawnInstance_
ACE.Properties_.Item("AccessMask") = 2032127
ACE.Properties_.Item("AceFlags") = 3
ACE.Properties_.Item("AceType") = 0
ACE.Properties_.Item("Trustee") = refTrustee
SecDesc.Properties_.Item("DACL") = Array(ACE)
Set Share = Services.Get("Win32_Share")
Set InParam = Share.Methods_("Create").InParameters.SpawnInstance_()
InParam.Properties_.Item("Access") = SecDesc
InParam.Properties_.Item("Description") = strDesc
InParam.Properties_.Item("Name") = ShareName
InParam.Properties_.Item("Path") = FolderName
InParam.Properties_.Item("Type") = 0
Set outParams=Share.ExecMethod_("Create", InParam)
wscript.echo("OUT: " & outParams.returnValue)
If outParams.returnValue <> 0 Then
wscript.echo("Failed to Create Share, return Code:" & outParams.returnValue)
Else
wscript.echo("Folder " & Foldername & _
" sucessfully shared as: " & sharename & " _
with FULL CONTROL Permissions for user " & strUser)
End If
The code above as listed on the Script Center.
Next, I had to strip out everything to do with working with the folders. Then, add a way to show the “refTrustee.* ” information, as you see in the next code listing.
strUser="David"
Set Services = GetObject("winmgmts:{impersonationLevel=impersonate,(Security)}!\\.\root\cimv2")
Set SecDescClass = Services.Get("Win32_SecurityDescriptor")
Set SecDesc = SecDescClass.SpawnInstance_()
Set colWinAcc = Services.ExecQuery("SELECT * _
FROM Win32_ACCOUNT WHERE Name='" & strUser & "'")
If colWinAcc.Count < 1 Then
Wscript.echo("User " & strUser & "Not Found - quitting")
wscript.quit
End If
For Each refItem in colWinAcc
Set refSID = Services.Get("Win32_SID='" & refItem.SID & "'")
Next
Set refTrustee = Services.Get("Win32_Trustee").spawnInstance_()
With refTrustee
.Domain = refSID.ReferencedDomainName
.Name = refSID.AccountName
.SID = refSID.BinaryRepresentation
.SidLength = refSID.SidLength
.SIDString = refSID.SID
End With
Wscript.echo "Domain Name: " & refTrustee.Domain
Wscript.echo "User Name: " & refTrustee.Name
strSID = Join(refTrustee.SID, ",")
WScript.Echo "SID: " & strSID
Wscript.echo "SidLength: " & refTrustee.SidLength
Wscript.echo "SIDString: " & refTrustee.SIDString
The only thing you will have to change in the modified script to get it to work is the name of a user. You can also save it as a text file and load it into the Scriptomatic V2 to run it.
The output from the BUILTIN Administrators group looks like this:
Domain Name: BUILTIN
User Name: Administrators
SID: 1,2,0,0,0,0,0,5,32,0,0,0,32,2,0,0
SidLength: 16
SIDString: S-1-5-32-544
To me, one of the most interesting things is the way to handle the array of Bytes, the script used the join
function to pull out all of the bytes.
strSID = Join(refTrustee.SID, ",")
WScript.Echo "SID: " & strSID
This does not work in VB.NET, you get a this function is not supported error.
Now the Conversion
Below is the VB.NET version of the stripped script above, I started by copying the script into a new project then adding the Import
s and references, also adding Dim
to a few lines and replace the WScript.Echo
with a way to get the information to a multi-line Textbox.
Here is what the form looks like:
And here is the code behind the form:
Imports System.Management
Imports System.Management.Instrumentation
Imports System.Threading
Imports System
Imports System.IO
Imports System.Security
Imports System.Security.Permissions
Imports System.Text
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles MyBase.Load
Dim uan As Integer
Dim usearcher As New ManagementObjectSearcher( _
"root\CIMV2", _
"SELECT * FROM Win32_UserAccount")
For Each queryObj As ManagementObject In usearcher.Get()
ComboBoxAllUsers.Items.Add(queryObj("Name"))
Next
lblUserNumber.Text = "Total Accounts Found: " & ComboBoxAllUsers.Items.Count
uan = ComboBoxAllUsers.Items.Count
End Sub
Private Sub btnGetNfo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles btnGetNfo.Click
Try
Dim strUser As String = ComboBoxAllUsers.SelectedItem.ToString
Dim refSID As System.Object
refSID = Nothing
Dim Services = GetObject("winmgmts:{impersonationLevel=impersonate,_
(Security)}!\\.\root\cimv2")
Dim SecDescClass = Services.Get("Win32_SecurityDescriptor")
Dim SecDesc = SecDescClass.SpawnInstance_()
Dim colWinAcc = Services.ExecQuery_
("SELECT * FROM Win32_ACCOUNT WHERE Name='" & _
strUser & "'")
If colWinAcc.Count < 1 Then
MsgBox("User " & strUser & "Not Found ")
End If
For Each refItem In colWinAcc
refSID = Services.Get_
("Win32_SID='" & refItem.SID & "'")
Next
Dim refTrustee = Services.Get("Win32_Trustee").spawnInstance_()
With refTrustee
.Domain = refSID.ReferencedDomainName
.Name = refSID.AccountName
.SID = refSID.BinaryRepresentation
.SidLength = refSID.SidLength
.SIDString = refSID.SID
End With
Dim strb As New StringBuilder
Dim strsid As New StringBuilder
Dim nstrSid As String
Dim pSid As String
For Each pSid In refTrustee.SID
strsid.Append(pSid & ",")
Next
nstrSid = strsid.ToString
strb.AppendLine("Domain Name: " & refTrustee.Domain)
strb.AppendLine("User Name: " & refTrustee.Name)
strb.AppendLine("SID: " & nstrSid)
strb.AppendLine("SidLength: " & refTrustee.SidLength)
strb.AppendLine("SIDString: " & refTrustee.SIDString)
tbSidNFO.Text = strb.ToString
Catch ex As Exception
If ComboBoxAllUsers.SelectedItem = Nothing Then
MsgBox("Please Select A User Name", _
MsgBoxStyle.Information, "User Not Selected")
Else
MsgBox("An Error Has Occured : " & vbCrLf & ex.Message)
End If
End Try
End Sub
Private Sub lblAbout_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles lblAbout.Click
My.Forms.AboutBox1.Show()
End Sub
End Class
The code for the above app only gets the local User Accounts it finds.
The way I handled the array of Bytes in VB.NET is with a foreach
loop and a string
builder. I’m not sure how others would do it but I kinda like the Stringbuilder
now that I have learned how to use it. The other way I used to do it was in 1 long line and several “&
” and “vbCrLf
” mixed in. When you had several properties to pull out, then it got harder to read and handle the code even using line continuation.
This code below is the way to get the information to the text box from the original code.
Dim strb As New StringBuilder
Dim strsid As New StringBuilder
Dim nstrSid As String
Dim pSid As String
For Each pSid In refTrustee.SID
strsid.Append(pSid & ",")
Next
nstrSid = strsid.ToString
strb.AppendLine("Domain Name: " & refTrustee.Domain)
strb.AppendLine("User Name: " & refTrustee.Name)
strb.AppendLine("SID: " & nstrSid)
strb.AppendLine("SidLength: " & refTrustee.SidLength)
strb.AppendLine("SIDString: " & refTrustee.SIDString)
tbSidNFO.Text = strb.ToString
Let's jazz this up a bit and get the System and Group accounts also.
Here is what the final app looks like:
In this version, I added a group box and the 3 Radio Buttons inside of it for selecting the account type.
I then moved the code to get the names for the account type to its own sub. Then in the radio button on change event, calls the selected user type sub to fill the combo box.
Now the Final Code
Imports System.Management
Imports System.Management.Instrumentation
Imports System.Threading
Imports System
Imports System.IO
Imports System.Security
Imports System.Security.Permissions
Imports System.Text
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles MyBase.Load
ComboBoxAllUsers.Items.Clear()
Get_userAccount()
End Sub
Private Sub btnGetNfo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles btnGetNfo.Click
Try
MsgBoxStyle.Information, "User Not Selected")
Dim strUser As String = ComboBoxAllUsers.SelectedItem.ToString
Dim refSID As System.Object
refSID = Nothing
Dim Services = GetObject("winmgmts:{impersonationLevel=impersonate,_
(Security)}!\\.\root\cimv2")
Dim SecDescClass = Services.Get("Win32_SecurityDescriptor")
Dim SecDesc = SecDescClass.SpawnInstance_()
Dim colWinAcc = Services.ExecQuery_
("SELECT * FROM Win32_ACCOUNT WHERE Name='" _
& strUser & "'")
If colWinAcc.Count < 1 Then
MsgBox("User " & strUser & "Not Found ")
End If
For Each refItem In colWinAcc
refSID = Services.Get("Win32_SID='" & refItem.SID & "'")
Next
Dim refTrustee = Services.Get("Win32_Trustee").spawnInstance_()
With refTrustee
.Domain = refSID.ReferencedDomainName
.Name = refSID.AccountName
.SID = refSID.BinaryRepresentation
.SidLength = refSID.SidLength
.SIDString = refSID.SID
End With
Dim strb As New StringBuilder
Dim strsid As New StringBuilder
Dim nstrSid As String
Dim pSid As String
For Each pSid In refTrustee.SID
strsid.Append(pSid & ",")
Next
nstrSid = strsid.ToString
strb.AppendLine("Domain Name: " & refTrustee.Domain)
strb.AppendLine("User Name: " & refTrustee.Name)
strb.AppendLine("SID: " & nstrSid)
strb.AppendLine("SidLength: " & refTrustee.SidLength)
strb.AppendLine("SIDString: " & refTrustee.SIDString)
tbSidNFO.Text = strb.ToString
Catch ex As Exception
If ComboBoxAllUsers.SelectedItem = Nothing Then
MsgBox("Please Select A User Name", _
MsgBoxStyle.Information, "User Not Selected")
Else
MsgBox("An Error Has Occured : " & vbCrLf & ex.Message)
End If
End Try
End Sub
Private Sub lblAbout_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles lblAbout.Click
My.Forms.AboutBox1.Show()
End Sub
Private Sub Get_userAccount()
tbSidNFO.Clear()
Dim uan As Integer
Dim usearcher As New ManagementObjectSearcher( _
"root\CIMV2", _
"SELECT * FROM Win32_UserAccount")
For Each queryObj As ManagementObject In usearcher.Get()
ComboBoxAllUsers.Items.Add(queryObj("Name"))
Next
lblUserNumber.Text = "Total User Accounts Found: " & ComboBoxAllUsers.Items.Count
uan = ComboBoxAllUsers.Items.Count
End Sub
Private Sub Get_GroupAccount()
tbSidNFO.Clear()
Dim uan As Integer
Dim usearcher As New ManagementObjectSearcher( _
"root\CIMV2", _
"SELECT * FROM Win32_Group")
For Each queryObj As ManagementObject In usearcher.Get()
ComboBoxAllUsers.Items.Add(queryObj("Name"))
Next
lblUserNumber.Text = "Total Group Accounts Found: " & ComboBoxAllUsers.Items.Count
uan = ComboBoxAllUsers.Items.Count
End Sub
Private Sub Get_SystemAccounts()
tbSidNFO.Clear()
Dim uan As Integer
Dim usearcher As New ManagementObjectSearcher( _
"root\CIMV2", _
"SELECT * FROM Win32_SystemAccount")
For Each queryObj As ManagementObject In usearcher.Get()
ComboBoxAllUsers.Items.Add(queryObj("Name"))
Next
lblUserNumber.Text = "Total System Accounts Found: " & ComboBoxAllUsers.Items.Count
uan = ComboBoxAllUsers.Items.Count
End Sub
Private Sub RadioButtonUserAccount_CheckedChanged(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles RadioButtonUserAccount.CheckedChanged
ComboBoxAllUsers.Items.Clear()
ComboBoxAllUsers.Text = _
"Please Select A User Account"
Get_userAccount()
End Sub
Private Sub RadioButtonGroupAccount_CheckedChanged(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles RadioButtonGroupAccount.CheckedChanged
ComboBoxAllUsers.Items.Clear()
ComboBoxAllUsers.Text = "Please Select A Group Account"
Get_GroupAccount()
End Sub
Private Sub RadioButtonSystemAccount_CheckedChanged(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles RadioButtonSystemAccount.CheckedChanged
ComboBoxAllUsers.Items.Clear()
ComboBoxAllUsers.Text = "Please Select A System Account"
Get_SystemAccounts()
End Sub
End Class
Now that I can get several sample versions of the binary/array of bytes version of the SID, I can get back to the research of being able to do a conversion of a given binary SID and do the conversion without having access to the original system it came from.
The best source I could find that explained how the conversion works is (located here) at selfadsi.org Microsoft Security Descriptor(SID)Attributes.
So far, I can’t get the math & conversion to agree with what this program shows.
Conclusion
If you have access to the systems and have Admin Authority, then this should work for you as an easy way to get the binary and string form for an account. You may also want to check out another tool of mine called All User Account NFO (located here).
Located on my website with a link to download the program.
That’s it for now, hope everyone else learned as much as I did.
Please post any comments or questions.
I think I have figured out the comments control, maybe.
Reposting 11/19/2011 to try and get the RSS to pick it up.