How to restrict IOCTL and socket communications with a driver to a verified process

Typically the restriction is to a group sid, not a user sid, and you restrict access to that group, typically using various AD tools

Or, you want to restrict the access to your device/driver to a given SERVICE and you run that service with a unique SERVICE SID.

You should really think carefully before you put a whole bunch of policy in kernel-mode. In general, architecturally, this is not where policy like this belongs.

Peter

@Tim_Roberts said:
At the risk of being disrespectful, you also need to stand back and ask yourself honestly how much protection is really warranted. Security is a sliding scale – you can spend as much money as you have. It doesn’t matter what you come up with, a dedicated hacker can get through it. You have to weigh the cost of your protection solution against the likelihood and cost of a violation. Is anyone REALLY going to try to use your driver directly? The typical user won’t even try, of course. And if there was a violation, what would be the cost?

I understand that completely but this question actually has nothing to do with a real life project. The question is just for me to learn how to do it, simply because I found it interesting. I thought there was a standard way to only allow the program the was created by the driver developer to communicate with it. This does not seem to be the case, however.

Now I have my services window open and am wondering how many of these drivers with an attack like this in mind. If every process could (theoretically) communicate with every driver, that would be a problem. We have patch guard along with driver signature enforcement, I thought this would provide some level of integrity to make a standard like this possible.

I may have failed to make it clear, but in the question I meant to assume a 100% integrity of the kernel space, I’m sure this must be possible, if so.

@“Peter_Viscarola_(OSR)” said:

Typically the restriction is to a group sid, not a user sid, and you restrict access to that group, typically using various AD tools

Or, you want to restrict the access to your device/driver to a given SERVICE and you run that service with a unique SERVICE SID.

You should really think carefully before you put a whole bunch of policy in kernel-mode. In general, architecturally, this is not where policy like this belongs.

Peter

I will look into this.

Thank you

If every process could (theoretically) communicate with every driver, that would be a problem
But every process can’t as the security model for windows is based on users and groups and devices are generally restricted to the admin group. Step one is to lock down admin access.

1 Like

@Mark_Roddy said:

If every process could (theoretically) communicate with every driver, that would be a problem
But every process can’t as the security model for windows is based on users and groups and devices are generally restricted to the admin group. Step one is to lock down admin access.

I think now I have a better understanding or maybe not.

So the solutions here will make it difficult for a random process on a random machine to communicate with a random driver, or even with a driver that is brought in with the process (as is the case with some malware). But this will not prevent someone from willingly giving their own program privileges (or whatever it takes) to communicate with a driver, and this is that part that is impossible to 100% prevent even with the assumption of 100% kernel integrity, because the user could simply emulate/duplicate what the legitimate process does to communicate with the driver, right? If so, then why not use certificates, since you can’t fake them, meaning if the driver’s code is assumed to be 100% secure, then problem solved.

If you insist on giving users on a system admin rights then sure, there is no way to secure that system. So don’t do that. You’ve already decided that you are going to verify your process by using a cert, so there doesn’t seem to be much point in trying to persuade you otherwise. However as you have given your users admin rights I’m going to exploit that by using detours style interception to insert my malware to f up your driver into your running special cert protected process.

… then why not use certificates, since you can’t fake them …

But you can certainly copy them. What does the phrase “use certificates” mean to you? Are you going to pass a certificate through an ioctl? That’s easily copied. Are you going to require the executable to be digitally signed, and check the checksum at runtime? Could be done, but it’s a pain, since you have to update the kernel driver every time the app changes.

It’s not a trivial problem, by any means.

1 Like

You’ve already decided that you are going to verify your process by using a cert
I’m absolutely not going to do that. I’m arguing for it merely because I couldn’t understand how security policies could prevent replication attacks by the user/admin themselves. Now I understand it is not as bad if only someone who is specifically targeting my driver could do it. Because if someone could find a way to communicate with the driver on their machine and turn it into a harm driver, that is fine as long as they can’t do it on any machine.

Are you going to require the executable to be digitally signed, and check the checksum at runtime
This is exactly what I meant. Isn’t that how driver signature enforcement does it? This was my first thought.

since you have to update the kernel driver every time the app changes.
You’re completely right, I oversaw that.

Or, you want to restrict the access to your device/driver to a given SERVICE and you run that service with a unique SERVICE SID.
This also sounds interesting. I’ve never written a service for Window, I’ll definitely try that as well, there is never harm in learning more.

