Checking the user-mode app's digital signature for secure communication with the user-mode?

Hi all,

Can we find the digital signature of the user-mode app inside our kernel mode WDM dispatch function, and check it to make sure the executable file of the user-mode process has our own certificate/signature or not? If so, how can i do this in kernel mode inside my dispatch function? for example inside the handler for irp_mj_device_control, how can i check the digital signature of the user mode app that is sending me the ICOTL?

Therefore make sure that only a executable with our own digital signature can communicate with us.

Regards,
Brad.

Even if you could do this, someone could still load a DLL or inject code into your process, so you would need to prevent that as well.

@antsteel said:
Even if you could do this, someone could still load a DLL or inject code into your process, so you would need to prevent that as well.

I can easily block any malicious user process from even opening a handle to me with ObRegisterCallbacks, let alone injecting into it, blocking this issue is pretty easy. i can also register my process as a protected process.

my main concern right now is to how to properly check the digital signature of a user mode process that send the IOCTL to my driver, in my kernel code?

“check the digital signature” is going to have to be done in user mode, so you will need to pass that request up to a service.

The short answer is that you don’t. The long answer is much longer, but if you are trying to implement some kind of ‘trusted’ process scheme, your viable options are to call out to a hypervisor or external hardware. beyond the ACL, don’t try to implement this kind of security. you will make a lot of very complex code that does not improve your security except with respect to obscurity.

@Mark_Roddy said:
“check the digital signature” is going to have to be done in user mode, so you will need to pass that request up to a service.

What is the best way to find who the certificate is issued to and if the certificate is valid, in user-mode? i assume combining WinVerifyTrust + CertGetCertificateChain will work?

@MBond2 said:
The short answer is that you don’t. The long answer is much longer, but if you are trying to implement some kind of ‘trusted’ process scheme, your viable options are to call out to a hypervisor or external hardware. beyond the ACL, don’t try to implement this kind of security. you will make a lot of very complex code that does not improve your security except with respect to obscurity.

May i ask why? assuming i am also protecting my user-mode process, how can someone bypass this and communicate with my driver then?

using hypervisor and external hardware is not an option for me.

i need to be 100% sure that no other application can communicate with my driver (for example by reversing my code and finding out the encryption method for communication, etc), and I think this is the best way, since only i have the certificate.

You can never be 100% sure. Whatever you do in user-mode can be cracked. So, you do the best you can.

To establish trust in some particular user-mode executable for your driver, Authenticode signatures are not required. You can use public/private key authentication based on the CNG BCrypt* functions (which are available in user-mode and kernel-mode as well):

  • Identify the process that is sending you IOCTLs and the executable file from which it was loaded. (To be honest, I have no idea if this step is trivial or already a problem of its own.)
  • Calculate a hash of this executable file, using BCryptHashData() etc.
  • Locate the signature associated with your preferred executable. (This value will have been calculated during the final steps of building your product setup package, and your installer may have placed it anywhere, for example in the registry.)
  • See if this signature matches your calculated hash, using BCryptVerifySignature().

Regarding the required BCrypt operations, there is a complete sample on GitHub. It uses the DSA signature algorithm, the one algorithm apparently not supported in kernel-mode, but I suppose it will work with other algorithms like BCRYPT_RSA_ALGORITHM just as well.

To protect your code against reverse engineering you can use tools like VMProtect, Code Virtualizer or Enigma Protector. They all can run your critical code in custom VMs with custom RISC Processor instruction sets. You can even protect your critical driver code (VMProtect works best). It still can be cracked but the bar is now much higher.

If the idea is to lock-down the user to driver interface, may I suggest using a Service SID with an appropriate ACL on your Device Object?

Peter

@Tim_Roberts said:
You can never be 100% sure. Whatever you do in user-mode can be cracked. So, you do the best you can.

Well i still believe that this approach will 100% block any unauthorized process from communicating with my driver.
And this is assuming that i protect my own process and executable files from modification. if so, then how can anyone bypass this? its technically not possible. they cant just forge my certificate and sign with it.

@“Peter_Viscarola_(OSR)” said:
If the idea is to lock-down the user to driver interface, may I suggest using a Service SID with an appropriate ACL on your Device Object?

Peter

I dont want to lock down the user mode to driver interface, i mostly want to make sure ONLY my own process/executable can communicate with my driver and issue commands to it.

