内容简介:TL;DR:We’ve open-sourced a new library,As a library,
TL;DR:We’ve open-sourced a new library, μthenticode , for verifying Authenticode signatures on Windows PE binaries without a Windows machine. We’ve also integrated it into recent builds of Winchecksec , so that you can use it today to verify signatures on your Windows executables!
As a library, μthenticode aims to be a breeze to integrate: It’s written in cross-platform, modern C++ and avoids the complexity of the CryptoAPI interfaces it replaces (namely WinVerifyTrust
and CertVerifyCertificateChainPolicy
). You can use it now as a replacement for many of SignTool
’s features, and more are on the way.
A quick Authenticode primer
Authenticode is Microsoft’s code signing technology, comparable in spirit (but not implementation) to Apple’s Gatekeeper .
At its core, Authenticode supplies (or can supply, as optional features) a number of properties for signed programs:
- Authenticity: A program with a valid Authenticode signature contains a chain of certificates sufficient for validating that signature. Said chain is ultimately rooted in a certificate stored in the user’s Trusted Publishers store , preventing self-signed certificates without explicit opt-in by the user.
- Integrity: Each Authenticode signature includes a cryptographic hash of the signed binary. This hash is compared against the binary’s in-memory representation at load time, preventing malicious modifications.
- Authenticode can also embed cryptographic hashes for each page of memory. These are used with forced integrity signing , which is necessary for Windows kernel drivers and requires a special Microsoft cross-signed “Software Publisher Certificate” instead of a self-signed or independently trusted Certificate Authority (CA).
- Timeliness: Authenticode supports countersignatures embedding from a Timestamping Authority (TSA), allowing the signature on a binary to potentially outlive the expiration dates of its signing certificates. Such countersignatures also prevent backdating of a valid signature, making it more difficult for an attacker to re-use an expired signing certificate.
Like all code signing technologies, there are things Authenticode can’t do or guarantee about a program:
- That it has no bugs: Anybody can write buggy software and sign for it, either with a self-signed certificate or by purchasing one from a CA that’s been cross-signed by Microsoft.
- That it runs no code other than itself: The Windows execution contract is notoriously lax (e.g., the DLL loading rules for desktop applications ), and many applications support some form of code execution as a feature (scripts, plugins, sick WinAMP skins, etc). Authenticode has no ability to validate the integrity or intent of code executed outside of the initial signed binary.
Similarly, there are some things that Authenticode, like all PKI implementations, is susceptible to:
- Misplaced trust: CAs want to sell as many certificates as possible, and thus have limited incentives to check the legitimacy of entities that purchase from them. Anybody can create a US LLC for a few hundred bucks .
- Stolen certificates: Code-signing and HTTPS certificates are prime targets for theft; many real-world campaigns leverage stolen certificates to fool users into trusting malicious code. Companies regularly check their secret material into source control systems, and code signing certificates are no exception .
- Fraudulent certificates: Flame infamously leveraged a novel chosen-prefix attack on MD5 to impersonate a Microsoft certificate that had been accidentally trusted for code signing. Similar attacks on SHA-1 are now tractable at prices reasonable for nation states and organized crime.
All told, Authenticode (and all other forms of code signing) add useful authenticity and integrity checks to binaries, provided that you trust the signer and their ability to store their key material .
With that said, let’s take a look at what makes Authenticode tick.
Parsing Authenticode signatures: spicy PKCS#7
In a somewhat unusual move for 2000s-era Microsoft, most of the Authenticode format is actually documented and available for download . A few parts are conspicuously under-defined or marked as “out of scope”; we’ll cover some of them below.
At its core, Authenticode has two components:
- The certificate table, which contains one or more entries, each of which may be a
SignedData
. -
SignedData
objects, which are mostly normal PKCS#7 containers (marked with a content type ofSignedData
per RFC 2315 ).
The certificate table
The certificate table is the mechanism by which Authenticode signatures are embedded into PE files.
It has a few interesting properties:
- Accessing the certificate table involves reading the certificate table directory in the data directory table. Unlike every other entry in the data directory table, the certificate directory’s
RVA
field is not a virtual address—it’s a direct file offset. This is a reflection of the behavior of the Windows loader, which doesn’t actually load the certificates into the address space of the program. - Despite this, real-world tooling appears to be inflexible about placement and subsequent parsing of the certificate table. Microsoft’s tooling consistently places the certificate table at the end of the PE (after all sections); many third-party tools naively seek to the certificate table offset and parse until EOF, allowing an attacker to trivially append additional certificates.
Once located, actually parsing the certificate table is straightforward: It’s an 8 byte-aligned blob of WIN_CERTIFICATE
structures:
…with some fields of interest:
-
wRevision
: the “revision” of theWIN_CERTIFICATE
.- MSDN only recently fixed the documentation for this field:
WIN_CERT_REVISION_2_0=0x0200
is the current version for Authenticode signatures;WIN_CERT_REVISION_1_0=0x0100
is for “legacy” signatures. I haven’t been able to find the latter in the wild.
- MSDN only recently fixed the documentation for this field:
-
wCertificateType
: the kind of encapsulated certificate data.- MSDN documents four possible values for
wCertificateType
, but we’re only interested in one:WIN_CERT_TYPE_PKCS_SIGNED_DATA
.
- MSDN documents four possible values for
-
bCertificate
: the actual certificate data. ForWIN_CERT_TYPE_PKCS_SIGNED_DATA
, this is the (mostly) PKCS#7SignedData
mentioned above.
As you might have surmised, the structure of the certificate table allows for multiple independent Authenticode signatures. This is useful for deploying a program across multiple versions of Windows, particularly versions that might have legacy certificates in the Trusted Publishers store or that don’t trust a particular CA for whatever reason.
Authenticode’s SignedData
Microsoft helpfullysupplies this visualization of their SignedData
structure:
This is almost a normal PKCS#7 SignedData
, with a few key deviations:
- Instead of one of the RFC 2315 content types, the Authenticode
SignedData
’scontentInfo
has a type ofSPC_INDIRECT_DATA_OBJID
, which Microsoft defines as1.3.6.1.4.1.311.2.1.4
. - The structure corresponding to this object identifier (OID) is documented as
SpcIndirectDataContent
. Microsoft conveniently provides its ASN.1 definition: (Observe that the customAlgorithmIdentifier
is actually just X.509’sAlgorithmIdentifier
—see RFC 3279 and its updates).⚠ The code below does no error handling or memory management; read the μthenticode source for the full version. ⚠Given the ASN.1 definitions above, we can use OpenSSL’s (hellish and completely undocumented) ASN.1 macros to parse Microsoft’s custom structures:
Actually checking the signature
With our structures in place, we can use OpenSSL’s (mostly) undocumented PKCS#7 API to parse our SignedData
and indirect data contents:
…and then validate them:
Voilà: the basics of Authenticode. Observe that we pass PKCS7_NOVERIFY
, as we don’t necessarily have access to the entire certificate chain—only Windows users with the relevant cert in their Trusted Publishers store will have that.
Calculating and checking the Authenticode hash
Now that we have authenticity (modulo the root certificate), let’s do integrity.
First, let’s grab the hash embedded in the Authenticode signature, for eventual comparison:
Next, we need to compute the binary’s actual hash. This is a little involved, thanks to a few different fields:
- Every PE has a 32-bit
CheckSum
field that’s used for basic integrity purposes (i.e., accidental corruption). This field needs to be skipped when calculating the hash, as it’s calculated over the entire file and would change with the addition of certificates. - The certificate data directory entry itself needs to be skipped, since relocating and/or modifying the size of the certificate table should not require any changes to pre-existing signatures.
- The certificate table (and constituent signatures) itself, naturally, cannot be part of the input to the hash.
- To ensure a consistent hash, Authenticode stipulates that sections are hashed in ascending order by the value of each section header’s
PointerToRawData
, not the order of the section headers themselves. This is not particularly troublesome, but requires some additional bookkeeping.
μthenticode ’s implementation of the Authenticode hashing process is a little too long to duplicate below, but in pseudocode:
- Start with an empty buffer.
- Insert all PE headers (DOS, COFF, Optional, sections) into the buffer.
- Erase the certificate table directory entry and
CheckSum
field from the buffer, in that order (to avoid rescaling the former’s offset). - Use pe-parse ’s
IterSec
API to construct a list of section buffers.IterSec
yields sections in file offset order as of #129 . - Skip past the certificate table and add trailing data to the buffer, if any exists.
- Create and initialize a new OpenSSL message digest context using the NID retrieved from the signature.
- Toss the buffer into
EVP_DigestUpdate
and finish withEVP_DigestFinal
. - Compare the result with the Authenticode-supplied hash.
Other bits and pieces
We haven’t discussed the two remaining major Authenticode features: page hashes and timestamp countersignatures.
Page hashes
As mentioned above, page hashes are conspicuously not documented in the Authenticode specification, and are described as stored in a “[…] binary structure [that] is outside the scope of this paper.”
Online information on said structure is limited to a few resources:
- The VirtualBox source code references OIDs for two different versions of the page hashes structure:
-
SPC_PE_IMAGE_PAGE_HASHES_V1_OBJID
:1.3.6.1.4.1.311.2.3.1
-
SPC_PE_IMAGE_PAGE_HASHES_V2_OBJID
:1.3.6.1.4.1.311.2.3.2
These OIDs are not listed in Microsoft’s OID reference or in the OID repository, although they do appear in
Wintrust.h
. -
- At least one fork of
osslsigncode
has support for generating and validating page hashes, and grants us further insight:- The V1 OID represents SHA-1 page hashes; V2 represents SHA2-256.
- The
serializedData
of eachSpcSerializedObject
is an ASN.1SET
, each member of which is an ASN.1SEQUENCE
, to the effect of: (The definitions above are my reconstruction from the body ofget_page_hash_link
;osslsigncode
confusingly reuses theSpcAttributeTypeAndOptionalValue
type forImpl_SpcPageHash
and constructs the rest of the contents ofSpcSerializedObject
manually.)
As far as I can tell, osslsigncode
only inserts one Impl_SpcPageHash
for the entire PE, which it calculates in pe_calc_page_hash
. The code in that function is pretty dense, but it seems to generate a table of structures as follows:
…where IMPL_PAGE_HASH_SIZE
is determined by the hash algorithm used (i.e., by Impl_SpcPageHash.type
), and the very first entry in the table is a null-padded “page hash” for just the PE headers with page_offset=0
. This table is not given an ASN.1 definition—it’s inserted directly into Impl_SpcPageHash.pageHashes
.
Timestamp countersignatures
Unlike page hashes, Authenticode’s timestamp countersignature format is relatively well documented, both in official and third-party sources.
Just as the Authenticode SignedData
is mostly a normal PKCS#7 SignedData
, Authenticode’s timestamp format is mostly a normal PKCS#9 countersignature. Some noteworthy bits include:
- When issuing a timestamp request (TSR) to a timestamp authority (TSA), the request takes the form of an HTTP 1.1 POST containing a DER-encoded, then base64-encoded ASN.1 message: …where
countersignatureType
is the custom Microsoft OID1.3.6.1.4.1.311.3.2.1
(i.e.,SPC_TIME_STAMP_REQUEST_OBJID
) andcontent
is the original Authenticode PKCS#7ContentInfo
. - The TSA response is a PKCS#7
SignedData
, from which theSignerInfo
is extracted and embedded into the main AuthenticodeSignedData
. The certificates from the TSA response are similarly embedded into the certificate list as unauthenticated attributes.
Wrapup
We’ve covered all four major components of Authenticode above: verifying the signature, checking the integrity of the file against the verified hash, calculating page hashes, and verifying any timestamp countersignatures.
μthenticode itself is still a work in progress, and currently only has support for signatures and the main Authenticode hash. You can help us out by contributing support for page hash parsing and verification, as well as timestamp signature validation!
μthenticode ’s’ APIs are fully documented and hosted , and most can be used immediately with a peparse::parsed_pe *
:
Check out the svcli
command-line tool for an applied example, including retrieving the embedded Authenticode hash.
Prior work and references
μthenticode was written completely from scratch and uses the official Authenticode document supplied by Microsoft as its primary reference. When that was found lacking, the following resources came in handy:
- ClamAV’s Authenticode documentation
- Peter Gutmann’s Authenticode notes
- The original osslsigncode and this fork
The following resources were not referenced, but were discovered while researching this post:
- jsign : A Java implementation of Authenticode
Want the scoop on our open-source projects and other security innovations?Contact us or sign up for our newsletter !
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
精通Spring 4.x
陈雄华、林开雄、文建国 / 电子工业出版社 / 2017-1-1 / CNY 128.00
Spring 4.0是Spring在积蓄4年后,隆重推出的一个重大升级版本,进一步加强了Spring作为Java领域第一开源平台的翘楚地位。Spring 4.0引入了众多Java开发者翘首以盼的基于Groovy Bean的配置、HTML 5/WebSocket支持等新功能,全面支持Java 8.0,最低要求是Java 6.0。这些新功能实用性强、易用性高,可大幅降低Java应用,特别是Java W......一起来看看 《精通Spring 4.x》 这本书的介绍吧!