SACL on Services using C# || get a handle to a ser

2019-07-17 15:16发布

问题:

Anyone have any idea how to get the SACL's on a remote service using C#? I've tried numerous different methods, and basically nothing works. I can get the DACL's and SACL's on the local machine, but getting either on a remote machine doesn't appear to be possible.

What I've done is create a class called ServiceSecurity that inherits from NativeObjectSecurity and acts a lot like the RegistrySecurity class. Below are two of the constructors that I have:

public ServiceSecurity(string serviceName, AccessControlSections includeSections)
        : base(true, ResourceType.Service, serviceName, includeSections, null, null)
    {
    }

    public ServiceSecurity(System.Runtime.InteropServices.SafeHandle handle, AccessControlSections includeSections)
        : base(true, ResourceType.Service, handle, includeSections)
    {
    }

The first one uses a service name, while the second expects a handle to the service. Obviously the first one works fine to get the DACL's and SACL's because it's all local, but to get to a remote machine, I'm using the ServiceController class to find the service on the remote machine. After getting it, I pass the ServiceHandle property of the service to the ServiceSecurity class I've built, at which point I get an Unauthorized Access exception, which doesn't seem right because my user account is a Domain Admin for the domain, and a Local admin on the target box. I also have the SeSecurityPrivilege right, which should allow me access.

Anyone have any ideas? It seems like the SafeHandle I'm getting isn't right, but the SafeHandle properties say that it's not closed and that it is a valid handle, so I don't quite know what's going on.

Here's the code I'm using to try to retrieve the data:

ServiceSecurity sSec = new ServiceSecurity(services[i].ServiceName, accessSections);
string outputData = sSec.GetSecurityDescriptorSddlForm(accessSections);

The above will work for local permissions and audit settings (DACL's and SACL's). But it's designed to work on a local machine. If I do this instead:

ServiceSecurity sSec = new ServiceSecurity(services[i].ServiceHandle, accessSections);
string outputData = sSec.GetSecurityDescriptorSddlForm(accessSections);

The ServiceSecurity constructor fails on both local and remote servers for the SACL's as follows, but still works for the DACL's:

System.UnauthorizedAccessException: Attempted to perform an unauthorized operation.
   at System.Security.AccessControl.Win32.GetSecurityInfo(ResourceType resourceType, String name, SafeHandle handle, AccessControlSections accessControlSections, RawSecurityDescriptor& resultSd)
   at System.Security.AccessControl.NativeObjectSecurity.CreateInternal(ResourceType resourceType, Boolean isContainer, String name, SafeHandle handle, AccessControlSections includeSections, Boolean createByName, ExceptionFromErrorCode exceptionFromErrorCode, Object exceptionContext)
   at System.Security.AccessControl.NativeObjectSecurity..ctor(Boolean isContainer, ResourceType resourceType, SafeHandle handle, AccessControlSections includeSections)

For the accessSections, I am only specifying a single one of the AccessControlSections, be it Audit or Access and Audit seems to fail every time when I'm passing the ServiceHandle.


Update: So perhaps the question should really be, how do I get a handle to a service that has the ACCESS_SYSTEM_SECURITY rights so I can get the SACL's?

回答1:

The handle you get from the ServiceController will not have ACCESS_SYSTEM_SECURITY rights so you can't access the SACL using it.

In order to get that right you would need to get a handle with that right. You might need to use DuplicateHandle to get the extra rights but enabling the SeSecurityPrivilege for the remote system might be tricky.

Have you tried using the named service constructor with a remote name?

I'll need to check on this a bit and get back to you. If nobody has given a better answer by tomorrow I'll dig up the answer for you.

EDIT: By named service constructor I mean try creating a ServiceSecurity object with the name of the service in the form \\server_name\service_name or \\IP_address\service_name. In theory it should work but it may not due to the fact that the SeSecurityPrivilege will be enabled on the local machine not on the remote machine.

In order to get a handle with the ACCESS_SYSTEM_SECURITY right granted you must follow these steps:

  • Retrieve the handle (from the ServiceController or through interop)
  • Enable the SeSecurityPrivilege for the current token (this is the tricky part), see: AdjustTokenPrivileges, TOKEN_PRIVILEGES, LUID_AND_ATTRIBUTES, etc.
  • Call DuplicateHandle on the handle requesting whatever permissions you need including ACCESS_SYSTEM_SECURITY to retrieve a new handle with the appropriate rights.
  • Disable the SeSecurityPrivilege (as above).

Unfortunately, I don't have a test environment set up appropriately to test this so I can't guarantee that it will work for remote services. The enabling of SeSecurityPrivilege for the remote server seems to be the main stumbling block. Anyway, I hope this helps somewhat at least.