@pwilly said:
To protect your code against reverse engineering you can use tools like VMProtect, Code Virtualizer or Enigma Protector. They all can run your critical code in custom VMs with custom RISC Processor instruction sets. You can even protect your critical driver code (VMProtect works best). It still can be cracked but the bar is now much higher.

I don’t think using VMProtect on a driver would be 100% stable, specially for drivers. haven’t seen it being used on production drivers before. and still this will only make their job harder, but still not impossible for them to bypass.

@“Wilhelm_Nöker” said:
To establish trust in some particular user-mode executable for your driver, Authenticode signatures are not required. You can use public/private key authentication based on the CNG BCrypt* functions (which are available in user-mode and kernel-mode as well):

  • Identify the process that is sending you IOCTLs and the executable file from which it was loaded. (To be honest, I have no idea if this step is trivial or already a problem of its own.)
  • Calculate a hash of this executable file, using BCryptHashData() etc.
  • Locate the signature associated with your preferred executable. (This value will have been calculated during the final steps of building your product setup package, and your installer may have placed it anywhere, for example in the registry.)
  • See if this signature matches your calculated hash, using BCryptVerifySignature().

Regarding the required BCrypt operations, there is a complete sample on GitHub. It uses the DSA signature algorithm, the one algorithm apparently not supported in kernel-mode, but I suppose it will work with other algorithms like BCRYPT_RSA_ALGORITHM just as well.

But isn’t doing the combination of WinVerifyTrust + CertGetCertificateChain from user-mode easier?

**Now regarding getting the the path to the executable that send me the IOCTL : **

If i use PsGetCurrentProcess in my IRP_MJ_DEVICE_CONTROL dispatch function, will it always give me the EPROCESS for the process that send the IOCTL? because if so, then i can easily get the process path from there.

I dont want to lock down the user mode to driver interface, i mostly want to make sure ONLY my own process/executable can communicate with my driver and issue commands to it.

Sorry
 how are those two things (“locking down the user-to-driver interface” and “mak[ing] sure ONLY my own process/executable can communicate” with a given driver) different? What am I missing? You’re trying to secure the channel against admin privileged users, so using a Service SID won’t work?

If i use PsGetCurrentProcess in my IRP_MJ_DEVICE_CONTROL dispatch function, will it always give me the EPROCESS for the process that send the IOCTL?

Well, yes or no, depending on how paranoid you want to be. And you seem to want to be very paranoid indeed, so “no” – You can’t be 100% guaranteed that your Dispatch Entry Point will be called in the context of the requesting thread/process if there’s a filter driver instantiated above your Device Object.

But you don’t need to do this
 you can get the issuing THREAD from the IRP at Irp->Overlay.Thread – This will be set for IRPs sent from user mode
 so be sure to guard against NULL in this field by first checking Irp->RequestorMode.

Peter

I’m reminded of the Sherlock Holmes story with the dancing man code - what one man can devise another may can decipher. That may not be an exact quote as it is entirely from memory

The import for this particular problem is that any security scheme that you devise, I can always break.
If necessary, we can go into specifics on the approaches that you mention but that’s axiomatic. But the important question is how difficult will it be for me to do so and whether I have the skill. Ask the NSA how much motivated and skilled people can do.

The conclusion is that you have to trust something at some point. And you should trust the OS and the security system built into it. And that means that you should assign an appropriate ACL and call it a day. Because every effort after that provides only illusory increases in security. If I have admin access, I can replace your driver with a decompiled version that omits your check if I want

@brand_H: I don’t think using VMProtect on a driver would be 100% stable, specially for drivers. haven’t seen it being used on production drivers before. and still this will only make their job harder, but still not impossible for them to bypass.
That you haven’t seen it doesn’t mean it doesn’t exist. You wouldn’t see if the driver is protected with VMProtect unless you carefully analyze the driver binary. From our experience it works very well and stable, not a single issue so far.
@MBond2: If I have admin access, I can replace your driver with a decompiled version that omits your check if I want.
If your driver is protected with VMProtect, decompiling is so much harder. You would need to decrypt the driver boot loader and than the VM with it’s custom instruction-set, Then track down all the garbage instructions injected with code mutation and then make sense of it. It can be done, but you need very skilled experts and a lot of time (and money) to do this.
However nothing is 100% secure and anything can be hacked, it’s more a matter of how difficult you make it.

