A forum for reverse engineering, OS internals and malware analysis 

Forum for discussion about kernel-mode development.
 #3636  by LiatLevontin
 Mon Nov 22, 2010 9:54 am
Hi

I'm writing a kernel driver which is crucial and going to do some security tasks. I don't want someone else to use my driver. How can I encrypt traffic in IOCTL between my user-mode application and kernel mode driver? Is there any protection for drivers to just allow it's usage to some specific programs? Any trick, method, etc.?

For example, how Kaspersky does it? Could I use kasper's driver to protect my own PID or kill a process or do some tasks? I don't think it's possible. So how Kasper achieved it? Please advice

Thanks
 #3642  by a_d_13
 Mon Nov 22, 2010 1:31 pm
Hello,

There are several methods of protecting a kernel driver from being used. The first method is to create a device that is secured - i.e. has an appropriate ACL. You can do this with the IoCreateDeviceSecure function. It allows you to specify the security settings for the device object. I'd recommend setting "Exclusive" to TRUE, so only one handle can be open at a time. Specify something like "SDDL_DEVOBJ_SYS_ALL_ADM_ALL " for the "DefaultSDDLString" parameter, which grants any code running with System or Administrator privileges access to the device.

However, if you are trying to prevent someone else from copying your driver file, reverse-engineering the IOCTL codes and then using it to do bad things, it's harder. I would recommend implementing a signature check of some sort. It's not possible (as far as I know) to check a digital signature of a file from kernel-mode, but your driver could require that a certain signature is passed in via an IOCTL before it allows any other IOCTLs to execute. The best solution is to not have potentially malicious code in your driver at all, or to perform sanity checks on all operations.

Thanks,
--AD
 #3645  by LiatLevontin
 Mon Nov 22, 2010 5:15 pm
Thanks for your reply at first...

About IoCreateDeviceSecure, that's pretty good method, but it works if my driver was going to work always. But it's not true. My driver is only when my program is executed by user. So what do you suggest now about my situtation? Driver etc. is not important. I want to stop loading or sending data to my driver by 3rd party application.

Best Regards
 #3649  by Mehdi
 Tue Nov 23, 2010 7:02 am
Hi
I think LiatLevontin wants to protect its IOCTLs from misuse by other unauthorized user apps (which is also my question)
(for example if we want to close a handle or delete a file, we send an IOCTL to the kernel driver with required information; now how we can know that the IOCTL is coming from our program, not some malicious app)
I think another workaround that can harden the job of attackers, is to protect IOCTLs from audit/sniff by tools such as DeviceIoView/IrpTracker/etc
 #3655  by GamingMasteR
 Tue Nov 23, 2010 3:03 pm
Read again :
a_d_13 wrote:I would recommend implementing a signature check of some sort. It's not possible (as far as I know) to check a digital signature of a file from kernel-mode, but your driver could require that a certain signature is passed in via an IOCTL before it allows any other IOCTLs to execute. The best solution is to not have potentially malicious code in your driver at all, or to perform sanity checks on all operations.
However, the attacker can RE the driver and fool it if he make up his mind to do this .
 #3659  by gglittle
 Tue Nov 23, 2010 7:49 pm
You can open a handle to the driver as exclusive, then have your app, or service, grab that handle as early as possible. That does not shut the door on some one determined to acquire the handle before you, but it does limit the time window when malware might acquire the handle. Use app to driver authentication, as has been suggested, possibly utilizing a TPM, to authenticate user access of your driver. Use the safe string functions found in ntsafe.h and other secure coding practices to limit your attack surface.

Mostly, don't allow malware on, or malicious people access to your system . Realize you can only do so much, and nearly all efforts can be defeated given time and a determined effort.

Gary
 #3660  by Alex
 Tue Nov 23, 2010 7:52 pm
About IoCreateDeviceSecure, that's pretty good method, but it works if my driver was going to work always. But it's not true. My driver is only when my program is executed by user. So what do you suggest now about my situtation? Driver etc. is not important. I want to stop loading or sending data to my driver by 3rd party application.
I'm afraid that it is rather impossible to fully protect a device/driver if it is load dynamically. My first idea was to load the driver automatically and catch process creation events (PsSetCreateProcessNotifyRoutine) and than check checksum(s) (some sort of signatures) of code section (and/or something else) of loaded user module. When the appropriate module (process) will be detected, it can be protected from kernel mode by the process security modifications Keeping Secrets - Windows Security (Part III)or by hooking few sensitive services (NtOpenProcess, NtDuplicateObject, ...) or by using DKOH method. The driver should also remember the address of the process object which was previously cached and use it at the beginning of I/O dispatcher code to be sure that IOCTL only comes from our process.
How can I encrypt traffic in IOCTL between my user-mode application and kernel mode driver?
You can use alternative method of UM-KM communication. For example Peter Silberman's & Jamie Butler's RAIDE (RAIDE: Rootkit Analysis Identification Elimination) uses shared memory with encrypted data (slide 31) instead of traditional communication channel.

