skelsec / pypykatz

Mimikatz implementation in pure Python
MIT License
2.81k stars 371 forks source link

'AES' object has no attribute 'block_size' #102

Closed sealmove closed 2 years ago

sealmove commented 2 years ago

I am getting an AttributeError when trying to decode blobs using master key. I installed pypykatz using pip. It looks like a problem with dependencies. Do I need to install any additional package(s) with pip?

pypykatz dpapi blob masterkey encrypted_key.blob
Traceback (most recent call last):
  File "/usr/sbin/pypykatz", line 8, in <module>
    sys.exit(main())
  File "/usr/lib/python3.10/site-packages/pypykatz/__main__.py", line 89, in main
    helper.execute(args)
  File "/usr/lib/python3.10/site-packages/pypykatz/dpapi/cmdhelper.py", line 121, in execute
    self.run(args)
  File "/usr/lib/python3.10/site-packages/pypykatz/dpapi/cmdhelper.py", line 233, in run
    dec_sec = dpapi.decrypt_securestring_file(args.blob)
  File "/usr/lib/python3.10/site-packages/pypykatz/dpapi/dpapi.py", line 541, in decrypt_securestring_file
    return self.decrypt_securestring_hex(data)
  File "/usr/lib/python3.10/site-packages/pypykatz/dpapi/dpapi.py", line 536, in decrypt_securestring_hex
    return self.decrypt_securestring_bytes(bytes.fromhex(hex_str))
  File "/usr/lib/python3.10/site-packages/pypykatz/dpapi/dpapi.py", line 533, in decrypt_securestring_bytes
    return self.decrypt_blob_bytes(data)
  File "/usr/lib/python3.10/site-packages/pypykatz/dpapi/dpapi.py", line 449, in decrypt_blob_bytes
    return self.decrypt_blob(blob, key = key)
  File "/usr/lib/python3.10/site-packages/pypykatz/dpapi/dpapi.py", line 434, in decrypt_blob
    return dpapi_blob.decrypt(key)
  File "/usr/lib/python3.10/site-packages/pypykatz/dpapi/structures/blob.py", line 109, in decrypt
    cleartext = unpad(cipher.decrypt(self.data), cipher.block_size)
AttributeError: 'AES' object has no attribute 'block_size'
skelsec commented 2 years ago

This is a bug caused by the change of crypto library in the recent month and the lack of tests on the DPAPI module.

sealmove commented 2 years ago

@skelsec I'll check it out and probably make a PR, thanks!

skelsec commented 2 years ago

Don't sweat it, modifying these 2 lines solve the problem:

cleartext = unpad(cipher.decrypt(self.data), ALGORITHMS_DATA[self.crypto_algorithm][3])
hash_block_size = ALGORITHMS_DATA[self.hash_algorithm][4]
sealmove commented 2 years ago

Looks good, thanks. I get another error though, might be or might not be related.

I am pretty sure my input is correct. It's an encrypted chrome aes key. I stripped the "DPAPI" prefix.

base64 -d encrypted_key.b64 | cut -c 6- | hexdump -ve '1/1 "%02x"' | tee key.blob
pypykatz dpapi blob masterkey key.blob
(...)
ValueError: Input is not padded or padding is corrupt
skelsec commented 2 years ago
sealmove commented 2 years ago

Which version should I try with?

$ pip show pypykatz
Name: pypykatz
Version: 0.5.7
Summary: Python implementation of Mimikatz
Home-page: https://github.com/skelsec/pypykatz
Author: Tamas Jos
Author-email: info@skelsecprojects.com
License: UNKNOWN
Location: /home/sealmove/.local/lib/python3.10/site-packages
Requires: aesedb, aiosmb, aiowinreg, minidump, minikerberos, msldap, unicrypto, winacl
Required-by: 
$ pypykatz dpapi blob masterkey key.blob
Error! non-hexadecimal number found in fromhex() arg at position 0
Traceback (most recent call last):
  File "/home/sealmove/.local/lib/python3.10/site-packages/pypykatz/dpapi/cmdhelper.py", line 230, in run
    bytes.fromhex(args.blob)
