-
-
[转帖]Authenticode certificates and checks from a KM driver
-
发表于: 2020-3-23 10:50 5163
-
Original link: https://astralvx.com/index.php/2020/03/20/authenticode-certificates-and-checks-from-a-km-driver/?utm_source=rss&utm_medium=rss&utm_campaign=authenticode-certificates-and-checks-from-a-km-driver
Introduction
Windows does not make it easy to do digital signature checks from a kernel driver. All the documented and easy to use wintrust.h APIs only reside in usermode, so you’re left to fend for your self.
The 1st option is to use OpenSSL but if you build a driver targeting 1903+, the WHQL process seems to require submitting additional logs like SDV etc. The OpenSSL project is littered with issues, you will get many 1000’s of warning from memory misalignment to type issues. More importantly the encrypted digest generated was never my expected value, it’s possible I could have done something wrong in the implementation. Plus if you read through the code and search for “hack”, “todo”, it makes me uneasy. Lastly a driver compiled with OpenSSL is typically 250KB, whereas using native method is only 30KB, and when you have limited space in (which is another story) it’s certainly important to us.
The 2nd option is to use the undocumented exports from the Code Integrity DLL loaded into the System [4] process. Whilst not the safest method for release drivers on customer machines, it is relatively simpler to implement and nicely explained by Ido Moshe and Liron Zuarets in this post https://medium.com/cybereason/code-integrity-in-the-kernel-66b3f5cce5f
Lastly the 3rd option is to use Bcrypt. It is certainly possible to link against this header from a kernel driver in C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\shared\bcrypt.h
and linking the kernel library ksecdd.lib
. One can find a variety of interesting exports such as BCryptOpenAlgorithmProvider, BCryptImportKeyPair, BCryptGenerateSymmetricKey, BCryptCreateHash, BCryptHashData, BCryptDecrypt, BCryptEncrypt, BCryptExportKey, BCryptVerifySignature etc, but a lot of struct’s usually created by other functions and passed around as HANDLEs, must now be manually and painfully crafted by you and passed around. Exports can be seen via:dumpbin.exe /exports "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.18362.0\km\x64\ksecdd.lib"
Authenticode
Authenticode is a Microsoft code signing technology using asymmetric encryption with public/private keys. It is an embedded DER-encoded X509 certificate in a non-executable portion of the driver or can use a catalog file (a detached signature) to verify the driver integrity. Authenticode is fully explained by Microsoft in Authenticode_PE.docx in 2008 however not all sections are up to date, for example the document states certificate Thumbprints are MD5 hashes, in modern times the Thumbprint (hash of the DER encoded cert) are actually SHA1 hashes.
Adding a certificate, or multiple certificates part of a chain, does not affect the binary signature because the certificate table itself, certificate table pointer, and file checksum are not part of the certificate signature as seen in Fig 1.
Figure 1 – Overview of the PE file and Authenticode signature format (Microsoft)
Notice a security issue? What if you added arbitrary data as a Certificate entry in a well signed binary, well the hashes would still be valid. Fortunately .cat files resolve this issue, but not all drivers are catalog signed.
* So if you’re a red teamer, why drop mimikatz or something loud; just add a new 3rd party root CA, not like anyone will notice with all the other junk in there as seen in Fig 4.
Some useful terminology:
- Digest/Message digest = the output of a hash function
- Encrypted digest = signer’s private key is used to encrypt the digest resulting in a signature
- Signing the message = entire process of calculating the digest and encrypting
- Public key = just contains a modulus and exponent
- PKCS #7 = is a standard format for cryptographic data, including signed data, certificates, and certificate revocation lists (CRLs)
The basic overview of the signing process is:
- PE image is hashed, specifically in Fig 1 the white background parts with e.g. SHA256
- Signer’s private key e.g. (RSA 2048) encrypts the above digest giving us an encrypted digest
- The encrypted digest is combined with the certificate and hash algorithm creating a signature block
- This signature block is inserted into the PE file
When a user verifies the Authenticode signature embedded:
- Public key is extracted from the embedded cert
- Encrypted digest embedded is decrypted with the public key
- The same hash algorithm (e.g. SHA256) that was used to create the original digest, is ran again on the PE code/data/etc sections, to create a digest
- Digests from step 2 and 3 are compared. If they’re the same, the public key matches the private key used to sign the original code, proving the code hasn’t been modified
Certificate chains
RDNs are zero or more comma-separated components called relative Distinguished Names, forming a DN. Looking at the DNs of a binary, here’s Adobe Reader uploaded locally. In Fig 2 and Table 1 we can see 10 RDNs for AcroRd32.exe Subject field.
Figure 2 – Cert Subject of Adobe Reader
Figure 3 – Certificate path
The process to check a chain of certificates back to its root, is simply to compare the child Issuer to a parent Subject. In Fig 3 above is the path, and below in Table 1 are the corresponding links between child (issuer field) and parent (subject field). And almost certainly that DigiCert Root CA with Thumbprint [5fb7ee..]
is installed on your machine, as seen below in Fig 4.
AcroRd32.exe | DigiCert EV Code Signing CA (SHA2) | DigiCert High Assurance EV Root CA | |
---|---|---|---|
Issuer | CN = DigiCert EV Code Signing CA (SHA2) OU = www.digicert.com O = DigiCert Inc C = US | CN = DigiCert High Assurance EV Root CA OU = www.digicert.com O = DigiCert Inc C = US | CN = DigiCert High Assurance EV Root CA OU = www.digicert.com O = DigiCert Inc C = US |
Subject | CN = Adobe Inc. OU = Acrobat DC O = Adobe Inc. L = San Jose S = ca C = US SERIALNUMBER = 2748129 2.5.4.15 = Private Organization 1.3.6.1.4.1.311.60.2.1.2 = Delaware 1.3.6.1.4.1.311.60.2.1.3 = US | CN = DigiCert EV Code Signing CA (SHA2) OU = www.digicert.com O = DigiCert Inc C = US | CN = DigiCert High Assurance EV Root CA OU = www.digicert.com O = DigiCert Inc C = US |
Thumbprint | 1bfc555fd6489422bc2baba036b31aa75dc0aa17 | 60ee3fc53d4bdfd1697ae5beae1cab1c0f3ad4e3 | 5fb7ee0633e259dbad0c4c9ae6d38f1a61c7dc25 |
Serial | 0ee3f1c8f451cbf21203341a53f23e71 | 03f1b4e15f3a82f1149678b3d7d8475c | 02ac5c266a0b409b8f0b79f2ae462577 |
Signature algorithim | sha256RSA | sha256RSA | sha1RSA |
Table 1 – The links between child and parent certs
Certificate locations
The location of root certificates can be found in the registry under a HKLM key. You may think this is potentially a security issue as I did initially, but an admin could just kernel debug the machine or attach to lsass.exe and steal the cached credentials or using psexec as SYSTEM overwrite protected folders; so an admin adding and removing system certificates is certainly within there power. In Table 2 and 3 is Window Certificate Manager store for all types of certs:
Context | Registry path | Explanation |
---|---|---|
User | HKCU:\SOFTWARE\Microsoft\SystemCertificates\ | Physical store for user-specific public keys |
User | HKCU:\SOFTWARE\Policies\Microsoft\SystemCertificates\ | Physical store for user-specific public keys installed by Active Directory (AD) Group Policy Objects (GPOs) |
Computer | HKLM:\SOFTWARE\Microsoft\SystemCertificates\ | Physical store for machine-wide public keys |
Computer | HKLM:\SOFTWARE\Microsoft\Cryptography\Services\ | Physical store for keys associated with a specific service |
Computer | KLM:\SOFTWARE\Policies\Microsoft\SystemCertificates\ | Physical store for machine-wide public keys installed by GPOs |
Computer | HKLM:\SOFTWARE\Microsoft\EnterpriseCertificates\ | Physical store for machine-wide public keys installed by the Enterprise PKI Containers within an AD domain |
Table 2 – Registry paths for cert stores
Context | File location | Explanation |
---|---|---|
User | C:\user\abc\APPDATA\Microsoft\SystemCertificates\ | Physical store for user-specific public keys and pointers to private keys |
User | C:\user\abc\APPDATA\Microsoft\Crypto\ | Physical store for user-specific private key containers |
Computer | C:\ProgramData\Microsoft\Crypto\ | Physical store for machine-wide private key containers |
Table 3 – Folders for cert stores
The types of cert stores are:
- AddressBook: Certificate store for other people and resources.
- AuthRoot: Certificate store for third-party certification authorities (CAs).
- CertificationAuthority: Certificate store for intermediate certification authorities (CAs).
- Disallowed: Certificate store for certificates that have been revoked so they aren’t forgotten.
- My: Certificate store for your personal certificates that you use and is where most custom certificates.
- Root: Certificate store for certificate authorities (CA) that you trust.
- TrustedPeople: Certificate store for other people and resources that you trust.
- TrustedPublisher: Certificate store for application publishers that you trust.
Our “DigiCert High Assurance EV Root CA” is a third-party CA, so we can look under the reg key for machine wide system certificates HKLM\SOFTWARE\Microsoft\SystemCertificates\AuthRoot\Certificates
The thumbprint for our DigiCert Root cert was 5fb7ee0633e259dbad0c4c9ae6d38f1a61c7dc25
and in Fig 4 we see it in the registry. This is the performant way Windows searches certs, by using Thumbprints.
Figure 4 – A whole boat load of non-Microsoft root CA’s on my machine
Another way to view individual cert details is to manually extract it from your signed binary, where you can “Copy to file” any cert in the chain. Then you can use the tool certutil
to dump a vast amount of data, or use certutil to view details about the overall binary.
OID
OID notation is a dotted string of numbers, for example 1.2.840.113549.1.9.4
which is represented in hex as 06 09 2A 86 48 86 F7 0D 01 09 04
this online conversion tool is helpful, and a python3 script I created to convert between them faster https://github.com/AstralVX/oidhex_to_dot – OID for messageDigest is broken down to:
- 1.2.840.113549.1.9.4 – messageDigest
- 1.2.840.113549.1.9 – PKCS-9 Signatures
- 1.2.840.113549.1 – PKCS
- 1.2.840.113549 – RSADSI
- 1.2.840 – USA
- 1.2 – ISO member body
- 1 – ISO assigned OIDs
TLV
Every ASN.1 data is encoded as a Tag Length Value (TLV) triplet. The tag defines the object type e.g. int, bool, string, sequence, etc. A TLV can also have sub TLVs.
Example ASN.1 data: 30 0D 0C 05 48 45 4C 4C 4F 02 01 1E 01 01 00
Table 4 explains the TLV, and below it is the ASN.1 sequence, and C style struct:
Tag | Length | Value | Explanation |
---|---|---|---|
30 | 0D | Sequence with a length of 13 | |
OC | 05 | 48 45 4C 4C 4F | UTF8 String with a length of 6 and the value HELLO |
02 | 01 | 1E | Integer with a length of 1 and the value 0x1E (30) |
01 | 01 | 00 | Boolean with a length of 1 and the value 0 (FALSE) |
//ASN.1 sequence Sequence { Name::= UTF8 String Age::= Integer isSmoker::= [0] Boolean OPTIONAL } //C struct struct person { CHAR[5] Name, INT age, BOOL isSmoker}
ASN.1
ASN.1 is a general way to describe structures. The Authenticode digital signature contains Authenticated attributes (all the juicy parts) and Unauthenticated attributes (timestamping signature, MS countersigned). Explorer is pretty lame as seen in Fig 5 and only shows a few OIDs with readable names, it doesn’t do all the conversions, and misses out some important OIDs. What we do see is in Authenticated attributes is:
1.2.840.113549.1.9.3 = contentType. Contains a messageDigest OID (1.3.6.1.4.1.311.2.1.4) 1.3.6.1.4.1.311.2.1.11 = SPC_STATEMENT_TYPE_OBJID 1.3.6.1.4.1.311.2.1.12 = SpcSpOpusInfo. Contains: programName [optional] description, field [optional], a URL 1.2.840.113549.1.9.4 = messageDigest
Where 1.3.6.1.4.1.311.2.1.4 == SPC_INDIRECT_DATA_OBJID (06 0a 2b 06 01 04 01 82 37 02 01 04), which is a SpcIndirectDataContent struct:
SpcIndirectDataContent (struct) { Data - SpcAttributeTypeAndOptionalValue (struct) { Type - OID: SPC_PE_IMAGE_DATAOBJ [1.3.6.1.4.1.311.2.1.15] (06 0a 2b 06 01 04 01 82 37 02 01 0f) Value - SpcPeImageData (struct) { Flags - File - SPCLink (struct) { URL [0]: Moniker [1]: SpcSerializedObject (struct) { SpcUuid - 10 byte const GUID (a6 b5 86 d5 b4 a1 24 66 ae 05 a2 17 da 8e 60 d6) SerializedData - struct of page hashes if present } } File [2]: SpcString (struct) containing Unicode string "<<<Obsolete>>>" } } MessageDigest - DigestInfo (struct) { DigestAlgorithim - must be same as SignerInfo.DigestAlgorithim Digest - message digest value } }
Figure 5 – Digital signature tab of Adobe Reader
Implementation
Due to Copyright of work for my company, I can not release the full crypto code which is a few thousand lines of code. But at a very high level one can do the following:
- Read PE header of target binary for digital signature check, parse DOS/NT sections, and extract the embedded Authenticode certs out
- Start the process of parsing TLVs – parse the SignedData sequence, then another TLV later is digest algorithm, then you can get ContentInfo struct, SignerInfo struct
- One can then loop between certs and check for serials/valid date ranges/hash algos/EKU code signing etc etc between the intermediate certs
- To validate the topmost parent is a root cert, you can generate a Thumbprint of the topmost cert using Bcrypt and check in the root cert store for that Thumbprint
- Then verify the message digest by hashing the actual binary with the appropriate digest algo, and comparing it to the embedded AuthenticodeInfo signer message digest
- Next you verify the encrypted digest which proves it was the signer who signed the raw message digest. That’s by done by generating a digest of the signed attribute struct, and applying their public key to the embedded encrypted digest to obtain the unsigned digest, if they match it proves the signer signed it with their private key. Tons of Bcrypyt needed here and the trickiest part, use of manually crafted pRsaKeyBlob which is composed of
[BCRYPT_RSAKEY_BLOB, pbPublicExponent (big endian), pbRawPublicKey (big endian)]. You can then use that crafted struct in BCryptImportKeyPair(BCRYPT_RSAPUBLIC_BLOB), and BCryptEncrypt(BCRYPT_PAD_NONE) which encrypts the unsigned digest with the public key - Lastly is the validation of the image digest – (hash as explained in the Authenticode docx at the start of this blog, dos/nt/sections/etc), and compare that generated hash to the image digest stored in authenticode
Unfortunately there is tons of TLV parsing, Bcrypt, buffer overrun checks, digests galore, blacklisted cert checks, cert pinning checks, within all those points. It’s not for the faint of heart to implement, not just a couple of a Bcrypt calls and some parsing, it’s multiple 1000’s of lines worth of work. But now you have an idea. Fig 6 shows some headers you would need to create.
Figure 6 – Structs related to Authenticode processing
Sample values
Using the same Adobe binary, if one needs a sample with calculated values to compare:
// Public Key 30 82 01 0a 02 82 01 01 00 b3 cc 04 ee e8 24 99 23 43 3b 6b 34 ce e6 ee 80 c5 8b ab 38 a4 28 f4 55 8f ab 8b e5 ab cb 44 62 25 c8 71 24 1f f6 15 03 35 76 a9 b0 9b a5 db 28 8a 24 45 fc e6 e4 fd 22 fe b7 17 8b d6 d3 77 11 42 bb 1a e3 6f f9 42 d2 b5 9b 41 d6 b1 5d 8f f9 87 18 fb 55 fd 5e 1a 51 3d bc 44 ac 96 ba d5 97 9d 88 e4 59 cf e7 21 f1 07 28 d7 a5 ef 17 00 2c 5e 1e 1d 67 c5 b4 a9 83 8f 2e 2c 08 51 64 e4 14 89 91 2e 6e e4 30 f5 24 e9 b3 0e c3 08 98 a0 9c f1 c9 68 7a 3d dd d6 f9 9c 3b db 2c 96 a2 7f 17 97 87 f8 16 5f ed 64 fb 1d 72 43 ff 1a 95 3d 91 28 90 68 56 76 2b 66 4e ff a8 ee 36 9f 49 cb a6 ee 87 9c 76 22 1d fb 98 48 9e 72 2e d1 1b b1 d8 2d 52 6b eb fa 62 00 9a c8 83 04 b6 64 ed 45 4c 21 34 f4 8d 36 3b 8b b4 52 47 b3 a9 4e 2b 54 94 0f b2 b7 06 54 25 23 1f 36 fb 07 ba d4 a3 ea b7 02 03 01 00 01 // Image digest - raw ef 64 7e 10 ca 06 df 3d 22 d1 10 fb 78 17 92 b4 f2 2c 0e f7 1e c1 95 cf 0c 33 59 a4 b8 b2 ff cb // Content info (Bcrypt header + Image digest) 30 5c 06 0a 2b 06 01 04 01 82 37 02 01 04 a0 4e 30 4c 30 17 06 0a 2b 06 01 04 01 82 37 02 01 0f 30 09 03 01 00 a0 04 a2 02 80 00 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 ef 64 7e 10 ca 06 df 3d 22 d1 10 fb 78 17 92 b4 f2 2c 0e f7 1e c1 95 cf 0c 33 59 a4 b8 b2 ff cb // Digest algo (_DIGEST_ALGO_IDENTIFIER_SHA256) 60 86 48 01 65 03 04 02 01 // Signer info - signature algo (ALGO_IDENTIFIER_RSA) 2a 86 48 86 f7 0d 01 01 01 // Message digest e.g. SHA256(Content info) a7 ee 55 49 55 11 15 df f5 ea 6a b4 10 44 9f de 24 66 c5 f4 3f 8e 5c e4 de 01 20 4c 55 ab ca 6e // Encrypted digest 33 d9 b3 76 1d a7 37 70 a1 b1 5b 3a 11 e8 12 34 03 43 b2 18 b4 b1 af 60 ad 2f cb 41 33 f5 6d 13 f5 6c 3f 84 d6 ef a1 77 07 b1 55 2c 9e 12 d5 1b 1f 3c 0e 6b 5e 9a d9 2f 5f d7 8b b2 a0 a1 62 c1 80 60 4f 8f ad 2c 0d 90 0e 1a 96 aa 13 24 8f ca d8 05 c1 aa 98 1d df df 93 85 ea eb 7e 0c 7f 94 df d0 36 e5 ca df fc 64 d1 a0 7b 30 5a 87 a0 38 7b cb ea e1 6c d1 53 ad ab 48 3f 7f 73 90 66 39 bc b1 6a 8a b2 42 1c 7b bb 63 9f 3c 03 e6 82 f5 3a 0e fc 22 d1 9c eb a5 da 2b be 93 b3 9d 88 a3 43 d0 0c 70 90 06 84 0d 33 ff 3a 8e 3a b6 8f a6 7d 54 4a ea 59 f6 b4 7f 95 51 49 dc 96 0a ba 29 8e a1 71 36 d5 a9 d2 72 39 0d 54 15 b8 62 5c f1 aa 3a 2b f4 18 16 5b 8b 8b 62 49 02 89 c6 7d ea 29 15 0e c3 a9 15 ee 7f bd da 96 6d 35 6f 4d 2b 7e e1 04 f0 de be 7b 01 15 a4 07 a8 09 df 69 d9 // Signer info - signed attributes 31 81 9a 30 19 06 09 2a 86 48 86 f7 0d 01 09 03 31 0c 06 0a 2b 06 01 04 01 82 37 02 01 04 30 1c 06 0a 2b 06 01 04 01 82 37 02 01 0b 31 0e 30 0c 06 0a 2b 06 01 04 01 82 37 02 01 15 30 2e 06 0a 2b 06 01 04 01 82 37 02 01 0c 31 20 30 1e a0 1c 80 1a 00 41 00 64 00 6f 00 62 00 65 00 20 00 41 00 63 00 72 00 6f 00 62 00 61 00 74 30 2f 06 09 2a 86 48 86 f7 0d 01 09 04 31 22 04 20 a7 ee 55 49 55 11 15 df f5 ea 6a b4 10 44 9f de 24 66 c5 f4 3f 8e 5c e4 de 01 20 4c 55 ab ca 6e
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)