11-15-2021, 05:09 PM
Prologue
Since a couple of years now, Microsoft introduced WINDOWS HELLO in the operating system Windows 10 in order to let the user sign-in on a more personal way: using the face, fingerprint or a PIN.
When the user adds a local Win10 account and chooses to activate the Windows Hello PIN, there still is the possibility to sign-in with the user password.
But, when adding an online Win10 account, the user is asked - by default - to setup a PIN. This became the only way to sign-in to this account.
In September '21, Microsoft made some publicity about pushing their users to use a PIN.
https://docs.microsoft.com/en-us/windows...n-password
However, is there a possibility to crack this PIN?
After doing some self-study, reading blogs, reading code on Github, and trying to understand what happens...we finally got it. Sort of.
Thanks to the exceptional reverse-engineering work of @tijldeneut, we understood that there is in fact a way to crack the PIN.
We also saw that he implemented a "--pinbrute" possibility, which we - for this Proof of Concept - extracted, cleaned and optimised.
Technical overview - TL;DR
Unfortunately the PIN does not have an easy-to-extract hash.
We wrote a tool WINHELLO2hashcat.py to do the hard work. Please visit our GitHub (https://github.com/Banaanhangwagen/WINHELLO2hashcat) to learn all the details.
Technical overview - detail
There are multiple steps one needs to follow (based on the article of @tijldeneut):
1) Parsing the NGC protector
First we need to determine the PIN_GUID, which can be found in the Next Generation Credential-folder: \Windows\ServiceProfiles\LocalService\AppData\Local\Microsoft\Ngc.
Each user has a subfolder there with a specific GUID; it contains encrypted data, but no keys.
In the `...\NGC\`-folder, there are some .dat-files which contain metadata
If a PIN is activated we should find a Software Key Provider (7.dat) with a corresponding GUID (2.dat).
2) Parsing the Crypto Keys
Next we need to go to the private keys folder: `%windir%\ServiceProfiles\LocalService\AppData\Roaming\Microsoft\Crypto\Keys` and parse all the keys.
They all contain metadata (key type (RSA or ECS), the Key GUID and the Public Key in clear text) + two System DPAPI blobs. The first DPAPI-blob contains the Private Key Properties, which can be decrypted with a System DPAPI key and a static entropy string.
3) RSA Private Key Properties
These RSA Private Key Properties contain two important fields: PIN_salt and PIN_rounds.
4) System Master Key
The decrypted System Master Key is also needed to fulfill 2). Therefore we need the SECURITY and SYSTEM hives.
5) Convert the PIN
Steps in order to convert the provided PIN:
In Python:
6) Check the PIN (proof of concept)
Finally, we need to verify that the provided sign matches the computed-one based on the provided masterkey, hmac, verif_blob and "secret pin" bytes.
In Python:
Extract the hash
Inspired by https://github.com/tijldeneut/dpapilab-n...keysdec.py, we wrote a tool from scratch - called WINHELLO2hashcat.py - to extract the needed variables, and to format it into a readable hash for Hashcat.
This is the format:
This script will be published and maintained on our GitHub - https://github.com/Banaanhangwagen/WINHELLO2hashcat
Remarks about TPM
Remarks about PIN
Remarks about signature check
During our testing, the hash algo used by default was always SHA512 (for Win10 and Win11).
Remarks about NGC
Reading the code on @tijldeneut's GitHub, we understand that when a Windows Hello PIN is used, the user-password can be extracted in clear (!) from disk.
Unfortunately, our tests with his tool were not always successful and we didn't have the time to deep-dive into this.
We encourage you to experiment with his code and to suggest any fixes or optimizations.
Credits
This work couldn't be possible without the hard work of:
Since a couple of years now, Microsoft introduced WINDOWS HELLO in the operating system Windows 10 in order to let the user sign-in on a more personal way: using the face, fingerprint or a PIN.
When the user adds a local Win10 account and chooses to activate the Windows Hello PIN, there still is the possibility to sign-in with the user password.
But, when adding an online Win10 account, the user is asked - by default - to setup a PIN. This became the only way to sign-in to this account.
In September '21, Microsoft made some publicity about pushing their users to use a PIN.
https://docs.microsoft.com/en-us/windows...n-password
However, is there a possibility to crack this PIN?
After doing some self-study, reading blogs, reading code on Github, and trying to understand what happens...we finally got it. Sort of.
Thanks to the exceptional reverse-engineering work of @tijldeneut, we understood that there is in fact a way to crack the PIN.
We also saw that he implemented a "--pinbrute" possibility, which we - for this Proof of Concept - extracted, cleaned and optimised.
Technical overview - TL;DR
Unfortunately the PIN does not have an easy-to-extract hash.
We wrote a tool WINHELLO2hashcat.py to do the hard work. Please visit our GitHub (https://github.com/Banaanhangwagen/WINHELLO2hashcat) to learn all the details.
Technical overview - detail
There are multiple steps one needs to follow (based on the article of @tijldeneut):
1) Parsing the NGC protector
First we need to determine the PIN_GUID, which can be found in the Next Generation Credential-folder: \Windows\ServiceProfiles\LocalService\AppData\Local\Microsoft\Ngc.
Each user has a subfolder there with a specific GUID; it contains encrypted data, but no keys.
In the `...\NGC\`-folder, there are some .dat-files which contain metadata
- user SID (1.dat)
- provider (7.dat)
- Key1 GUID (2.dat)
- Timestamp in LE (9.dat)
- Encrypted data (15.dat)
If a PIN is activated we should find a Software Key Provider (7.dat) with a corresponding GUID (2.dat).
2) Parsing the Crypto Keys
Next we need to go to the private keys folder: `%windir%\ServiceProfiles\LocalService\AppData\Roaming\Microsoft\Crypto\Keys` and parse all the keys.
They all contain metadata (key type (RSA or ECS), the Key GUID and the Public Key in clear text) + two System DPAPI blobs. The first DPAPI-blob contains the Private Key Properties, which can be decrypted with a System DPAPI key and a static entropy string.
3) RSA Private Key Properties
These RSA Private Key Properties contain two important fields: PIN_salt and PIN_rounds.
4) System Master Key
The decrypted System Master Key is also needed to fulfill 2). Therefore we need the SECURITY and SYSTEM hives.
5) Convert the PIN
Steps in order to convert the provided PIN:
Code:
Stage 1: convert the provided string to a specific hex format
Phase A: convert each char to the hex ASCII representation
Phase B: take the hex value of the uppercase representation of each nibble
Phase C: concat and convert to UTF-16-LE representation
Stage 2: derive the previous value with PBKDF2-SHA256 with the provided salt and iterations
Stage 3: get the hex representation of the derived bytes in uppercase + convert it to UTF-16-LE
Stage 4: compute the SHA512 of this value
In Python:
Code:
def convert_userpin_to_secretpin(user_pin: str, pin_salt: bytes, pin_rounds: int) -> bytes:
stage1_hexpin = user_pin.encode().hex().upper().encode('UTF-16LE')
stage2_pbkdf2 = hashlib.pbkdf2_hmac('sha256', stage1_hexpin, pin_salt, pin_rounds)
stage3_hexconvert = stage2_pbkdf2.hex().upper().encode('UTF-16LE')
stage4_sha512 = hashlib.sha512(stage3_hexconvert).digest()
return stage4_sha512
6) Check the PIN (proof of concept)
Finally, we need to verify that the provided sign matches the computed-one based on the provided masterkey, hmac, verif_blob and "secret pin" bytes.
Code:
The hash algo used by default is SHA512 (for Win10 and Win11).
Stage 1: prepare the master key
Phase A: compute the SHA1 of the master key
Phase B: append 108 bytes of 0x0 (to have a bytearray of 128 bytes)
Stage 2: create two separate seeds (sub_digest_seed and main_digest_seed)
- sub_digest_seed is the masterkey xored with 0x36
- main_digest_seed is the masterkey xored with 0x5c
Stage 3: compute the sub digest
SHA512(
sub_digest_seed +
hmac +
'\x78\x54\x35\x72\x5a\x57\x35\x71\x56\x56\x62\x72\x76\x70\x75\x41\x00' +
secret_pin +
verif_blob)
Stage 4: compute the main digest
SHA512(main_digest_seed + "stage3 digest")
In Python:
Code:
def is_signature_matching(sign: bytes, masterkey: bytes, hmac: bytes, verif_blob: bytes, secret_pin: bytes) -> bool:
sha512_blocsize = 128
# Stage 1 : prepare the master key
masterkey = hashlib.sha1(masterkey).digest()
masterkey += ('\x00' * 108).encode()
# Stage 2 : create two separate seeds (sub_digest_seed and main_digest_seed)
sub_digest_seed = bytes(a ^ b for (a, b) in zip(masterkey, b'\x36' * sha512_blocsize))
main_digest_seed = bytes(a ^ b for (a, b) in zip(masterkey, b'\x5c' * sha512_blocsize))
# Stage 3: compute the sub digest
sub_digest = hashlib.sha512()
sub_digest.update(sub_digest_seed)
sub_digest.update(hmac)
sub_digest.update(b'\x78\x54\x35\x72\x5a\x57\x35\x71\x56\x56\x62\x72\x76\x70\x75\x41\x00' + secret_pin)
sub_digest.update(verif_blob)
# Stage4: compute the main digest
main_digest = hashlib.sha512()
main_digest.update(main_digest_seed)
main_digest.update(sub_digest.digest())
if main_digest.digest() == sign:
return True
return False
Extract the hash
Inspired by https://github.com/tijldeneut/dpapilab-n...keysdec.py, we wrote a tool from scratch - called WINHELLO2hashcat.py - to extract the needed variables, and to format it into a readable hash for Hashcat.
This is the format:
Code:
$WINHELLO$*SHA512*{pin_iterations}*{pin_salt}*{sign}*{masterkey}*{hmac}*{verif_blob}*{entropy}
This script will be published and maintained on our GitHub - https://github.com/Banaanhangwagen/WINHELLO2hashcat
Remarks about TPM
- The above mentioned technique only works on systems without TPM-chip. The NGC Crypto Provider will no longer be “Microsoft Software Key Storage Provider” but rather “Microsoft Platform Crypto Provider”. The file `15.dat` will not be present.
- As a reminder, in order to install Windows 11, a TPM-chip is mandatory. However, this requirement can be bypassed using an officially documented technique. (https://support.microsoft.com/en-us/wind...e77ac7c70e)
Remarks about PIN
- A Windows Hello PIN is minimum 4 and maximum 127 characters long. (https://docs.microsoft.com/en-us/windows...e-defaults )
- By default only digits are activated. Nonetheless, the user can chose to "include letters and symbols" in the PIN. Only non-accented letters, digits and special characters from ASCII-table are accepted.
- During our testing, the pin_iterations were always 10000.
Remarks about signature check
During our testing, the hash algo used by default was always SHA512 (for Win10 and Win11).
Remarks about NGC
Reading the code on @tijldeneut's GitHub, we understand that when a Windows Hello PIN is used, the user-password can be extracted in clear (!) from disk.
Unfortunately, our tests with his tool were not always successful and we didn't have the time to deep-dive into this.
We encourage you to experiment with his code and to suggest any fixes or optimizations.
Credits
This work couldn't be possible without the hard work of:
- @tijldeneut:
updated dpapilab from @dfirfpi
updated dpapick to python3 (https://pypi.org/project/dpapick3/ )
article: https://www.insecurity.be/blog/2020/12/2...one-dpapi/
- @gentilkiwi (Mimikatz for better understanding and debugging)
- @atom for implementing this PoC into Hashcat
- Our team (you know who you are!)