Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enumerate Windows user group members on remote system using c#

Within c#, I need to be able to

  • Connect to a remote system, specifying username/password as appropriate
  • List the members of a localgroup on that system
  • Fetch the results back to the executing computer

So for example I would connect to \SOMESYSTEM with appropriate creds, and fetch back a list of local administrators including SOMESYSTEM\Administrator, SOMESYSTEM\Bob, DOMAIN\AlanH, "DOMAIN\Domain Administrators".

I've tried this with system.directoryservices.accountmanagement but am running into problems with authentication. Sometimes I get:

Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. Disconnect all previous connections to the server or shared resource and try again. (Exception from HRESULT: 0x800704C3)

The above is trying because there will be situations where I simply cannot unmap existing drives or UNC connections.

Other times my program gets UNKNOWN ERROR and the security log on the remote system reports an error 675, code 0x19 which is KDC_ERR_PREAUTH_REQUIRED.

I need a simpler and less error prone way to do this!

like image 252
quux Avatar asked Aug 22 '08 00:08

quux


Video Answer


1 Answers

davidg was on the right track, and I am crediting him with the answer.

But the WMI query necessary was a little less than straightfoward, since I needed not just a list of users for the whole machine, but the subset of users and groups, whether local or domain, that were members of the local Administrators group. For the record, that WMI query was:

SELECT PartComponent FROM Win32_GroupUser WHERE GroupComponent = "Win32_Group.Domain='thehostname',Name='thegroupname'"

Here's the full code snippet:

public string GroupMembers(string targethost, string groupname, string targetusername, string targetpassword)
        {
            StringBuilder result = new StringBuilder(); 
            try
            {
                ConnectionOptions Conn = new ConnectionOptions();
                if (targethost != Environment.MachineName) //WMI errors if creds given for localhost
                {
                    Conn.Username = targetusername; //can be null
                    Conn.Password = targetpassword; //can be null
                }
                Conn.Timeout = TimeSpan.FromSeconds(2);
                ManagementScope scope = new ManagementScope("\\\\" + targethost + "\\root\\cimv2", Conn);
                scope.Connect();
                StringBuilder qs = new StringBuilder();
                qs.Append("SELECT PartComponent FROM Win32_GroupUser WHERE GroupComponent = \"Win32_Group.Domain='");
                qs.Append(targethost);
                qs.Append("',Name='");
                qs.Append(groupname);
                qs.AppendLine("'\"");
                ObjectQuery query = new ObjectQuery(qs.ToString());
                ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
                ManagementObjectCollection queryCollection = searcher.Get();
                foreach (ManagementObject m in queryCollection)
                {
                    ManagementPath path = new ManagementPath(m["PartComponent"].ToString());                                        
                    { 
                        String[] names = path.RelativePath.Split(',');
                        result.Append(names[0].Substring(names[0].IndexOf("=") + 1).Replace("\"", " ").Trim() + "\\"); 
                        result.AppendLine(names[1].Substring(names[1].IndexOf("=") + 1).Replace("\"", " ").Trim());                    
                    }
                }
                return result.ToString();
            }
            catch (Exception e)
            {
                Console.WriteLine("Error. Message: " + e.Message);
                return "fail";
            }
        }

So, if I invoke Groupmembers("Server1", "Administrators", "myusername", "mypassword"); I get a single string returned with:

SERVER1\Administrator
MYDOMAIN\Domain Admins

The actual WMI return is more like this:

\\SERVER1\root\cimv2:Win32_UserAccount.Domain="SERVER1",Name="Administrator"

... so as you can see, I had to do a little string manipulation to pretty it up.

like image 139
quux Avatar answered Oct 04 '22 17:10

quux