Monday, August 28, 2017

Application of Authenticode Signatures to Unsigned Code

Attackers have been known to apply legitimate digital certificates to their malware, presumably, to evade basic signature validation utilities. This was the case with the Petya ransomware. As a reverse engineer or red team capability developer, it is important to know the methods in which legitimate signatures can be applied to otherwise unsigned, attacker-supplied code. This blog post will give some background on code signing mechanisms, digital signature binary formats, and finally, techniques describing the application of digital certificates to an unsigned PE file. Soon, you will also see why these techniques are even more relevant in research that I will be releasing next month.


What does it mean for a PE file (exe, dll, sys, etc.) to be signed? The simple answer to many is to open up the file properties on a PE and if a “Digital Signatures” tab is present, it means it was signed. When you see that the “Digital Signatures” tab is present on a file, it actually means that the PE file was Authenticode signed, which means within the file itself there is a binary blob of data consisting of a certificate and a signed hash of the file (more specifically, the Authenticode hash which doesn’t consider certain parts of the PE header in the hash calculation). The format in which an Authenticode signature is stored is documented in the PE Authenticode specification.

Many files that one would expect to be signed, however, (for example, consider notepad.exe) do not have a “Digital Signatures” tab. Does this mean that the file isn’t signed and that Microsoft is actually shipping unsigned code? Well, it depends. While notepad.exe does not have an Authenticode signature embedded within itself, in reality, it was signed via another means - catalog signing. Windows contains a catalog store consisting of many catalog files that are basically just a list of Authenticode hashes. Each catalog file is then signed to attest that any files with matching hashes originated from the signer of the catalog file (which is Microsoft in almost all cases). So while the Explorer UI does not attempt to lookup catalog signatures, pretty much any other signature verification tool will perform catalog lookups - e.g. Get-AuthenticodeSignature in PowerShell and Sysinternals Sigcheck.

Note: The catalog file store is located in %windir%\System32\CatRoot\{F750E6C3-38EE-11D1-85E5-00C04FC295EE}

In the above screenshot, the SignatureType property indicates that notepad.exe is catalog signed. What is also worth noting is the IsOSBinary property. While the implementation is not documented, this will show “True” if a signature chains to one of several known, hashed Microsoft root certificates. Those interested in learning more about how this works should reverse the CertVerifyCertificateChainPolicy function.

Sigcheck with the “-i” switch will perform catalog certificate validation and also display the catalog file path that contains the matching Authenticode hash. The “-h” switch will also calculate and display the SHA1 and SHA256 Authenticode hashes of the PE file (PESHA1 and PE256, respectively):

sigcheck -q -h -i C:\Windows\System32\notepad.exe
  Verified:       Signed
  Catalog:        C:\WINDOWS\system32\CatRoot\{F750E6C3-38EE-11D1-85E5-00C04FC295EE}\
    Microsoft Windows
      Status:         Valid
      Valid Usage:    NT5 Crypto, Code Signing
      Serial Number:  33 00 00 01 06 6E C3 25 C4 31 C9 18 0E 00 00 00 00 01 06
      Thumbprint:     AFDD80C4EBF2F61D3943F18BB566D6AA6F6E5033
      Algorithm:      1.2.840.113549.1.1.11
      Valid from:     1:39 PM 10/11/2016
      Valid to:       1:39 PM 1/11/2018
    Microsoft Windows Production PCA 2011
      Status:         Valid
      Valid Usage:    All
      Serial Number:  61 07 76 56 00 00 00 00 00 08
      Thumbprint:     580A6F4CC4E4B669B9EBDC1B2B3E087B80D0678D
      Algorithm:      1.2.840.113549.1.1.11
      Valid from:     11:41 AM 10/19/2011
      Valid to:       11:51 AM 10/19/2026
    Microsoft Root Certificate Authority 2010
                Status:         Valid
                Valid Usage:    All
                Serial Number:  28 CC 3A 25 BF BA 44 AC 44 9A
                                9B 58 6B 43 39 AA
                Thumbprint:     3B1EFD3A66EA28B16697394703A72CA340A05BD5
                Algorithm:      1.2.840.113549.1.1.11
                Valid from:     2:57 PM 6/23/2010
                Valid to:       3:04 PM 6/23/2035
    Signing date:   1:02 PM 3/18/2017
    Counter Signers:
      Microsoft Time-Stamp Service
        Status:         Valid
        Valid Usage:    Timestamp Signing
        Serial Number:  33 00 00 00 B3 39 BB D4 12 93 15 A9 FE 00 00 00 00 00 B3
        Thumbprint:     BEF9C1F4DA0F153FF0900303BE78A59ADA8ADCB9
        Algorithm:      1.2.840.113549.1.1.11
        Valid from:     10:56 AM 9/7/2016
        Valid to:       10:56 AM 9/7/2018
      Microsoft Time-Stamp PCA 2010
        Status:         Valid
        Valid Usage:    All
        Serial Number:  61 09 81 2A 00 00 00 00 00 02
        Thumbprint:     2AA752FE64C49ABE82913C463529CF10FF2F04EE
        Algorithm:      1.2.840.113549.1.1.11
        Valid from:     2:36 PM 7/1/2010
        Valid to:       2:46 PM 7/1/2025
      Microsoft Root Certificate Authority 2010
        Status:         Valid
        Valid Usage:    All
        Serial Number:  28 CC 3A 25 BF BA 44 AC 44 9A 9B 58 6B 43 39 AA
        Thumbprint:     3B1EFD3A66EA28B16697394703A72CA340A05BD5
        Algorithm:      1.2.840.113549.1.1.11
        Valid from:     2:57 PM 6/23/2010
        Valid to:       3:04 PM 6/23/2035
    Publisher:      Microsoft Windows
    Description:    Notepad
    Product:        Microsoft« Windows« Operating System
    Prod version:   10.0.15063.0
    File version:   10.0.15063.0 (WinBuild.160101.0800)
    MachineType:    64-bit
    MD5:    F60A9D3A9461F68DE0FCCEBB0C6CB31A
    SHA1:   2302BA58181F3C4E1E44A47A7D214EE9397CF2BA
    PE256:  0C67E3923EDA8154A89ADCA8A6BF47DF7C07D40BB41963DEB16ACBCF2E54803E
    SHA256: C84C361B7F5DBAEAC93828E60D2B54704D3E7CA84148BAFDA632F9AD6CDC96FA
    IMP:    645E8D8B0AEA808FF16DAA70D6EE720E

