Windows System Software -- Consulting, Training, Development -- Unique Expertise, Guaranteed Results

Home NTDEV

More Info on Driver Writing and Debugging


The free OSR Learning Library has more than 50 articles on a wide variety of topics about writing and debugging device drivers and Minifilters. From introductory level to advanced. All the articles have been recently reviewed and updated, and are written using the clear and definitive style you've come to expect from OSR over the years.


Check out The OSR Learning Library at: https://www.osr.com/osr-learning-library/


Before Posting...

Please check out the Community Guidelines in the Announcements and Administration Category.

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

brad_Hbrad_H Member Posts: 69

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.

«1

Comments

  • antsteelantsteel Member Posts: 10
    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.
  • brad_Hbrad_H Member Posts: 69
    edited April 28

    @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?

  • Mark_RoddyMark_Roddy Member - All Emails Posts: 4,440

    "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.

  • MBond2MBond2 Member Posts: 344

    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.

  • brad_Hbrad_H Member Posts: 69

    @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?

  • brad_Hbrad_H Member Posts: 69

    @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.

  • Tim_RobertsTim_Roberts Member - All Emails Posts: 14,028

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

    Tim Roberts, [email protected]
    Providenza & Boekelheide, Inc.

  • Wilhelm_NökerWilhelm_Nöker Member Posts: 44

    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.

  • pwillypwilly Member Posts: 16

    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.

  • Peter_Viscarola_(OSR)Peter_Viscarola_(OSR) Administrator Posts: 8,560

    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

    Peter Viscarola
    OSR
    @OSRDrivers

  • brad_Hbrad_H Member Posts: 69

    @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.

  • brad_Hbrad_H Member Posts: 69
    edited April 29

    @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.

  • Peter_Viscarola_(OSR)Peter_Viscarola_(OSR) Administrator Posts: 8,560

    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

    Peter Viscarola
    OSR
    @OSRDrivers

  • MBond2MBond2 Member Posts: 344

    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

  • pwillypwilly Member Posts: 16

    @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.

  • Phil_BarilaPhil_Barila Member - All Emails Posts: 154

    @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.

  • brad_Hbrad_H Member Posts: 69

    @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.....

  • brad_Hbrad_H Member Posts: 69

    @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.

  • brad_Hbrad_H Member Posts: 69

    @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)

  • pwillypwilly Member Posts: 16
    edited April 30

    @brad_H: But Have you seen VMprotect or similar VM based obfuscator being used in any known production driver...?

    Well, we use it in production since a couple of years, never had any issues. We protect only a few critical functions to protect our IP from reverse engineering.

  • Wilhelm_NökerWilhelm_Nöker Member Posts: 44

    @brad_H said:

    @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):

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

    Like you said, it's user-mode, and the result is needed in your driver. But if you're comfortable with the techniques required to have a driver delegate some task to a user-mode component, then sure, it's easier that way. I was suggesting a technique that could run completely in kernel-mode.

    **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've used PsGetCurrentProcess myself in a COM port driver to let an associated diagnostic tool know which process is keeping the COM port open, and it's certainly good enough for that purpose. But I did most of the remaining work in user-mode (OpenProcess, EnumProcessModules, GetModuleBaseName), so I just don't know how this would be done in kernel-mode, and whether it's easy or not. That's all.

  • MBond2MBond2 Member Posts: 344

    Well, let's assume that you can actually protect your files and processes - so what more do you need to do? What extra value does this scheme provide?

    If a system administrator is determined enough and skillful enough, he will break anything that you attempt to do. The folks at the NSA have proved this point by pre-calculating all of the possible prime factors for common RSA encryption strengths - and that's what we know that they could do about 10 years ago.

    and what does verifying an on disk image versus a signature have with respect to a loaded module that must necessarily have DLL imports and thunks resolved? Even if the on disk signature is valid, any in process or out of process actor can arbitrarily change the behavior of the binary by replacing the code bytes. And those changes can't be detected by checking the signature of the disk image, and checking the 'signature' of the in memory image is impossible, so what's the plan?

    This may thwart the most infantile attacks - but so does a simple ACL. And it will fail utterly under sophisticated attacks - as will a simple ACL. Possibly achieving some nebulous mid level attack protection at the cost of a huge amount of extra complexity?

  • 0xrepnz0xrepnz Member Posts: 73
    edited May 1

    @brad_H

    In this scenario, if i am checking the digital signature of the process that is sending me the IOCTL, you CANNOT bypass is, literally.

    This is incorrect. First of all, an admin can install any driver and circumvent any protection you will come up with, and this is pretty easy when there are many third party signed drivers that simply provide an IOCTL interface to read/write memory in kernel mode - you don't even have to sign your own driver. This allows the attacker to install any driver and "remove" your ObRegisterCallbacks protection for example or do whatever he wants.

    Moreover, You will soon realize that the suggested "ObRegisterCallbacks protection" is never a 100% protection since there are legitimate windows services that need to open a handle to your process for all sorts of purposes, so you will have to allow them to open a handle if you don't want to cause serious system instability - meaning that if someone injects to these processes (such as a critical svchost) he will be able to inject into your "signed" IOCTL caller from there.

    What you are doing here is security by obscurity, which probably isn't worth it (Depending on who you ask and on your specific requirements, different people have different opinions about this subject). You need to understand that if someone tried to write code to invoke an IOCTL in your driver, he will probably also be able to inject code to your "signed" process and run it from there if he wants - it's not hard.

    For example, just to show you these things are pretty easy and well known, look at this project here: https://github.com/notscimmy/libelevate

    Just to summarize: If I were you, I would use an ACL to protect the device, and that's it.

    - Ori Damari
  • 0xrepnz0xrepnz Member Posts: 73

    Also, Injection can be done without opening a handle to your process - I guess you have dependencies on DLLs in your "signed app" right? Remember an admin attacker can simply replace any DLL on disk with his own DLL..

    - Ori Damari
  • brad_Hbrad_H Member Posts: 69

    @0xrepnz said:
    @brad_H

    In this scenario, if i am checking the digital signature of the process that is sending me the IOCTL, you CANNOT bypass is, literally.

    This is incorrect. First of all, an admin can install any driver and circumvent any protection you will come up with, and this is pretty easy when there are many third party signed drivers that simply provide an IOCTL interface to read/write memory in kernel mode - you don't even have to sign your own driver. This allows the attacker to install any driver and "remove" your ObRegisterCallbacks protection for example or do whatever he wants.

    Although i agree that if a user is already an admin, at the end of the day he can always bypass protections, but you have to realize my goal here is not to secure a system from malicious users, but only to do my job as a driver developer of securing my communications. Just don't want my driver to end up as one of those poorly written drivers that gets used by rootkits as a way to bypass driver signature enforcement. if i make it extremely difficult for them use my driver for malicious activities, then they will basically switch to another driver.

  • Peter_Viscarola_(OSR)Peter_Viscarola_(OSR) Administrator Posts: 8,560

    my goal here is not to secure a system from malicious users, but only to do my job as a driver developer

    Then your efforts are being badly misdirected.

    Put an ACL on you device object, as has been suggested here multiple times, and you’re done. That’s considered the standard for best practice. Anything beyond that is just window dressing, unless you want to go with a full-on threat analysis and a set of countermeasures focus on your identified threats.

    Peter

    Peter Viscarola
    OSR
    @OSRDrivers

  • MBond2MBond2 Member Posts: 344

    Remember too that an admin can arbitrarily affect the way that signatures get checked and which ones are acceptable. Exceptions exist, but administrators routinely take advantage of this to trust their internal private certificates in addition to those that the public CAs provide.

    An ACL check is no more or less secure than a signature check, but they check different things. A signature check is like a more sophisticated CRC - excellent for detecting corruption or unauthorized modification, but almost irrelevant for security. ACLs don't even attempt to detect corruption, but they do enforce security across security boundaries. The assumption has to be made that the code enforcing the security boundary has not been compromised. Think about it and you'll understand why

  • 0xrepnz0xrepnz Member Posts: 73
    edited May 3

    Just don't want my driver to end up as one of those poorly written drivers that gets used by rootkits as a way to bypass driver signature enforcement

    The main issue about "security" is that it's almost always a trade-off with something else such as performance, reliability, stability, usability, etc. There is no supported way to perform "WinVerifyTrust" from kernel mode so basically implementing this will require a complex piece of code to develop, maintain and support. I think that in this case, the quality of your driver is much more important than stopping some rootkit developer from abusing your driver. As you said, they will simply move to the next driver they have so what's the point? I think it's not worth the potential issues you will encounter in this complex piece of code. Moreover, as we already agreed, this is not really any kind of security boundary you're trying to protect here, it's just a trick you want to do in order to "warn off" rootkit developers..

    - Ori Damari
  • antsteelantsteel Member Posts: 10
    > @brad_H said:
    > Although i agree that if a user is already an admin, at the end of the day he can always bypass protections, but you have to realize my goal here is not to secure a system from malicious users, but only to do my job as a driver developer of securing my communications. Just don't want my driver to end up as one of those poorly written drivers that gets used by rootkits as a way to bypass driver signature enforcement. if i make it extremely difficult for them use my driver for malicious activities, then they will basically switch to another driver.

    As long as you don't add IOCTLs that allow to read/write all of physical memory like some of these poorly written drivers, then you should be fine.
  • antsteelantsteel Member Posts: 10
    If you want to know what you should not do, you can check out the drivers in this repo https://github.com/hfiref0x/KDU
This discussion has been closed.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Upcoming OSR Seminars
OSR has suspended in-person seminars due to the Covid-19 outbreak. But, don't miss your training! Attend via the internet instead!
Developing Minifilters 24 May 2021 Live, Online
Writing WDF Drivers 14 June 2021 Live, Online
Internals & Software Drivers 27 September 2021 Live, Online
Kernel Debugging 15 November 2021 Live, Online