6

When a Windows .exe installer is code-signed, I thought that modifying a single byte (thus changing its SHA256 hash) would make the digital signature invalid, but surprisingly, this is not true.

Indeed, as reported two days ago in Each Firefox download has a unique identifier, a few bytes differ in each download, with a unique ID. If you compare two downloaded .exe installers, you can notice the digital signature is the same, but only a few bytes are different: the ID.

Is this Windows-executable-file-format's feature documented somewhere? Which bytes can be modified in a .exe without invalidating a digital signature?

Reference:

https://docs.fileformat.com/executable/exe/

https://en.wikipedia.org/wiki/.exe

In the case of the Firefox installer, it is a Portable Executable (dumpbin gives: PE signature found, File Type: EXECUTABLE IMAGE).

3
  • Probably that depends if the generated signature is on all executable file or the generated signature is only for some part of the file, for example without some file header (in Linux the ELF header)
    – camp0
    Mar 19 at 15:53
  • @camp0 How to sign only the headers? digicertutil does not offer the possibility to sign only a portion of the .exe file: digicert.com/kb/images/code-signing/…
    – tree1234
    Mar 19 at 16:21
  • I dont know the internals of that tool, but you can sign whatever part you want of a file with standard algorithms such as sha256, blake, and so on. Is just a matter of implementation.
    – camp0
    Mar 19 at 17:01

1 Answer 1

5

From https://docs.microsoft.com/en-us/windows/win32/debug/pe-format, emphasis mine:

When included in a certificate, the image digest must exclude certain fields in the PE Image, such as the Checksum and Certificate Table entry in Optional Header Data Directories. This is because the act of adding a Certificate changes these fields and would cause a different hash value to be calculated.

The Win32 ImageGetDigestStream function provides a data stream from a target PE file with which to hash functions. This data stream remains consistent when certificates are added to or removed from a PE file. Based on the parameters that are passed to ImageGetDigestStream, other data from the PE image can be omitted from the hash computation.

The ImageGetDigestStream function is documented at https://docs.microsoft.com/en-us/windows/win32/api/imagehlp/nf-imagehlp-imagegetdigeststream, which shows that it accepts a bitfield indicating optional file sections to include: the import data, the debug symbols, and the resource section (which is used for things like embedded image files and other large binary blobs). The executable image (code and static data) are always included.

Beyond all of that, there's some padding between sections of file, and at the end of the file, that is not used in the digest. It's conventionally zero (NULL) bytes, but can be changed as long as the total file length doesn't change. I've verified that changing the padding at the end, at least, doesn't invalidate the signature.


Based on that, some (non-exclusive) candidates for explaining the different Firefox downloads (without even looking at actual examples):

  • Unique certificates in the Checksum and Certificate Table, either actually used for code signing or just there to uniquely identify the binary.
  • Resource data that just isn't included in the signature (I don't know if there is any; it's certainly quite common for installers to include lots of resource data but I'd hope it would be signed).
  • Padding, especially padding at the end of the file.
3
  • Thank you. For an .exe that I compile myself with Visual C++ and that I code-sign, how can I find the offset at which I can modify a few bytes? Should I prepare something before compilation to ensure there are reserved bytes that can be modified? (Example: should I create a string in a resource.rc file, or are resources included in the code-signing hash?)
    – tree1234
    Mar 19 at 20:43
  • As mentioned, resources are optionally included in the digest. I don't know whether a binary that has resources but they aren't signed would be considered signed as a whole, though. It might be possible but rely on tools other than signtool.exe; wouldn't be the first time somebody left a potential (depends how the resources are used) vulnerability that their official tools couldn't reach but that the spec allowed... In any case, the padding (especially at the end of the file) is pretty reliably present, and you can tweak strings within the file to ensure padding is always needed.
    – CBHacking
    Mar 20 at 2:23
  • I found this approach quite complex for simple verification, Microsoft!
    – kelalaka
    Mar 20 at 19:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.