Knowing the Authenticode hash allows you to look up the respective entry in the catalog file. You can double-click a catalog file to view its entries. I also wrote the CatalogTools PowerShell module to parse catalog files. The “hint” metadata field gives away that notepad.exe is indeed the corresponding entry:

Digital Signature Binary Format

Now that you have an understanding of the methods in which a PE file can be signed (Authenticode and catalog), it is useful to have some background on the binary format of signatures. Whether Authenticode signed or catalog signed, both signatures are stored as PKCS #7 signed data which is ASN.1 formatted binary data. ASN.1 is simply a standard that states how binary data of different data types should be stored. Before observing/parsing the bytes of a digital signature, you must first know how it is stored in the file. Catalog files are straightforward as the file itself consists of raw PKCS #7 data. There are online ASN.1 decoders that parse out ASN.1 data and present it in an intuitive fashion. For example, try loading the catalog file containing the hash for notepad.exe into the decoder and you will get a sense of the layout of the data. Here’s a snippet of the parsed output:

Each property within the ASN.1 encoded data begins with an object identifier (OID) - a unique numeric sequence that identifies the type of data that follows. The OIDs worth noting in the above snippet are the following:
  1. 1.2.840.113549.1.7.2 - This indicates that what follows is PKCS #7 signed data - the format expected for Authenticode and catalog-signed code.
  2. - This indicates that what follows is catalog file hash data
It is worth spending time exploring all of the fields contained within a digital signature. All fields present are outside of the scope of this blog post, however. Additional crypto/signature-related OIDs are listed here.

Embedded PE Authenticode Signature Retrieval

The digital signature data in a PE file with an embedded Authenticode signature is appended to the end of the file (in a well-formatted PE file). The OS obviously needs a little bit more information than that though in order to retrieve the exact offset and size of the embedded signature. Let’s look at kernel32.dll in one of my favorite PE parsing/editing utilities: CFF Explorer.

The offset and size of the embedded digital signature is stored in the “security directory” offset within the “data directories” array within the optional header. The data directory contains offsets and size of various structures within the PE file - exports, imports, relocations, etc. All offsets within the data directory are relative virtual offsets (RVA) meaning they are the offset to the respective portion of the PE when loaded in memory. There is one exception though - the security directory which stores its offset as a file offset. The reason for this is because the Windows loader doesn’t actually load the content of the security directory in memory.

The binary data in the at the security directory file offset is a WIN_CERTIFICATE structure. Here’s what the structure for kernel32.dll looks like parsed out in 010 Editor (file offset 0x000A9600):

PE Authenticode signatures should always have a wRevision of WIN_CERT_TYPE_PKCS_SIGNED_DATA. The byte array that follows is the same PKCS #7, ASN.1 encoded signed data as was seen in the contents of a catalog file. The only difference is that you shouldn’t find the OID, indicating the presence of catalog hashes.

Parsing out the raw bCertificate data in the online ASN.1 decoder confirms we’re dealing with proper PKCS #7 data:

Application of Digital Signatures to Unsigned PEs

Now that you have a basic idea of the binary format and storage locations of digital signatures, you can start applying existing signatures to your unsigned code.

Application of Embedded Authenticode Signatures

Applying an embedded Authenticode signature from a signed file to an unsigned PE file is quite straightforward. While the process can obviously be automated, I’m going to explain how to do it manually with a hex editor and CFF Explorer.

Step #1: Identify the Authenticode signature that you want to steal. In this example, I will use the one in kernel32.dll

Step #2: Identify the offset and size of the WIN_CERTIFICATE structure in the “security directory”

So the file offset in the above screenshot is 0x000A9600 and the size is 0x00003A68.

Step #3: Open kernel32.dll in a hex editor, select 0x3A68 bytes starting at offset 0xA9600, and then copy the bytes.

