From 344678de025504239e5d9c0f596a5b507c2adf6f Mon Sep 17 00:00:00 2001 From: Clement Rouault Date: Wed, 12 Apr 2017 17:13:44 +0200 Subject: [PATCH] Add doc/sample for CryptObject --- docs/source/crypto.rst | 12 +++++++++ docs/source/sample.rst | 33 +++++++++++++++++------- samples/certificate.py | 36 +++++++++++++++++++++----- windows/crypto/certificate.py | 43 +++++++++++++++++++++++-------- windows/crypto/encrypt_decrypt.py | 7 +++-- 5 files changed, 99 insertions(+), 32 deletions(-) diff --git a/docs/source/crypto.rst b/docs/source/crypto.rst index abcea1f..cbd2640 100644 --- a/docs/source/crypto.rst +++ b/docs/source/crypto.rst @@ -42,12 +42,24 @@ CertificateContext .. autoclass:: CertificateContext + EHCERTSTORE ''''''''''' .. autoclass:: EHCERTSTORE +CryptObject +''''''''''' + +.. autoclass:: CryptObject + + .. warning:: + + The methods and property of this class will problably change as I haven't yet had the time + to try it out in real cases. + + CryptContext '''''''''''' diff --git a/docs/source/sample.rst b/docs/source/sample.rst index bdec430..0eff11d 100644 --- a/docs/source/sample.rst +++ b/docs/source/sample.rst @@ -525,7 +525,7 @@ This sample is a working POC able to generate key-pair, encrypt and decrypt file Ouput:: - (cmd λ) python samples\encryption_demo.py genkey YOLOCERTIF mykey --pfxpassword MYPASSWORD + (cmd λ) python ..\samples\encryption_demo.py genkey YOLOCERTIF mykey --pfxpassword MYPASSWORD (cmd λ) ls @@ -533,7 +533,7 @@ Ouput:: (cmd λ) echo|set /p="my secret message" > message.txt - (cmd λ) python samples\encryption_demo.py crypt message.txt message.crypt mykey.cer + (cmd λ) python ..\samples\encryption_demo.py crypt message.txt message.crypt mykey.cer Encryption done. Result: bytearray(b'0\x82\x01\x19\x06\t*\x86H\x86\xf7\r\x01\x07\x03\xa0\x82\x01\n0\x82\x01\x06\x02\x01\x001\x81\xc30\x81 \xc0\x02\x01\x000)0\x151\x130\x11\x06\x03U\x04\x03\x13\nYOLOCERTIF\x02\x10\x1b\xa4>\x17\xf7\xed\xec\xabO\xf8\x11 @@ -543,7 +543,7 @@ Ouput:: \xc6\x12x\x1am\xc8\x01t\xac\xa6\xf3#\x02\xd4J \x8eZ\xbb\x10W\xe1 0;\x06\t*\x86H\x86\xf7\r\x01\x07\x010\x14\x06\x08* \x86H\x86\xf7\r\x03\x07\x04\x08\x14F\x04\xad\xed9\xed<\x80\x18\x80]6\xccTV\xbc\xb8*\x84QY!~\xb3\n\x1aV\xd4\rf\xd1n:') - (cmd λ) python samples\encryption_demo.py decrypt message.crypt mykey.pfx BADPASS + (cmd λ) python ..\samples\encryption_demo.py decrypt message.crypt mykey.pfx BADPASS Traceback (most recent call last): File "..\samples\encryption_demo.py", line 103, in res.func(**res.__dict__) @@ -576,13 +576,13 @@ Ouput:: (cmd λ) python .\samples\certificate.py Analysing certificate: - * name: - * issuer: - * raw_serial: <[51, 0, 0, 1, 6, 110, 195, 37, 196, 49, 201, 24, 14, 0, 0, 0, 0, 1, 6]> - * serial: <33 00 00 01 06 6e c3 25 c4 31 c9 18 0e 00 00 00 00 01 06> - * encoded start: + * name: + * issuer: + * raw_serial: <[51, 0, 0, 1, 6, 110, 195, 37, 196, 49, 201, 24, 14, 0, 0, 0, 0, 1, 6]> + * serial: <33 00 00 01 06 6e c3 25 c4 31 c9 18 0e 00 00 00 00 01 06> + * encoded start: - This certificate has 1 certificate chain + This certificate has 1 certificate chain(s) Chain 0: : * issuer: @@ -593,4 +593,17 @@ Ouput:: Looking for in trusted certificates matches = [] - Found it ! \ No newline at end of file + Found it ! + + == PE Analysis == + Target sha1 = <059cb1ba1a41433a18dd8f87422c2ac3bf35b7b8> + Analysing + File has 1 signer(s): + Signer 0: + * Issuer: bytearray(b'0\x81\x841\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x130\x11\x06\x03U\x04\x08\x13\nWashington1\x100\x0e\x06\x03U\x04\x07\x13\x07Redmond1\x1e0\x1c\x06\x03U\x04\n\x13\x15Microsoft Corporation1.0,\x06\x03U\x04\x03\x13%Microsoft Windows Production PCA 2011') + * HashAlgorithme: 2.16.840.1.101.3.4.2.1 + * Certificate: + + File embdeds 2 certificate(s): + * 0) + * 1) \ No newline at end of file diff --git a/samples/certificate.py b/samples/certificate.py index 3b251d9..6aba8b1 100644 --- a/samples/certificate.py +++ b/samples/certificate.py @@ -1,3 +1,4 @@ +import hashlib import windows.crypto windowscert = """-----BEGIN CERTIFICATE----- @@ -35,15 +36,15 @@ raw_cert = ("".join(windowscert.split("\n")[1:-1])).decode('base64') cert = windows.crypto.CertificateContext.from_buffer(raw_cert) print("Analysing certificate: {0}".format(cert)) -print("* name: <{0}>".format(cert.name)) -print("* issuer: <{0}>".format(cert.issuer)) -print("* raw_serial: <{0}>".format(cert.raw_serial)) -print("* serial: <{0}>".format(cert.serial)) -print("* encoded start: <{0!r}>".format(cert.encoded[:20])) +print(" * name: <{0}>".format(cert.name)) +print(" * issuer: <{0}>".format(cert.issuer)) +print(" * raw_serial: <{0}>".format(cert.raw_serial)) +print(" * serial: <{0}>".format(cert.serial)) +print(" * encoded start: <{0!r}>".format(cert.encoded[:20])) print "" chains = cert.chains -print("This certificate has {0} certificate chain".format(len(chains))) +print("This certificate has {0} certificate chain(s)".format(len(chains))) for i, chain in enumerate(chains): print("Chain {0}:".format(i)) for ccert in chain: @@ -63,4 +64,25 @@ print("matches = {0}".format(matchs)) if matchs: print("Found it !") else: - print("Not found :(") \ No newline at end of file + print("Not found :(") + +## Extract certificates of a PE file + +print ("") +print ("== PE Analysis ==") +TARGET_FILE = r"C:\windows\system32\ntdll.dll" +print("Target sha1 = <{0}>".format(hashlib.sha1(open(TARGET_FILE, "rb").read()).hexdigest())) +cryptobj = windows.crypto.CryptObject(TARGET_FILE) +print("Analysing {0}".format(cryptobj)) +print("File has {0} signer(s):".format(cryptobj.nb_signer)) +for i, signer in ((i, cryptobj.get_signer_data(i)) for i in range(cryptobj.nb_signer)): + print("Signer {0}:".format(i)) + print(" * Issuer: {0!r}".format(windows.crypto.ECRYPT_DATA_BLOB(signer.Issuer.cbData, signer.Issuer.pbData).data)) + print(" * HashAlgorithme: {0}".format(signer.HashAlgorithm.pszObjId)) + cert = cryptobj.get_signer_certificate(i) + print(" * Certificate: {0}".format(cert)) + +print("") +print("File embdeds {0} certificate(s):".format(cryptobj.nb_cert)) +for i, certificate in ((i, cryptobj.get_cert(i)) for i in range(cryptobj.nb_cert)): + print(" * {0}) {1}".format(i, certificate)) \ No newline at end of file diff --git a/windows/crypto/certificate.py b/windows/crypto/certificate.py index c02e282..5241d69 100644 --- a/windows/crypto/certificate.py +++ b/windows/crypto/certificate.py @@ -32,6 +32,10 @@ CRYPT_OBJECT_FORMAT_TYPE_DICT = {x:x for x in CRYPT_OBJECT_FORMAT_TYPE} ## Move CryptObject to new .py ? class CryptObject(object): + """Extract information from an CryptoAPI object. + + Current main use is extracting the signers certificates from a PE file. + """ MSG_PARAM_KNOW_TYPES = {CMSG_SIGNER_INFO_PARAM: CMSG_SIGNER_INFO, CMSG_SIGNER_COUNT_PARAM: DWORD, CMSG_CERT_COUNT_PARAM: DWORD} @@ -71,17 +75,30 @@ class CryptObject(object): winproxy.CryptMsgGetParam(self.hmsg, param_type, index, buffer, signer_info) if param_type in self.MSG_PARAM_KNOW_TYPES: - buffer = self.MSG_PARAM_KNOW_TYPES[param_type].from_buffer_copy(buffer) + buffer = self.MSG_PARAM_KNOW_TYPES[param_type].from_buffer(buffer) return buffer - def get_nb_signer(self): + @property + def nb_signer(self): + """The number of signers for the CryptObject + + :type: :class:`int` + """ return self.msg_get_param(CMSG_SIGNER_COUNT_PARAM).value def get_signer_data(self, index=0): + """Returns the signer informations for signer nb ``index`` + + :return: :class:`CMSG_SIGNER_INFO` + """ return self.msg_get_param(CMSG_SIGNER_INFO_PARAM, index) - def get_signer_certificate(self): - data = self.get_signer_data() + def get_signer_certificate(self, index=0): + """Return the certificate used for signer nb ``index`` + + :return: :class:`CertificateContext` + """ + data = self.get_signer_data(index) cert_info = CERT_INFO() cert_info.Issuer = data.Issuer cert_info.SerialNumber = data.SerialNumber @@ -93,20 +110,24 @@ class CryptObject(object): return self.msg_get_param(CMSG_CERT_PARAM, index) def get_cert(self, index=0): + """Return embded certificate number ``index``. + + note: not all embded certificate are directly used to sign the :class:`CryptObject`. + + :return: :class:`CertificateContext` + """ return CertificateContext.from_buffer(self.get_raw_cert(index)) cert = property(get_cert) - #@property + @property def nb_cert(self): - "TEST" + """The number of certificate embded in the :class:`CryptObject` + + :type: :class:`int` + """ return self.msg_get_param(CMSG_CERT_COUNT_PARAM).value - #@property - def all_certs(self): - return [self.get_cert(i) for i in range(self.nb_cert())] - - def __repr__(self): return '<{0} "{1}" content_type={2}>'.format(type(self).__name__, self.filename, self.content_type) diff --git a/windows/crypto/encrypt_decrypt.py b/windows/crypto/encrypt_decrypt.py index acda5e0..db09a36 100644 --- a/windows/crypto/encrypt_decrypt.py +++ b/windows/crypto/encrypt_decrypt.py @@ -46,11 +46,10 @@ def encrypt(cert_or_certlist, msg, algo=szOID_RSA_DES_EDE3_CBC, initvector=genin """ alg_ident = CRYPT_ALGORITHM_IDENTIFIER() alg_ident.pszObjId = algo - # Is 'certs' an iterable ? - try: - certlist = tuple(cert_or_certlist) - except TypeError as e: + if isinstance(cert_or_certlist, PCERT_CONTEXT): certlist = (cert_or_certlist,) + else: + certlist = tuple(cert_or_certlist) # Set (compute if needed) the IV if initvector is None: