Authenticode

Back in 2014, I explained two techniques that have been used by developers to store information in Authenticode-signed executables without breaking the signature.

Recently, Kevin Jones pointed out that Chrome’s signed installer differs on each download, as you can see in this file comparison of two copies of the Chrome installer, one downloaded from IE and one from Firefox:

Binary diff shows different bytes

Surprisingly, the Chrome installer is not using either unvalidated data-injection technique I wrote about previously.

So, what’s going on?

The Extra Certificate Technique

Fortunately, Kevin wrote an awesome tool for examining Authenticode signatures at a deep level: Authenticode Lint, which he describes in this blog post. Running his tool with the default options, the solution to our mystery is immediately revealed. The signing block contains an extra certificate named (literally) “Dummy Certificate”:

Authenticode Lint reveals "Dummy Certificate"

By running the tool with the -extract argument, we can view the extra (unsigned) certificate included in the signature block and see that it contains a proprietary data field with the per-instance data:image

This technique for injecting unsigned data into a signature block could be a source of vulnerabilities unless you’re very careful; anyone using this Extra Certificate technique should follow the same best practices previously described for the Unvalidated Attributes technique.

Authenticode Lint is a great tool, and I strongly recommend that you use it to help ensure you’re following best-practices for Authenticode-signing. My favorite feature is that you can automate the tool to verify an entire folder tree of binaries. You can thus be confident that you’ve signed all of the expected files, and done so properly.

-Eric

Note: Microsoft has not confirmed this change yet; analysis below comes from looking at behavior of 14 signed installers.

In December of last year, I wrote about all of the different places hashes are used in code-signing. Then, in January I blogged that Windows 10 had stopped accepting SHA-1 certificates and certificate chains for Authenticode-signed binaries (unless a timestamp marked the binary as being signed before 1/1/2016).

I called out the fact that while SHA1 certificates were verboten, “SHA1 file digests are still allowed (heck, MD5 digests are still allowed!)”.

With Windows 10 build 14316, things have changed again. It appears that SHA-1 file digests are now forbidden too, at least in the download codepath of Edge and Internet Explorer. This interrupts the download of Firefox, Opera, Fiddler, and other programs:

Firefox signature rejected

Fiddler signature rejected

As with the earlier lockdown, if you examine the signature in Windows Explorer, it’ll tell you that everything is “OK.”

File Hash is SHA1 and Cert is SHA256, UI shows 'ok'

To resolve this, you should dual-sign your binaries using both SHA-1 and SHA-256. It appears that the same Windows 10 14316 build has a bug whereby a signature containing only a SHA256 file digest will cause the download in IE/Edge to hang at 99%.

-Eric

I tried to install Telerik DevCraft Ultimate, but Windows 8.1 and Windows 10 blocked it:

Blocked

“Unknown Publisher”? Hrm.

That’s weird. I know Telerik signs their code and I was pretty sure their code-signing certificate is SHA256, so the new restrictions on SHA1 in code-signing shouldn’t be a problem, right?

Sure enough, the code is signed with a SHA256 certificate:

SHA256

… and we know that SHA1 file digests are still allowed (heck, MD5 digests are still allowed!). So what’s going wrong?

Check out the certificate chain:

image

The intermediate certificate is SHA1.

Other code, signed with the same chain, doesn’t fail, but that’s because that other code was time-stamped before the January 1st deprecation of SHA-1.

To avoid “Unknown Publisher” warnings for your software, you need to ensure that any intermediate certificates in your signing chain are also signed using SHA256. (Only the root certificate at the top of the chain may use SHA1).

-Eric

Twitter started to light up a bit tonight with folks who are having problems with signatures; both third-party ISVs:

Twitter post about bad signature

Signature is invalid or corrupt

The signature is corrupt or invalid.

… and even Microsoft’s own SysInternals utilities show1 an error:

Twitter complaint about bad signature

Signature is invalid or corrupt

Developers are surprised to see their workflow suddenly broken and wonder why.

The problem is outlined here – the tl;dr is that you must use a SHA256-signed certificate when codesigning any file after January 1st, 2016. If you failed to timestamp your file when you signed it, the date of signature cannot be determined and today’s date is used.

Confusingly, if you examine the File Properties in Windows Explorer, it will say that the signature is OK:

Explorer UI shows OK

This digital signature is OK. But not really.

To see the problem, you must dig into the certificate details:

SHA1 certificates

SHA1-signed Certificates

To fix this problem, you must

  1. Replace your code-signing certificate with a SHA256-signed certificate. Your CA should be willing to do this for free; if they aren’t, a little public shaming on Twitter will probably change their mind. Note: The entire certificate chain (except the root) must be SHA256, not just your certificate.
  2. Re-sign your files with the new certificate
  3. Accept that Windows XP SP2 and earlier don’t understand SHA256 certificates and will treat the file as unsigned. This is fine; XP SP3 resolved that limitation and users on XP have much worse problems to worry about anyway.