ValueError: non-hexadecimal number found in fromhex() arg at position 0

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/sealmove/.local/bin/pypykatz", line 8, in <module>
    sys.exit(main())
  File "/home/sealmove/.local/lib/python3.10/site-packages/pypykatz/__main__.py", line 89, in main
    helper.execute(args)
  File "/home/sealmove/.local/lib/python3.10/site-packages/pypykatz/dpapi/cmdhelper.py", line 121, in execute
    self.run(args)
  File "/home/sealmove/.local/lib/python3.10/site-packages/pypykatz/dpapi/cmdhelper.py", line 233, in run
    dec_sec = dpapi.decrypt_securestring_file(args.blob)
  File "/home/sealmove/.local/lib/python3.10/site-packages/pypykatz/dpapi/dpapi.py", line 541, in decrypt_securestring_file
    return self.decrypt_securestring_hex(data)
  File "/home/sealmove/.local/lib/python3.10/site-packages/pypykatz/dpapi/dpapi.py", line 536, in decrypt_securestring_hex
    return self.decrypt_securestring_bytes(bytes.fromhex(hex_str))
  File "/home/sealmove/.local/lib/python3.10/site-packages/pypykatz/dpapi/dpapi.py", line 533, in decrypt_securestring_bytes
    return self.decrypt_blob_bytes(data)
  File "/home/sealmove/.local/lib/python3.10/site-packages/pypykatz/dpapi/dpapi.py", line 449, in decrypt_blob_bytes
    return self.decrypt_blob(blob, key = key)
  File "/home/sealmove/.local/lib/python3.10/site-packages/pypykatz/dpapi/dpapi.py", line 434, in decrypt_blob
    return dpapi_blob.decrypt(key)
  File "/home/sealmove/.local/lib/python3.10/site-packages/pypykatz/dpapi/structures/blob.py", line 109, in decrypt
    cleartext = unpad(cipher.decrypt(self.data), ALGORITHMS_DATA[self.crypto_algorithm][3])
  File "/home/sealmove/.local/lib/python3.10/site-packages/unicrypto/backends/pure/padding/pkcs7.py", line 11, in unpad
    raise ValueError('Input is not padded or padding is corrupt')
ValueError: Input is not padded or padding is corrupt
skelsec commented 2 years ago

I'd suggest 0.5.2 That one is before unicrypto got added.

sealmove commented 2 years ago

Tested on a clean environment. I get the same error with v0.5.2.

$ pypykatz dpapi blob masterkey key.blob
Error! non-hexadecimal number found in fromhex() arg at position 0
Traceback (most recent call last):
  File "/home/remnux/.local/lib/python3.8/site-packages/pypykatz/dpapi/cmdhelper.py", line 230, in run
    bytes.fromhex(args.blob)
ValueError: non-hexadecimal number found in fromhex() arg at position 0

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/bin/pypykatz", line 33, in <module>
    sys.exit(load_entry_point('pypykatz==0.5.7', 'console_scripts', 'pypykatz')())
  File "/home/remnux/.local/lib/python3.8/site-packages/pypykatz/__main__.py", line 84, in main
    helper.execute(args)
  File "/home/remnux/.local/lib/python3.8/site-packages/pypykatz/dpapi/cmdhelper.py", line 121, in execute
    self.run(args)
  File "/home/remnux/.local/lib/python3.8/site-packages/pypykatz/dpapi/cmdhelper.py", line 233, in run
    dec_sec = dpapi.decrypt_securestring_file(args.blob)
  File "/home/remnux/.local/lib/python3.8/site-packages/pypykatz/dpapi/dpapi.py", line 542, in decrypt_securestring_file
    return self.decrypt_securestring_hex(data)
  File "/home/remnux/.local/lib/python3.8/site-packages/pypykatz/dpapi/dpapi.py", line 537, in decrypt_securestring_hex
    return self.decrypt_securestring_bytes(bytes.fromhex(hex_str))
  File "/home/remnux/.local/lib/python3.8/site-packages/pypykatz/dpapi/dpapi.py", line 534, in decrypt_securestring_bytes
    return self.decrypt_blob_bytes(data)
  File "/home/remnux/.local/lib/python3.8/site-packages/pypykatz/dpapi/dpapi.py", line 450, in decrypt_blob_bytes
    return self.decrypt_blob(blob, key = key)
  File "/home/remnux/.local/lib/python3.8/site-packages/pypykatz/dpapi/dpapi.py", line 435, in decrypt_blob
    return dpapi_blob.decrypt(key)
  File "/home/remnux/.local/lib/python3.8/site-packages/pypykatz/dpapi/structures/blob.py", line 109, in decrypt
    cleartext = unpad(cipher.decrypt(self.data), cipher.block_size)
  File "/home/remnux/.local/lib/python3.8/site-packages/pypykatz/crypto/unified/pkcs7.py", line 11, in unpad
    raise ValueError('Input is not padded or padding is corrupt')
ValueError: Input is not padded or padding is corrupt
skelsec commented 2 years ago

in that case I believe the error could be that the masterkeys you have do not match the one which was used to encrypt the blob OR the decryption code is buggy.
you can check the masterkey GUID required by the blob by parsing the blob (no decryption) and the GUID of the header should match one of the GUIDS of the masterkeys you have.

sealmove commented 2 years ago

EDIT: The GUIDs seem to match. Looks like there is a slight difference in the GUIDs. Could it be a version incompatibility?

$ pypykatz dpapi masterkey 865be7a6-863c-4d73-ac9f-233f8734089d prekeys
[GUID] 865be7a6-863c-4d73-ac9f-233f8734089d [MASTERKEY] 138f089556f32b87e53c5337c47f5f34746162db7fe9ef47f13a92c74897bf67e890bcf9c6a1d1f4cc5454f13fcecc1f9f910afb8e2441d8d3dbc3997794c630