Step #4: Open your unsigned PE (HelloWorld.exe in this example) in a hex editor, scroll to the end, and paste the bytes copied from kernel32.dll. Take note of the file offset of the beginning of the signature (0x00000E00 in my case). Save the file after pasting in the signature.

Step #5: Open HelloWorld.exe in CFF Explorer and update the security directory to point to the digital signature that was applied: offset - 0x00000E00, size - 0x00003A68. Save the file after making the modifications. Ignore the “Invalid” warning. CFF Explorer doesn’t treat the security directory as a file offset and gets confused when it tries to reference what section the data resides in.

That’s it! Now, signature validation utilities will parse and display the signature properly. The only caveat is that they will report that the signature is invalid because the calculated Authenticode of the file does not match that of the signed hash stored in the certificate.

Now, if you were wondering why the SignerCertificate thumbprint values don’t match, then you are an astute reader. Considering we applied the identical signature, why doesn’t the certificate thumbprint match? That’s because Get-AuthenticodeSignature first attempts a catalog file lookup of kernel32.dll. In this case, it found a catalog entry for kernel32.dll and is displaying the signature information for the signer of the catalog file. kernel32.dll is also Authenticode signed though. To validate that the thumbprint values for the Authenticode hashes are identical, temporarily stop the CryptSvc service - the service responsible for performing catalog hash lookups. Now you will see that the thumbprint values match. This indicates that the catalog hash was signed with a different code signing certificate from the certificate used to sign kernel32.dll itself.

Application of a Catalog Signature to a PE File

Realistically, CryptSvc will always be running and catalog lookups will be performed. Suppose you want to be mindful of OPSEC and match the identical certificate used to sign your target binary. It turns out, you can actually apply the contents of a catalog file to an embedded PE signature by swapping out the contents of bCertificate in the WIN_CERTIFICATE structure and updating dwLength accordingly. Feel free to follow along as this is done. Note that our goal (in this case) is to apply an Authenticode signature to our unsigned binary that is identical to the one used to sign the containing catalog file: Certificate thumbprint AFDD80C4EBF2F61D3943F18BB566D6AA6F6E5033 in this case.

Step #1: Identify the catalog file containing the Authenticode hash of the target binary - kernel32.dll in this case. If a file is Authenticode signed, sigcheck will actually fail to resolve the catalog file. Signtool (included in the Windows SDK) will, however.

Step #2: Open the catalog file in in a hex editor and annotate the file size - 0x000137C7

Step #3: We’re going to manually craft a WIN_CERTIFICATE structure in a hex editor. Let’s go through each field we’ll supply:
  1. dwLength: This is the total length of the WIN_CERTIFICATE structure - i.e. bCertificate bytes plus the size of the other fields = 4 (size of DWORD) + 2 (size of WORD) + 2 (size of WORD) + 0x000137C7 (bCertificate - the file size of the .cat file) = 0x000137CF.
  2. wRevision: This will be 0x0200 to indicate WIN_CERT_REVISION_2_0.
  3. wCertificateType: This will be 0x0002 to indicate WIN_CERT_TYPE_PKCS_SIGNED_DATA.
  4. bCertificate: This will consist of the raw bytes of the catalog file.
When crafting the bytes in the hex editor, be mindful that the fields are stored in little-endian format.

Step #4: Copy all the bytes from the crafted WIN_CERTIFICATE, append them your unsigned PE, and update the security directory offset and size accordingly.

Now, assuming your calculations and alignments were proper, behold a thumbprint match with that of the catalog file!

Anomaly Detection Ideas

The techniques presented in this blog post have hopefully got some people thinking about how one might go about detecting the abuse of digital signatures. While I have not investigated signature heuristics thoroughly, let me just pose a series of questions that might motivate others to start investigating and writing detections for potential signature anomalies:
  • For a legitimately signed Microsoft PE, is there any correlation between the PE timestamp and the certificate validity period? Would the PE timestamp for attacker-supplied code deviate from the aforementioned correlation?
  • After reading this article, what is your level of trust in a “signed” file that has a hash mismatch?
  • How would you go about detecting a PE file that has an embedded Authenticode signature consisting of a catalog file? Hint: A specific OID mentioned earlier might be useful.
  • How might you go about validating the signature of a catalog-signed file on a different system?
  • What effect might a stopped/disabled CryptSvc service have on security products performing local signature validation? If that was to occur, then most system files, for all intents and purposes will cease to be signed.
  • Every legitimate PE I’ve seen is padded on a 0x10 byte boundary. The example I showed where I applied the catalog contents to an Authenticode signature is not 0x10 byte aligned.
  • How might you differentiate between a legitimate Microsoft digital signature and one where all the certificate attributes are applied to a self-signed certificate?
  • What if there is data appended beyond the digital signature? This has been abused in the past.
  • Threat intel professionals should find the Authenticode hash to be an interesting data point when investigating identical code with different certificates applied. VirusTotal supplies this as the "Authentihash" value: i.e. the hash value that was calculated with "sigcheck -h". If I were investigating variants of a sample that had more than one hit on a single Authentihash in VirusTotal, I would find that to be very interesting.