Skip to content

Adding support for AzureAD accounts#3

Open
laxaa wants to merge 2 commits into
tijldeneut:mainfrom
laxaa:main
Open

Adding support for AzureAD accounts#3
laxaa wants to merge 2 commits into
tijldeneut:mainfrom
laxaa:main

Conversation

@laxaa

@laxaa laxaa commented Mar 15, 2024

Copy link
Copy Markdown

Adding support for AzureAD accounts on the diana-msaccountdec.py. This effectively yields a key to be used when decrypting masterkey. Contrary to MicrosoftAccount, the output is not a password but a key. This key can be used for example with dpapi.py from impacket:

$ dpapi.py masterkey -file {masterkey} -key KEY

@tijldeneut

tijldeneut commented Mar 24, 2024

Copy link
Copy Markdown
Owner

Hi,
Great work, thank you.
I am trying it out now, I have several systems with an AzureAD account but the string 'Version' seems to not be part of the decrypted file.
I tried it using:
Windows\System32\config\systemprofile\AppData\Local\Microsoft\Windows\CloudAPCache\MicrosoftAccount\<id>\Cache\CacheData
and
Windows\System32\config\systemprofile\AppData\Local\Microsoft\Windows\CloudAPCache\AzureAD\<id>\Cache\CacheData

I was able to get a key (20 bytes), but wasn't able to decrypt the user Masterkey with it.

What version of Windows did you test it on, please?

@laxa

laxa commented Mar 24, 2024

Copy link
Copy Markdown

Hello, the Version string is the first key in the JSON when decrypting an AzureAD CacheData file. Maybe there are different formats on different versions. My test system was a Windows 10 Pro up-to-date with the account linked to an AzureAD environment.

@tijldeneut

tijldeneut commented Mar 24, 2024

Copy link
Copy Markdown
Owner

Hi, thanks for the reply.
I used Win11 Pro up-to-date with an AzureAD account.
Do you use the CacheData from
C:\Windows\System32\config\systemprofile\AppData\Local\Microsoft\Windows\CloudAPCache\MicrosoftAccount\abd83620589e3d48d82ee7007b6d3246d6c68ff4c59a45a8d4388a08f23e18f3\Cache\CacheData
or
C:\Windows\System32\config\systemprofile\AppData\Local\Microsoft\Windows\CloudAPCache\AzureAD\e294585274c946c4d3f0b3b861eff845f9ba77e09bc93498d5faa442ba3ac7de\Cache\CacheData?

Does the CacheData for AzureAD also has the static 124 byte header?
I will now launch a Windows 10 Pro and link a fresh AzureAD account to test it out.

If it helps, I can provide you with a sample CacheData file, I only use burner accounts anyway.

@laxa

laxa commented Mar 24, 2024

Copy link
Copy Markdown

I used the CacheData in the AzureAD folder.
Does the CacheData for AzureAD also has the static 124 byte header? => Yes it does.

Let me know if it works better on Windows 10. I can do the R&D on Windows 11 later on to verify if the comportement is different.

@tijldeneut

Copy link
Copy Markdown
Owner

Hi, I have the same issues on Windows 10.
Unable to properly decrypt any pieces of the CacheData file, maybe the cleartext AzureAD password might be the issue.
Is it still

bDecryptionKey = hashlib.pbkdf2_hmac('sha256', options.password.encode('UTF-16LE'), b'', 10000)
oCipher = AES.new(bDecryptionKey, AES.MODE_CBC, b'\x00' * 16)

to convert and use the password for decrypting the CacheData file?

@laxa

laxa commented Mar 24, 2024

Copy link
Copy Markdown

Hello, the script should be used in the same manner that with a Microsoft Account. The plaintext password of the Azure Account is used to decrypt the CacheData file that contains a first part containing raw bytes and then JSON content. The only difference from a MicrosoftAccount is that the DPAPI key is not in a plaintext format but derived from bytes at the top of the decrypted blob.

@tijldeneut

Copy link
Copy Markdown
Owner

Hi,
We're making progress :-)
I was able to get the JSON, I think it has to do with the type of AzureAD account.
So that's a very important part done.

Now to digest the actual User MasterKey decryption key, that part is not working yet:
In my sample, I have 144 raw bytes before the start of the JSON portion.
When looking at offset 0x30 from those 144 bytes, the first 4 bytes are "0x60000000", which is the exact length (96 bytes) of the remainder of those 144 bytes. There also a lot of other zero's. Is that normal?
Or should we have 64bytes of random data going into SHA1 hash?
In other words: in your example, if you print out bClearData[0x30:0x70].hex(), this it contain any \x00\x00 bytes?

Thanks for the assistance

@laxa

laxa commented Mar 25, 2024

Copy link
Copy Markdown

The modified version of the script does not work out of the box?

In my sample, I have 112 bytes before the start of the JSON. I have not reversed not looked at the format so there might be some differences between different setups I guess. But here is the start of the data in my case:

