Back to Top

Tuesday, September 30, 2008

How to verify executable digital signatures under Linux?

The PE executable format (the one used by Windows) supports the use of digital certificates to verify the source of the file. Normally you can verify it using Windows Explorer (by right-clicking on the file and selecting Properties). It also shows up when you try to run an executable downloaded from the Internet with IE or FF3. If you wish to mass-verify it, you can use a tool like sigcheck by Mark Russinovich.

However, great tool as sigcheck may be, there are at least two problems with it:

  • It doesn't run on Linux. Last time I've tried under wine, it complained about missing imports from the cryptography libraries (this might have changed in the meantime)
  • It wants to connect to the Internet (to verify CLRs I assume). This can lead to some nasty pseudo-hanging processes on systems which (for security reasons) are cut off from the Internet but are still able to resolve domains.

So I looked into verifying the signatures myself. The method I'll describe in the following paragraphs will work on Linux (in fact it will work on any OS which has OpenSSL binaries), but it has at least two limitations you should be aware of:

  • It doesn't work with catalog (.CAT) files. The catalog files use an undocumented binary format, but it has been reverse engineered by both the wine and ReactOS folks, so those implementations should point you in the right direction.
  • I'm not actually verifying the validty of the signature, although I give a pointer on how you would possibly do this.

Ok, now with disclaimers set aside, here are the steps:

  1. Extract the digital signature from the file. This post from Didier Stevens tells you how. What you need is actually the content after the DWORD containing the size and the marker (0x00020200). Just to be clear:
    size DWORD = the size of the signature + 4 (the size DWORD) + 4 (the marker)
    actual signature = location specified by the data directory + 8 (marker + size)
    
    The actual signature is in DER format and is a standard PKCS7 signature, as Didier says (there are two standard file formats for PKCS7: DER - a binary format - and PEM - a text format - you will need this when specifying the input format for OpenSSL)
  2. To calculate the MD5/SHA1 hashes which match the one contained in the certificates, I've used the information from this mail by Peter Gutman. Basically, to calculate the matching hashes, you will need to exclude three fields from hashing: the checksum field and the address/size field from the security data directory. The area hashed ends before the location pointed by the security entry in the data directory. The digital signature should be last thing in the file. I didn't test the case when the digital signature is in the middle of the file, but I assume that either an erro is generated, or it is ignored as the afore mentioned three fields (I don't think that there is a security vulnerability which would result in semi-protected executables).
  3. The hashes calculated at the previous point should match the one printed out by the command:
    openssl asn1parse -inform DER -in "signature.der"
    The signature can contain either a SHA1 or a MD5 hash. To dump the certificate tree, you can use the command:
    openssl pkcs7 -inform DER -print_certs -text -in "signature.der"
    The first in the list is the one you need to trust (this is usually a big provider like Verisign / Thawte / Comodo / etc) and the last one is the actual signatory of the file.
  4. If you want to actually verify the signature, you need to create a copy of the file excluding the parts specified at step 2, and issue the command (taken from here):
    openssl smime -verify -in signature.der -content modified_executable -inform DER -binary

8 comments:

  1. Anonymous1:52 PM

    Very interesting! Can you post the perl code how to calculate hash, excluding 3 fields?

    ReplyDelete
  2. Not easily, because it uses a set of internal libraries and I would have to publish those too...

    Sorry.

    ReplyDelete
  3. After I published my post, Microsoft has published a detailed document explaining Authenticode:

    http://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/Authenticode_PE.docx

    If you want to write your own utility to calculate the hash used by Authenticode, I recommend you use the pefile Python module.
    http://code.google.com/p/pefile/

    There is an open-source signcode utility, called osslsigncode:
    http://sourceforge.net/projects/osslsigncode/
    But the last version doesn't support signature verification yet.

    ReplyDelete
  4. Nick Carlon12:04 AM

    Manual coding is the only option for step 2?

    ReplyDelete
  5. @Nick Carlon: that was my choice. From what I hear, there is a Microsoft API to do this, but I don't know the details (and I've heard about it from a friend who struggled to get it working).

    ReplyDelete
  6. I tryed the WinVerifyTrust function, but it doesn't work properly (the function returs TRUST_E_SUBJECT_NOT_TRUSTED when verifying "notepad.exe" file).
    Even SignTool.exe from Microsoft doesn't recognizes "notepad.exe" file.
    SigCheck.exe from SysInternals is the only one I found so far that recognizes exe files like notepad.exe, explorer.exe as being signed by Microsoft.
    Does anyone have any idea how this can be done?
    Thanks!

    ReplyDelete
  7. Anonymous2:44 AM

    the last part is incorrect,the modified_executable should be the first signedata section of the pkcs7 block.

    openssl smime -verify -in signature.der -content modified_executable -inform DER -binary

    ReplyDelete
  8. Anonymous6:16 PM

    How about chktrust which is part of the Mono-Project and started out some time in the early 2000s? See http://linux.die.net/man/1/chktrust

    ReplyDelete