YARA sigs for security best practices

2017.07.24

RSS feed

In the latest Downclimb, I mentioned that you should hunt for software that is not compiled with best practices with regard to security in the same way you should hunt for malware. This came up due to an exploitable vuln in Counter Strike and other games which would have been more difficult to exploit had best practices been used.

Specifically, you should look for software that is not compiled with DEP or ASLR which are exploit mitigation features that come by default when you compile code with a modern compiler. This is easy to look for, and if it is missing, it likely indicates other problems with that software. If software does not have those settings then you should either get rid of it, work with the vendors to improve their software, or take this information into account when deciding between vendors. For software vendors, you should ensure checking for these features is part of your release process: Lack of DEP and ASLR in any executable is a failed build.

I created Serene to make these checks easy. However, in order to make it easier to hunt, you may want YARA sigs for this. This post shows how to write these YARA signatures, along with the final signatures.

Parsing executables for DEP and ASLR support

Both DEP and ASLR are enabled via bits in the PE header that tell the OS to enable those features. Using Kaitai Struct, we can parse a sample executable, such as Google Chrome (from C:\Program Files (x86)\Google\Chrome\Application\chrome.exe on a Windows system). On the left hand side choose the format kaitai.io->formats->executable->microsoft_pe.ksy then drag and drop Chrome. Then in the "object tree" window, look at the value for optionalHdr->windows->dllCharacteristics which should show 0xC160.

Kaitai screenshot of dllCharacteristics for Chrome

Thank you @kaitai_io for Kaitai Struct!

This value encodes the relevant features by enabling/disabling each bit as described by Microsoft in the MSDN here. The setting IMAGE_DLLCHARACTERISTICS_NX_COMPAT at 0x0100 determines whether DEP is enabled for an executable. The setting IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE at 0x0040 determines if ASLR is enabled.

This encoding is made more clearly in my old IceBuddha project. Unfortunately, I no longer maintain that project, but it should work for any PE files.

IceBuddha screenshot of dllCharacteristics for Chrome

The value 0xC160 in binary is 1100000101100000. The values 0x0100 and 0x0040 for DEP and ASLR are 0000000100000000 and 0000000001000000 so applying some simple boolean ANDs we get:

1100000101100000 &
0000000100000000
=
0000000100000000   // Value is non-zero, so DEP is enabled

and

1100000101100000 &
0000000001000000
=
0000000001000000   // Value is non-zero, so ASLR is enabled

You can see that the final values are non-zero, and thus DEP and ASLR are enabled.

Writing the YARA sig

YARA has a module for PE files, called PE, which makes things much easier to avoid having to do some of the offset math. Using this, checking if DEP is on is as simple as:

pe.dll_characteristics & pe.NX_COMPAT == 0 

However, to reduce false positives, we'll do some filtering:

  1. We only want to scan PE files as indicated by an "MZ" header (pretty standard).
  2. We want to make sure there is actually code in the file. Some applications use DLL's that only contain data, called MUI files, and whether or not those have security features isn't important.
  3. We'll want to ignore driver (ie. .sys) files, since these concepts don't really apply to kernel code.

Next, DEP support only matters for the main executable (the .exe file) and not the DLL's, so when we check for DEP, we can ignore DLL's. Also, all 64-bit processes have DEP enabled, so we only want to check 32-bit executables.

With that, here is the final rule set, with an additional check to look for 32-bit executables, since ASLR doesn't do much for 32-bit executables.

YARA signature to check for DEP and ASLR

import "pe"

private rule IsPE {
    condition:
        // If it is a PE file (MZ header at offset 0, 
        //    and PE signature at offset stored in header at 0x3C)

        uint16(0) == 0x5A4D 
        and uint32(uint32(0x3C)) == 0x00004550

        // Also ensure the file contains executable code
        // We do this by checking SizeOfCode.
        // If non-zero, it means there is executable code in the file.
        // To get there we need to get to the start of the IMAGE_OPTIONAL_HEADER
        // which is at: e_lfanew + sizeof(IMAGE_NT_HEADER.magic) + sizeof(IMAGE_FILE_HEADER)
        // then this value starts at offset 4 in the structure.

        and uint32(uint32(0x3C) + 4 + 20 + 4) != 0 

        // Also ignore driver (ie. .sys) files
        and pe.characteristics & pe.SYSTEM == 0
}


rule Not_64_bit {
    condition:
        // Check machine type for whether this code is 32-bit or 64-bit
        IsPE and pe.machine == pe.MACHINE_I386
}


rule DEP_missing
{
    meta:
        description = "DEP is not enabled"

    condition:
       IsPE and pe.dll_characteristics & pe.NX_COMPAT == 0 

       // All 64-bit executables have DEP, so only check 32-bit
       and pe.machine == pe.MACHINE_I386

       // Also ensure this is not a DLL
       and pe.characteristics & pe.DLL == 0
}

rule ASLR_missing
{
    meta:
        description = "ASLR is not enabled"

    condition:
       IsPE and pe.dll_characteristics & pe.DYNAMIC_BASE == 0
}