@brad_H said:
I can easily block any malicious user process from even opening a handle to me with ObRegisterCallbacks, let alone injecting into it, blocking this issue is pretty easy. i can also register my process as a protected process.

This keeps honest processes honest. It’s not a guarantee that code that doesn’t play by the rules can’t compromise your process.

@brad_H said:
Well i still believe that this approach will 100% block any unauthorized process from communicating with my driver.
And this is assuming that i protect my own process and executable files from modification. if so, then how can anyone bypass this? its technically not possible. they cant just forge my certificate and sign with it.

This is true. It is also mostly useless. It’s not the at-rest representation you need to worry about, it’s the in-memory image, with all of the address fix-ups and data values, (and possibly injections), that matters. And you won’t be able to assert a lot of that, because of the image in memory will look pretty different than it will on-disk.

@MBond2 said:
I’m reminded of the Sherlock Holmes story with the dancing man code - what one man can devise another may can decipher. That may not be an exact quote as it is entirely from memory

The import for this particular problem is that any security scheme that you devise, I can always break.
If necessary, we can go into specifics on the approaches that you mention but that’s axiomatic. But the important question is how difficult will it be for me to do so and whether I have the skill. Ask the NSA how much motivated and skilled people can do.

The conclusion is that you have to trust something at some point. And you should trust the OS and the security system built into it. And that means that you should assign an appropriate ACL and call it a day. Because every effort after that provides only illusory increases in security. If I have admin access, I can replace your driver with a decompiled version that omits your check if I want

Well, as i said, in this scenario lets assume that i can protect my files and processes from modification (since I’m already in kernel and we assume the attacker doesn’t have a driver, if he did then its obviously game over)

In this scenario, if i am checking the digital signature of the process that is sending me the IOCTL, you CANNOT bypass is, literally. Okay maybe if i used SHA-1 then there is a small chance, but if i am using a SHA-2 digital signature, you CANNOT, and i repeat, CANNOT bypass this without having some sort of super computer to forge my digital signature
 I am more than happy to see if you can provide me any type of attack that can bypass my protection.

And again, we assume that the attacker doesn’t have a driver to begin with, and wants to use the functionality of my driver for exploit or something else, and we assume that i am properly protecting my processes and files using callbacks and minifilters, in this scenario i am 100% sure you cannot bypass this and communicate with my driver unless you break SHA-2


@pwilly said:

@brand_H: I don’t think using VMProtect on a driver would be 100% stable, specially for drivers. haven’t seen it being used on production drivers before. and still this will only make their job harder, but still not impossible for them to bypass.
That you haven’t seen it doesn’t mean it doesn’t exist. You wouldn’t see if the driver is protected with VMProtect unless you carefully analyze the driver binary. From our experience it works very well and stable, not a single issue so far.

But Have you seen VMprotect or similar VM based obfuscator being used in any known production driver, other than those crazy Anti-Cheat companies? I assume some of them could be using this, but as far as i heard they do so many crazy undocumented stuff that shows they really don’t care about producing a stable driver, so I’m excluding them from this question.

For example have you seen any Anti virus company use VMprotect? I’m pretty sure non of them do, because they want to have a stable driver.

@“Peter_Viscarola_(OSR)” said:

I dont want to lock down the user mode to driver interface, i mostly want to make sure ONLY my own process/executable can communicate with my driver and issue commands to it.

Sorry
 how are those two things (“locking down the user-to-driver interface” and “mak[ing] sure ONLY my own process/executable can communicate” with a given driver) different? What am I missing? You’re trying to secure the channel against admin privileged users, so using a Service SID won’t work?

But why should i use Service SID, when i can use the digital signature of the executable and be 100% sure that no other executable can communicate with me? I’m not saying using Service SID is bad, But obviously digital signatures, specially SHA-2, are mathematically proven to be extremely hard (if not Impossible) to forge/bypass. so why shouldn’t everyone use this approach to be 100% sure?

This topic is important, because i think as you know, the amount of drivers out there that are not properly checking the ICOTLs, and therefore making their drivers exploitable, is vast, and driver developers really need to come up with a solid plan that can block any type of unauthorized user mode app from communicating with them, that can be proven to be impossible to bypass (or at least block 99.99999% of attacks)