The kernel/user module can be also packed/protected and obfuscated by custom protector like in Rustock rootkic case.
 #3684  by EP_X0FF
 Wed Nov 24, 2010 9:58 am
Here is the some source that doing IoCreateDeviceSecure alike job so only SYSTEM and admins will be allowed to open target device from UM. Additionally to protect your driver from unauthorized access you need to randomize IOCTL's, encrypt input/output buffers, add a lot of sanity checks inside etc. However if we are talking about commercial application, you can take a rest and spare your time. If attacker will RE your driver, nothing helps.
Code: Select all
NTSTATUS NTAPI VxSetDefaultSecurity(HANDLE hObject)
{
    SID_IDENTIFIER_AUTHORITY Authority = SECURITY_NT_AUTHORITY;
    PSID                     AdmSid    = NULL;
    PSID                     SysSid    = NULL;
    PACL                     SysAcl    = NULL;
    ULONG                    DaclSize  = 0;
    NTSTATUS                 Result;
    SECURITY_DESCRIPTOR      SecurityDescriptor;
  
    do
    {
        AdmSid = mmalloc(RtlLengthRequiredSid(2));
        SysSid = mmalloc(RtlLengthRequiredSid(1));

        if ( (AdmSid == NULL) || (SysSid == NULL) ) {
            
            Result = STATUS_UNSUCCESSFUL; 
            break;
        }

        RtlInitializeSid(AdmSid, &Authority, 2);
        RtlInitializeSid(SysSid, &Authority, 1);

        RtlSubAuthoritySid(AdmSid, 0)[0] = SECURITY_BUILTIN_DOMAIN_RID;
        RtlSubAuthoritySid(AdmSid, 1)[0] = DOMAIN_ALIAS_RID_ADMINS;
        RtlSubAuthoritySid(SysSid, 0)[0] = SECURITY_LOCAL_SYSTEM_RID;

        DaclSize = sizeof(ACL) + (2 * sizeof(ACCESS_ALLOWED_ACE)) +
            SeLengthSid(AdmSid) + SeLengthSid(SysSid) + 8;

        if ( (SysAcl = (PACL)mmalloc(DaclSize)) == NULL) {
            
            Result = STATUS_INSUFFICIENT_RESOURCES; 
            break;
        }
        
        Result = RtlCreateAcl(SysAcl, DaclSize, ACL_REVISION);

        if (!NT_SUCCESS(Result))
            break;

        Result = RtlAddAccessAllowedAce(
            SysAcl, ACL_REVISION, GENERIC_ALL, SysSid);

        if (!NT_SUCCESS(Result))
            break;

        Result = RtlAddAccessAllowedAce(
            SysAcl, ACL_REVISION, GENERIC_ALL, AdmSid);

        if (!NT_SUCCESS(Result))
            break;

        Result = RtlCreateSecurityDescriptor(&SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION1);

        if (!NT_SUCCESS(Result))
            break;
       
        Result = RtlSetDaclSecurityDescriptor(&SecurityDescriptor, TRUE, SysAcl, FALSE);

        if (!NT_SUCCESS(Result))
            break;

        Result = ZwSetSecurityObject(hObject, DACL_SECURITY_INFORMATION, &SecurityDescriptor);

    } while (0);

    if (SysAcl != NULL) mmfree(SysAcl); 
    if (AdmSid != NULL) mmfree(AdmSid); 
    if (SysSid != NULL) mmfree(SysSid); 

    return Result;
}
 #3856  by LiatLevontin
 Mon Dec 06, 2010 8:19 pm
So for example for Kaspersky, why an attacker doesn't try to open Kaspersky driver handle, send an IOCTL to kill a process, unprotect a process, etc. etc. for bypassing Kaspersky itself? I'm sure they have a function in kernel for such stuff, like terminating process, killing a file, etc. etc.

What do you think?
 #3857  by r2nwcnydc
 Mon Dec 06, 2010 10:52 pm
First, I would create a random name for the device each time you load driver. Only your program and the driver need to know the device name, so that should not be a problem.
Second, I would require a random passphrase (which again would be created each time you load the driver), which would be used to authenticate your application as the proper authority.

The first step prevents a user from writing a generic application (simply providing a known device name) to clean any system that might be running your driver. However, they could still brute force the driver name by listing all loaded devices on the system, and trying your kill command for each device.
The second step prevents the kill command from being performed as the user needs to guess the random passphrase. The longer / more random the passphrase the longer a brute force approach would take.

Hope this helps.