I’ve written a few articles about using Authenticode to sign your code to help prevent attacks, increase user confidence, and reduce interference from security software like Windows SmartScreen. You can read the overview, discussion of code-signing tokens, and “tricks” you can use to shoot yourself in the foot by adding data to a file without breaking its signature.
At the end of the last post, I mentioned that you shouldn’t be using signatures based on the MD5-hash, as that hash algorithm is outdated and collision attacks are getting better and cheaper with each passing day. Bizarrely, Microsoft hasn’t yet disabled MD5 for Authenticode (even as they increasingly talk about the threats to SHA1-hashed certificates). As far as I can tell, Microsoft hasn’t even announced a plan or timetable to do so.
The problem is that any signature based on MD5 could be simply copied from an MD5-signed file and then applied to any file whose MD5 hash was made to collide with the original—the result would be that the new file would appear to be signed by the certificate that had signed the original file. Back in 2014, the installer for CoPilot was signed with MD5 and was vulnerable to this attack.
Earlier this month, Automattic announced a new WordPress Desktop client which suffered from the same problem. You can examine the hash (digest) algorithm by right-clicking a signed file in Windows Explorer and choosing Properties from the context menu. The Digital Signatures tab will show information about the signed hash(es) used:
After investigating with the Automattic engineering team, we determined that the problem was due to the same root cause—signcode.exe defaults to MD5 and both the Windows and Mono version of this ancient tool default to the insecure MD5 hash.
The Automattic folks quickly fixed the problem (by simply adding
-a SHA1 to the tool’s command line arguments) and pushed a new build. They were kind enough to name the release after me, with a funny release note:
Hashes, Hashes, and Hashes
When it comes to Authenticode, up to four different signed hashes are used:
- The contents of the signed file
- The contents of each of the certificates in the file’s signing certificate chain
- The contents of the signing timestamp
- The contents of each of the certificates in the timestamp’s signing certificate chain
The first hash is what this post has talked about thus far, but the second is arguably more interesting. If a certificate has a weak signature, an attacker could copy its weak signature to a maliciously crafted certificate and then he could sign an unlimited number of malicious files, making it appear as if they had been signed by the victim organization.
As a consequence of this greater threat, Microsoft has been gradually ramping up the restrictions on the hashes used in the signing chain: In 2016, certificate chains containing SHA1 will be blocked for files originating from the Internet. Code-signing certificate chains should be using the much stronger SHA256 hash.
You may be wondering whether you should be also using SHA256 to hash the contents of the file:
SHA256 file digests are supported on the latest versions of Windows, but not on Windows XP, even with Service Pack 3 which supports SHA256-hashed certificates. Fortunately, if you need to support older versions of Windows, you can dual-sign the file by applying multiple signatures:
To dual-sign with SHA256, you must use the
signtool.exe rather than
/as argument is used to append additional signatures, and
/fd sha256 is used to specify a SHA256 digest. You also need to ensure that your SHA256 signature’s timestamp uses a modern timestamping URL.
I was excited to start dual-signing my tools with SHA1 and SHA256, but I’ve hit a stumbling block; the eToken Pro signing token I use only supports MD5 and SHA1, but not SHA256. I’ll need to replace the token with a more modern version to use SHA256. Update: I ordered a YubiKey4 and wrote about using SHA256 with YubiKey.
Thanks for your help in securing downloads!