00000000  00 00 00 00 01 00 00 00  00 00 00 00 60 00 00 00  |............`...|
00000010  60 00 00 00 00 00 00 00  20 00 00 00 40 00 00 00  |`....... ...@...|

Then, 16 random bytes that I discard, followed by the next 0x40 random bytes before the start of the JSON that are used to derive the masterkey's decryption key.

Maybe taking the 0x40 bytes before the JSON would be a good enough solution to avoid parsing the blob...

Moreover, the SID can be omitted in the argument and retrieved from the JSON content, I can edit the PR to reflect this.

@tijldeneut

Copy link
Copy Markdown
Owner

Hi,
As soon as I got it working, I will approve your PR and edit the code some more.
From I see, I think the modified version can never work because of these 2 lines in the code:

if not bCacheDataOrg[72:72 + 4] == b'\x02\x00\x00\x00':
  exit('[-] Error: Not a valid Microsoft Live Account CacheData file?')

-> In case of an AzureAD file, this seems to be b'\x01\x00\x00\x00'

I assume the SID is also the AzureAD version? E.g. S-1-12-1-1234567890-1234567890-1234567890-123456789

Good to know, we need (probably) need the 0x40 bytes prior to the start of the JSON.

@laxa

laxa commented Mar 25, 2024

Copy link
Copy Markdown

The code:

if not bCacheDataOrg[72:72 + 4] == b'\x02\x00\x00\x00':
  exit('[-] Error: Not a valid Microsoft Live Account CacheData file?')

Worked in my context because this filter is applied before the data in decrypted, but I am unsure of its necessity and I have no opinion about it.

I assume the SID is also the AzureAD version? E.g. S-1-12-1-1234567890-1234567890-1234567890-123456789 => Yes, the SID should be the same between C:\users\USER\appdata\roaming\microsoft\protect\SID and the one contained in the decrypted CacheData file.

@laxaa

laxaa commented Jun 5, 2024

Copy link
Copy Markdown
Author

Hello @tijldeneut, a colleague performed a more in depth reverse of the CacheData file format and came up with better parsing. As such, this should work better for MSAccount and AzureAD account. I could not test the export option because I dont know the associated formats.

Moreover, if your interested, colleagues published a review of Windows Hello For Business and the article contains details of all cryptographic operations and credit some of your work that was used during their research: https://www.synacktiv.com/publications/whfb-and-entra-id-say-hello-to-your-new-cache-flow

@tijldeneut

Copy link
Copy Markdown
Owner

@laxaa
Wow, very interesting indeed, thank you for sharing and referencing my research.
I'm reserving some time in the coming days to have a very close look.

I already managed to write a new, custom parser for CacheData, based on AADInternals. Seems to be more generic than this approach.
There are multiple ways the CacheData can be organized, even strictly within the AzureAD folder, that's one of the goals my tests.

Keep you posted once I have more info (and a very close read of the article and other references)

@tijldeneut

Copy link
Copy Markdown
Owner

Hi, as mentioned, I will read and verify your article in close detail in the coming days, but in the mean time I'd like to share my attempt from 2 months ago for a future diana-azaccountdec.py script:
https://gist.github.com/tijldeneut/e1c81c6f8937cd99ed957c68a3166c38
Example output: https://imgur.com/a/Im25DTY

I think we might be able to combine your additions on DPAPI key extraction and my PRT details for the best results.
Mileage may vary as I think it might also be dependent on the type of AzureAD/EntraID account (Business, hybrid, OS, TPM etc ...)

Any feedback more than welcome, work in progress

@tijldeneut

tijldeneut commented Jun 15, 2024

Copy link
Copy Markdown
Owner

Update: I included your changes into my earlier script, it now prints CredKey, PRT and POP:
https://gist.github.com/tijldeneut/e1c81c6f8937cd99ed957c68a3166c38

This POP can be decrypted via diana-cloudprtdec.py, but it seems that this it is not really usable in RoadRecon yet (but I have to verify this later)

Whatever happens, I will of course give full credit to you and your work, will mention it with a link on the home directory and I am very thankful for your research and assistance :-)

@synacktiv-rj

synacktiv-rj commented Jun 21, 2024

Copy link
Copy Markdown

Hello @tijldeneut, a colleague performed a more in depth reverse of the CacheData file format and came up with better parsing. As such, this should work better for MSAccount and AzureAD account. I could not test the export option because I dont know the associated formats.

Moreover, if your interested, colleagues published a review of Windows Hello For Business and the article contains details of all cryptographic operations and credit some of your work that was used during their research: https://www.synacktiv.com/publications/whfb-and-entra-id-say-hello-to-your-new-cache-flow

Hello ! With the blogpost, we released a tool (based on your work for the NGC part) which parses the CacheData file and some of its entries (PIN, Password and Microsoft Smart Card). I think you might be interested to take a look at it :) https://github.com/synacktiv/CacheData_decrypt/tree/main
Rémi

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants