Proper way to generate/create an SID based on a service name to restrict a device_object?

Hi everyone,

I have a DEVICE_OBJECT, and based on recommendations on this forum, it seems like using DACL is the proper way of restricting my device_object for getting only opened by a certain service name.

My question is, what is the proper way of generating the proper SID for the DACL?

I know that i have to use RtlAddAccessAllowedAce + ZwSetSecurityObject, but the problem here is that the last argument (SID) on every sample that i found is using a generic thing like SeExports->SeLocalSystemSid, But how can i create/generate the proper SID for the last argument of this API based on a service name like “NT SERVICE\MyServiceName” in kernel? Tried reading some MSDN articles regarding this but they are way too complicated… For example should the SID_IDENTIFIER_AUTHORITY be SECURITY_NT_AUTHORITY? If so then why the name is “NT SERVICE\MyServiceName” and not “NT AUTHORITY\MyServiceName”?

Here’s a sample code that restrict device_object to admins and system:

VOID SetDeviceDacl(PDEVICE_OBJECT pDeviceObject)
{
    HANDLE hDevice;
    PACL pAcl;
    ULONG AclSize;
    SECURITY_DESCRIPTOR SecDesc;

    ObOpenObjectByPointer(pDeviceObject, OBJ_KERNEL_HANDLE, NULL, WRITE_DAC, NULL, KernelMode, &hDevice);

    AclSize = sizeof(ACL);
    AclSize += RtlLengthSid(SeExports->SeLocalSystemSid);
    AclSize += RtlLengthSid(SeExports->SeAliasAdminsSid);
    AclSize += 2 * FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart);

    pAcl = ExAllocatePoolWithTag(PagedPool, AclSize, DRIVER_TAG);

    if (!pAcl)
        return;

    RtlCreateAcl(pAcl, AclSize, ACL_REVISION);
    RtlAddAccessAllowedAce(pAcl, ACL_REVISION, GENERIC_READ | GENERIC_WRITE | DELETE, SeExports->SeLocalSystemSid);
    RtlAddAccessAllowedAce(pAcl, ACL_REVISION, GENERIC_READ | GENERIC_WRITE | DELETE, SeExports->SeAliasAdminsSid);

    RtlCreateSecurityDescriptor(&SecDesc, SECURITY_DESCRIPTOR_REVISION);
    RtlSetDaclSecurityDescriptor(&SecDesc, TRUE, pAcl, FALSE);
    ZwSetSecurityObject(hDevice, DACL_SECURITY_INFORMATION, &SecDesc);

    ExFreePoolWithTag(pAcl, DRIVER_TAG);
    ZwClose(hDevice);
}

Another question i forgot to ask:

I read in a blogpost that restricting device objects based on service name is only available from windows 7, is this correct?

Google “Service SID”

Set the DACL on your device object in your INF… or if you must do it programmatically in your driver, use IoCreateDeviceSecure or WdfDeviceInitAssignSddlString.

Peter

@“Peter_Viscarola_(OSR)” said:
Google “Service SID”

Set the DACL on your device object in your INF… or if you must do it programmatically in your driver, use IoCreateDeviceSecure or WdfDeviceInitAssignSddlString.

Peter

But there is no good article regarding this? The top result is just a Microsoft article regarding service SID in SQL server that doesn’t provide any useful information for my problem, I just want to see a simple example or anything that tells me how can i convert a string service name like “MyService” to an SID so i can give it to RtlAddAccessAllowedAce.

Also what’s the difference between the code that i provided vs using IoCreateDeviceSecure? Doesnt the code above also do the job?

And is it true that this is only supported in windows 7+? (Securing a device object based on service name)

I have to also say that I already implemented a manual way of doing it in my create callback, in it i just check the path of the PID that tries to open a handle, and compare that to the service path, so if doing the DACL approach is only supported for 7+, then i assume my approach is better right? Because I’m not even sure how the DACL is implemented and whether or not an admin can overwrite it or what? But the manual approach is not possible to bypass.

You use the command “sc showsid

Using IoCreateDeviceSecure (or the INF, which is an entirely better idea) eliminates the race between creating the device object and setting a protection on it.

Doing path-based validation is a terrible idea… what happens if some back actor renames their malware into your path+file-name?

I suspect the service SID this is Vista and later…. Is anybody still using anything earlier than Win 7? If so, that’s ridiculous.

ETA: more than you want to know about device object protection https://www.osr.com/nt-insider/2017-issue1/making-device-objects-accessible-safe/

Peter

1 Like

@“Peter_Viscarola_(OSR)” said:

Doing path-based validation is a terrible idea… what happens if so,Enoch renames their malware into your path+file-name?

Indeed its a very bad idea IF the service key is not being protected, but we already protect our service related keys, and i get the path from the ImagePath which is protected, and the destination folder is also protected, therefore there is literally no way for a malware to bypass this other than using a driver.

I suspect the service SID this is Vista and later…. Is anybody still using anything earlier than Win 7? If so, that’s ridiculous.

So i guess this makes it not really a good approach compared to the manual approach. In an ideal world every customer is using windows 11 but we both know thats not true and many are still using XP/2003…

But I’ll still give the DACL approach a try to see if it has any security advantage compared to the manual approach.

@david_mk85 said:

how can i create/generate the proper SID for the last argument of this API based on a service name
like “NT SERVICE\MyServiceName” in kernel?

I just want to see a simple example or anything that tells me how can i convert a string service name
like “MyService” to an SID so i can give it to RtlAddAccessAllowedAce.

Try to use SecLookupAccountName function. This is kernel-mode equivalent of the Win32 LookupAccountName function.
Your service must be registered in system at the time of the function call. There is also “unofficial” way to
get the service SID by it’s name - RtlCreateServiceSid (available only in user mode).

By the way, sometimes you may use pre-calculated security descriptor (it must be in self-relative format).
In driver code it will look like a byte array. Sometimes it is much easier than API “nightmare” with DACL/ACE…

Also what’s the difference between the code that i provided vs using IoCreateDeviceSecure? Doesnt the code above also do the job?

In fact, IoCreateDeviceSecure is a WDK library wrapper that calls ObOpenObjectByPointer, ZwSetSecurityObject and performs
some “magic” in registry. So, both of these options are nearly identical and do the same thing.

I prefer to set security descriptor manually due to some limitations of IoCreateDeviceSecure. For example, this function
supports only subset of SDDL syntax and old implementations like WDK 7600 have certain compatibility problems with
the “Code Integrity” policy on Windows 8 and higher. And also it leaves some garbage in registry…

I read in a blogpost that restricting device objects based on service name is only available from windows 7, is this correct?

May be this is a limitation of IoCreateDeviceSecure itself.

@“Peter_Viscarola_(OSR)” said:
Using IoCreateDeviceSecure (or the INF, which is an entirely better idea) eliminates the race
between creating the device object and setting a protection on it.

This race is usable only if ‘DO_DEVICE_INITIALIZING’ flag has been reset between creating device object and setting it’s security.
Correct me, please, if I am wrong.

This race is usable only if ‘DO_DEVICE_INITIALIZING’ flag has been

Yes, no opens are allowed until DO_DEVICE_INITIALIZING has been cleared.

In an ideal world every customer is using windows 11 but we both know thats not true and many are still using XP/2003.

Sure… There are lots of folks still using systems as old as Windows 7. But Windows XP? You really have a user-based that’s running XP? That OS version is more than 20 years old… and it’s well and truly beyond it’s expiration date. You’re patching a hole in a boat that’s not just leaking, its actually disintegrating. But we digress. I’m sure you have your reasons, and they’re not arbitrary.

therefore there is literally no way for a malware to bypass this other than using a driver.

Hmmm… except for anybody with admin creds, right? ANYhow…

Don’t forget that if this is a PnP Device Object that you’re protecting, there’s also an FDO to take into account…

OK… I think I’ve “helped” here as much as I’m going to be able.

Peter