After you upgrade to the proper certificate, you should look into dual-signing your binaries so that the Authenticode signature itself contains both SHA1 and SHA256 signatures; this isn’t strictly required yet, but may be in the future. You should also follow other best-practices, including time-stamping and using a hardware token.

Stay secure out there!

-Eric Lawrence
1 At first, when I tried this using the SysInternals site, I didn’t see any complaints about the signature. That’s because the http://www.sysinternals.com site sends its binaries inside a .ZIP file. I’m using 7-Zip, which has a significant security bug– it fails to propagate the Mark-of-the-Web from a .ZIP to the files extracted from a ZIP file; as a consequence, Windows and SmartScreen won’t recognize that the files are from the Internet. If you’re not using Explorer’s built-in ZIP engine (which propagates MOTW properly) you can download executables directly from live.sysinternals.com to see the SHA1 problem.

Last month, I noticed that my eToken USB code-signing key only supports SHA1 and not SHA256. I began hunting for a replacement that can sign using the stronger hash. Fortunately, I didn’t have to look far—the Yubico YubiKey 4 is $40 and supports SHA256, RSA 4096, and ECC p384. Beyond supporting stronger algorithms, it seems to integrate better with Windows – I don’t need to install third-party software to use it after loading my certificate with the YubiKey PIV Manager.

To take advantage of SHA256, I needed to update my scripts to use signtool.exe instead of the older signcode.exe, which only supports SHA1.

My script is simply:

signtool sign /d "Brotli [De]compressor" /du "https://github.com/google/brotli" /n "Eric Lawrence" /t http://timestamp.digicert.com /fd SHA1 brotli.exe
signtool sign /as /d "Brotli [De]compressor" /du "
https://github.com/google/brotli" /n "Eric Lawrence" /tr http://timestamp.digicert.com /td SHA256 /fd SHA256 brotli.exe

Notably, we sign the file twice:

Windows File Properties show two signatures

First, sign using a SHA1 digest (older Windows versions don’t support SHA256). Then add an additional signature (the /as argument) using the stronger SHA256 file digest (the /fd argument).

For the stronger signature, use /tr to specify the timestamp URL (SHA256 signatures should use RFC3161 timestamps) and request that the timestamping server use a SHA256 digest (the /td argument).

Both signtool invocations will prompt for your PIN to access the private key stored on the token:

Windows PIN prompt

I was somewhat annoyed that the YubiKey only supports an 8 character PIN/password; I later learned that I can use the same 10 character password my old token uses—the final two characters are silently ignored.

After you’ve signed the file, you should use Windows Explorer to verify that each of the signatures and timestamps is valid:

Signature OK

Timestamp Signature OK

Interestingly, most public CAs will use SHA256 for the timestamp’s digest but not for the signature itself; you can see this if you look closely at the timestamp signature (“RSA”):

Just RSA

This is likely due to a limitation in OpenSSL, and isn’t seen in Microsoft’s signatures (“sha256RSA”):

SHA256RSA

A Few Caveats

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:

image

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:

WordPress Update Dialog

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 digest

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:

Dual-signed with SHA1/SHA256

To dual-sign with SHA256, you must use the signtool.exe rather than signcode.exe; the /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!

-Eric Lawrence

Over a decade ago, Windows started checking the signature of downloaded executables. When invoked, Attachment Execute Services’ (AES) UI displays the publisher’s information for signed executables; unsigned executables instead show a security prompt with a red shield and a bolded warning that the publisher of the file is unknown:

image

In contrast, signed executables show a yellow shield and the name of the publisher and the publisher’s declared name of the application.

When Windows Vista released in late 2006, an “elevation dialog” was introduced to prompt the user for permission to run an executable with elevated (administrator) rights. The new prompt’s design somewhat mirrored that of the earlier AES prompt, where unsigned executables are scary:

image

… and signed executables are less so:

image

As you can see, the prompt’s icon, program name, and publisher name are all pulled from the downloaded file.

To avoid double-prompting the user, the system detects whether a given executable will be elevated, and if so the AES dialog is suppressed and only the elevation prompt is shown.

As a consequence, the security UI in modern Windows is a bit backwards… the lower-risk “run as user” dialog seems complex and scary, while the higher-risk “run as administrator” dialog seems simpler and more trustworthy:

BadDesign

From a security design point-of-view, this seems unfortunate. Application designers should never be in the position of choosing higher-permission requests to get friendlier prompt behavior.

-Eric Lawrence