Ok, so you validated that your process is correctly signed. However I’ve put a filter driver in your device stack and I’m going to hijack your process context to send my malware into your driver.

filter driver in your device stack

I was trying to understand, if someone was able to communicate with my driver on their own machine (given they can do whatever it takes on that machine i.e. they can disable DSE, enable debug mode, attaching kernel debuggers, bytepatch the kernel etc.), would they then be able to do the same on random people’s machines with all the aforementioned default Windows restrictions and limitations (forget anti-viruses).

Now I understand it is not as bad if only someone who is specifically targeting my driver could do it. Because if someone could find a way to communicate with the driver on their machine and turn it into a harm driver, that is fine as long as they can’t do it on any machine.

If the only way for you to attack a my driver is to be able to violate the Windows’ kernel integrity, then I’m completely in the clear, since you may easily do it on your machine but it is not an easy task to do on random people’s PCs who (in this hypothetical scenario) use my driver. It’s not my duty to prevent kernel attacks anyway, unless I was working on a AV, which is probably not the case.

If the only way for you to attack a my driver is to be able to violate the Windows’ kernel integrity
But it isn’t. As I noted earlier. I can hijack your process by code injection in user mode.

I can hijack your process by code injection in user mode.
Not if the process is protected by the kernel driver on initialization.

The “if the process is protected” is the tricky part. How do you know which process to protect?

Process hollowing/doppleganging/herpaderping/ghosting are hard to deal with (see https://www.microsoft.com/en-us/security/blog/2022/06/30/using-process-creation-properties-to-catch-evasion-techniques/). And, even better, if I fake you out into thinking that my process is your “special process” then you’ll even protect me from being tampered with by anything else ?

Note that a Windows solution for this is having a PPL. It attacks the problem a little differently by ensuring you can create a service that only runs code signed by you or Microsoft. Combine that with a Service SID and you’re pretty hardened.

https://learn.microsoft.com/en-us/windows/win32/services/protecting-anti-malware-services-

even assuming that your special process is perfectly protected during initialization, all UM VA includes data that does not exactly match the disk image - and it is infeasible to ‘protect’ all of the loaded DLLs and relocation information. So yes, I can hijack your special process in UM after you have verified the image. And I can do it not just on my machine with a debugger hooked up, but on any other machine that has my software installed.

The short version is to use the security model implemented by the OS

I’m still arguing for a solution that I will not end up using just to learn what is wrong with it.

I fake you out into thinking that my process is your “special process”
Good luck finding a preimage for my signed sections.

UM VA includes data that does not exactly match the disk image
This doesn’t apply to all sections. Specifically, the .text section is mapped as is. I know you could probably find a way around it by adding sections etc. but I wonder if the Windows security model (without the PPL which costs money) prevents all the mentioned attacks!

Note that a Windows solution for this is having a PPL. It attacks the problem a little differently by ensuring you can create a service that only runs code signed by you or Microsoft. Combine that with a Service SID and you’re pretty hardened.

That’s the approach I’m going to take. I just still don’t understand why the service SID is tied to its name according to this https://pcsxcetrasupport3.wordpress.com/2013/09/08/how-do-you-get-a-service-sid-from-a-service-name/

Can’t any service start with any name it wants? How does the driver distinguish?

services can’t start with arbitrary names. Services can only start with names that have been preconfigured in the registry and the parameters passed to StartServiceCtrlDispatcher must match with those that the SCM expects when it launched the process or send a control to a shared process service. The SIDs derive from the names because they must be unique and must be installed by an admin

If I have access to your software, and a debugger, I will surely be able to find a match to your ‘super special’ section in minutes - a few hours at the most. At worst, I just use your whole image, but the start execution somewhere else that never calls your code. you can’t protect against that kind of attack without also making your software unusable after a Windows update

I don’t quite understand what login means in this context, could provide a link?

The SCM has to start every service process in the context of some security principal. When users start ordinary processes using the start menu or shortcuts, those processes inherit the security principal of the process that launches them - usually explorer.exe running as the the logged in user.

but the SCM runs as LOCAL SYSTEM, but not every service should. When it must run a service that should run under a different security principal, it must login as that principal in order to generate a token that can then be used by that process to arbitrate its access to various resources. For some accounts, it can use its TCB Name (act as part of the operating system) privileges, but for others it must rely on a saved password

Does that mean I need to make my client process “login” (as unique? user x? with login credentials?) in order for it to communicate with the driver?

what you should do is create a special user account for your service and set the ACL so that only that user can open a handle to your driver.