cat encrypted_key.blob

01000000d08c9ddf0115d1118c7a00c04fc297eb01000000a6e75b863c86734dac9f233f8734089d00000000020000000000106600000001000020000000273b46c4c77da6aec6b84106322aaa686ba6ee18801ba89ef3674ba9b01e1e3a000000000e8000000002000020000000bfdc532d088604106c3adcf8abceaacafe54242c77d414c19382d2ff7ad877f43000000041c108468a6b567d2736ac03bc294c72393e3782260a547129b2ecb7ca9df129d2a3d8423a7e71c083a1c2400000003a4218497890c65b8514dbafd35f7ab87338602e4bb38ea5c581e1fe08776b4cb8e42bdd2f684a5725eadbc38d5ea8561296ca2e42ca41a3fe51d0761e5f6f790a

EDIT: If my comparison is correct then (after applying correct byte ordering):

masterkey GUID:                      a6e75b863c86734dac9f233f8734089d
masterkey GUID used to encrypt blob: a6e75b863c86734dac9f233f8734089d
sealmove commented 2 years ago

@skelsec could you please try decrypting this blob with this masterkey?

skelsec commented 2 years ago

I can verify that decryption fails, thank you for providing the data. At first glance it looks like either the encrypted.blob is corrupted at the end OR the parser code is incorrect somehow, as the end of the encrypted blob reports an impossibly large signature_length

sealmove commented 2 years ago

I can verify the blob is not corrupted. Let me also provide the original source of the information, in case I did something wrong when extracting it:

AppData/Local/Google/Chrome/User Data/Local State

"os_crypt": {
    "encrypted_key": "RFBBUEkBAAAA0Iyd3wEV0RGMegDAT8KX6wEAAACm51uGPIZzTayfIz+HNAidAAAAAAIAAAAAABBmAAAAAQAAIAAAACc7RsTHfaauxrhBBjIqqmhrpu4YgBuonvNnS6mwHh46AAAAAA6AAAAAAgAAIAAAAL/cUy0IhgQQbDrc+KvOqsr+VCQsd9QUwZOC0v962Hf0MAAAAEHBCEaKa1Z9JzasA7wpTHI5PjeCJgrNbSTeklRxKbLst8qd8SnSo9hCOn5xwIOhwkAAAAA6QhhJeJDGW4UU26/TX3q4czhgLkuzjqXFgeH+CHdrTLjkK90vaEpXJerbw41eqFYSlsouQspBo/5R0HYeX295"
}
sealmove commented 2 years ago

@skelsec Hmm, after decoding this base64 string, I removed the first 5 bytes ("DPAPI" prefix) manually using a hex editor. Previously I was using the command cut -c 6- to achieve this. The two methods of removing the prefix yield different result! So you were right, cut -c 6- somehow corrupts the information. It seems to decrypt it just fine. Sorry for the confusion!

P.S.: The output could be improved:

Error! non-hexadecimal number found in fromhex() arg at position 0
HEX: 46befddb52a607c5e775b7a930b6b6c4f3a35e7c1c30aaa4ce0d2277fbca6c19
Traceback (most recent call last):
  File "/home/sealmove/.local/bin/pypykatz", line 8, in <module>
    sys.exit(main())
  File "/home/sealmove/.local/lib/python3.10/site-packages/pypykatz/__main__.py", line 89, in main
    helper.execute(args)
  File "/home/sealmove/.local/lib/python3.10/site-packages/pypykatz/dpapi/cmdhelper.py", line 121, in execute
    self.run(args)
  File "/home/sealmove/.local/lib/python3.10/site-packages/pypykatz/dpapi/cmdhelper.py", line 238, in run
    print('STR: %s' % dec_sec.decode('utf-16-le'))
  File "/usr/lib/python3.10/encodings/utf_16_le.py", line 16, in decode
    return codecs.utf_16_le_decode(input, errors, True)
UnicodeDecodeError: 'utf-16-le' codec can't decode bytes in position 2-3: illegal UTF-16 surrogate

The decrypted information is not necessarily utf-16. Maybe the output could be more friendly instead of a stack trace in this case.

skelsec commented 2 years ago

The reason you get that message is that I like stack traces, don't kink shame me.
You are using the incorrect decrypt function for chrome. Not your fault as I forgot to push the code for that part tho.
Please wait till I push the new code because it fixes another error which came in -again- because of the unicrypto lib.

sealmove commented 2 years ago

Which step is wrong? The command pypykatz dpapi blob masterkey encrypted_key.blob? Ok, standing by =)

skelsec commented 2 years ago

That command is okay, but I guess you want to see the actual logindata/cookies. There is a command to do just that in the not-yet-uploaded version

skelsec commented 2 years ago

Try now with a clean environment, cloning master (new version uploaded)
There will be a new command for chrome like:
pypykatz dpapi chrome masterkey Local\ State --logindata Login\ Data

sealmove commented 2 years ago

Thank you very much for the release! Works like a charm =)